Skip to content

Agent SDK 通过子进程运行 Claude Code CLI,CLI 内置 OpenTelemetry 采集能力。配置环境变量 CLAUDE_CODE_ENABLE_TELEMETRY=1 并设置至少一个 exporter 后,CLI 会直接向你的 OTLP 后端(Honeycomb、Datadog、Grafana、Langfuse 或自建 Collector)导出追踪、指标和日志事件。你可以在进程环境或 options.env 中配置,并可通过资源属性标记具体 Agent 和端用户。追踪功能目前为 Beta,需额外设置 CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1

Agent SDK 怎么配置 OpenTelemetry 遥测

使用 Agent SDK 将代理应用部署到生产环境时,需要知道代理实际做了哪些操作:

  • 调用了哪些工具
  • 每次模型请求耗时多久
  • 消耗了多少 token
  • 哪里发生了失败

Agent SDK 可以将这些数据作为 OpenTelemetry 追踪、指标和日志事件导出到任何支持 OTLP(OpenTelemetry Protocol)的后端,例如 Honeycomb、Datadog、Grafana、Langfuse 或自建 Collector。

本文说明 SDK 如何产生遥测数据、如何配置导出,以及如何在数据到达后端后进行标记和过滤。如果希望直接从 SDK 响应流读取 token 用量和成本而不导出到后端,请参阅 跟踪成本与用量

遥测数据从 SDK 到后端的流转过程

Agent SDK 将 Claude Code CLI 作为子进程运行,通过本地管道与它通信。CLI 内置了 OpenTelemetry 检测:它在每次模型请求和工具执行时记录 span,发出 token 和成本计数器的指标,以及针对提示和工具结果的结构化日志事件。SDK 本身不产生遥测数据,而是将配置传递给 CLI 进程,由 CLI 直接导出到你的 Collector。

配置通过环境变量传递。默认情况下子进程继承你的应用环境,因此你可以在以下位置之一配置:

  • 进程环境:在 shell、容器或编排平台中,在应用启动前设置变量。每次 query() 调用自动拾取,无需修改代码。生产部署推荐使用这种方式。
  • 每次调用选项:在 ClaudeAgentOptions.env(Python)或 options.env(TypeScript)中设置变量。当同一个进程中的不同代理需要不同的遥测设置时使用。Python 中 env 会合并到继承的环境之上;TypeScript 中 env 会完全替换继承的环境,因此记得在传入的对象中展开 ...process.env

CLI 导出三种相互独立的 OpenTelemetry 信号,每种信号都有独立的启用开关和导出器,你可以只开启需要的信号。

信号内容启用方式
指标token、成本、会话、代码行数、工具决策的计数器OTEL_METRICS_EXPORTER
日志事件每次提示、API 请求、API 错误和工具结果的结构化记录OTEL_LOGS_EXPORTER
追踪每次交互、模型请求、工具调用和钩子的 span(Beta)OTEL_TRACES_EXPORTER 加上 CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1

完整的指标名称、事件名称和属性列表,请参阅 Claude Code 监控 参考。Agent SDK 运行的是同一个 CLI,因此发出的数据相同。Span 名称见下面的 读取 Agent 追踪

启用遥测导出

遥测默认关闭,直到你设置了 CLAUDE_CODE_ENABLE_TELEMETRY=1 并选择了至少一个导出器。最常见配置是将三种信号全部通过 OTLP HTTP 发送到一个 Collector。

以下示例将变量写在字典中,通过 options.env 传入。代理运行单个任务,CLI 在读取响应流的同时将 span、指标和事件导出到 collector.example.com 的 Collector:

python
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions

OTEL_ENV = {
    "CLAUDE_CODE_ENABLE_TELEMETRY": "1",
    # 追踪需要以下变量(Beta)。指标和日志事件不需要。
    "CLAUDE_CODE_ENHANCED_TELEMETRY_BETA": "1",
    # 为每种信号选择导出器。使用 SDK 时请用 otlp;参见下方提示。
    "OTEL_TRACES_EXPORTER": "otlp",
    "OTEL_METRICS_EXPORTER": "otlp",
    "OTEL_LOGS_EXPORTER": "otlp",
    # 标准 OTLP 传输配置。
    "OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf",
    "OTEL_EXPORTER_OTLP_ENDPOINT": "http://collector.example.com:4318",
    "OTEL_EXPORTER_OTLP_HEADERS": "Authorization=Bearer your-token",
}


async def main():
    options = ClaudeAgentOptions(env=OTEL_ENV)
    async for message in query(
        prompt="List the files in this directory", options=options
    ):
        print(message)


asyncio.run(main())
typescript
import { query } from "@anthropic-ai/claude-agent-sdk";

const otelEnv = {
  CLAUDE_CODE_ENABLE_TELEMETRY: "1",
  // 追踪需要以下变量(Beta)。指标和日志事件不需要。
  CLAUDE_CODE_ENHANCED_TELEMETRY_BETA: "1",
  // 为每种信号选择导出器。使用 SDK 时请用 otlp;参见下方提示。
  OTEL_TRACES_EXPORTER: "otlp",
  OTEL_METRICS_EXPORTER: "otlp",
  OTEL_LOGS_EXPORTER: "otlp",
  // 标准 OTLP 传输配置。
  OTEL_EXPORTER_OTLP_PROTOCOL: "http/protobuf",
  OTEL_EXPORTER_OTLP_ENDPOINT: "http://collector.example.com:4318",
  OTEL_EXPORTER_OTLP_HEADERS: "Authorization=Bearer your-token",
};

for await (const message of query({
  prompt: "List the files in this directory",
  // TypeScript 中 env 会替换继承的环境,因此先展开 process.env
  options: { env: { ...process.env, ...otelEnv } },
})) {
  console.log(message);
}

因为子进程默认继承你的应用环境,你也可以将这些变量导出在 Dockerfile、Kubernetes 清单或 shell profile 中,然后省略 options.env,效果相同。

console 导出器会将遥测写入标准输出,而 SDK 使用标准输出作为消息通道。因此,通过 SDK 运行时不要将导出器设置为 console。如需在本地检查遥测,请将 OTEL_EXPORTER_OTLP_ENDPOINT 指向本地 Collector 或一个集成了 Jaeger 的容器。

短存活期调用的遥测刷出

CLI 会将遥测数据批量导出并按时间间隔发送。在进程正常退出时,它会尝试刷出待处理数据,但刷出有时间超时限制,因此如果 Collector 响应慢,span 仍可能丢失。如果你的进程在 CLI 关闭前被杀死,批量缓冲区中的所有数据都会丢失。降低导出时间间隔可以缩小这两个窗口。

默认情况下,指标每 60 秒导出一次,追踪和日志每 5 秒导出一次。以下示例将三个间隔都缩短,使数据在短任务仍在运行时就能到达 Collector:

python
OTEL_ENV = {
    # ... 上一示例的导出器配置 ...
    "OTEL_METRIC_EXPORT_INTERVAL": "1000",
    "OTEL_LOGS_EXPORT_INTERVAL": "1000",
    "OTEL_TRACES_EXPORT_INTERVAL": "1000",
}
typescript
const otelEnv = {
  // ... 上一示例的导出器配置 ...
  OTEL_METRIC_EXPORT_INTERVAL: "1000",
  OTEL_LOGS_EXPORT_INTERVAL: "1000",
  OTEL_TRACES_EXPORT_INTERVAL: "1000",
};

读取 Agent 追踪

追踪提供 Agent 运行最详细的视图。设置 CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1 后,Agent 循环的每一步都会成为一个 span,你可以在追踪后端中查看:

  • claude_code.interaction:封装 Agent 循环的一次往返,从接收提示到生成响应。
  • claude_code.llm_request:封装每次 Claude API 调用,属性包含模型名称、延迟和 token 数量。
  • claude_code.tool:封装每次工具调用,包含权限等待(claude_code.tool.blocked_on_user)和执行本身(claude_code.tool.execution)的子 span。
  • claude_code.hook:封装每次钩子的执行。需要额外设置 ENABLE_BETA_TRACING_DETAILED=1BETA_TRACING_ENDPOINT

llm_requesttoolhook span 是 claude_code.interaction span 的子 span。当 Agent 通过 Task 工具生成子代理时,子代理的 llm_requesttool span 会嵌套在父 Agent 的 claude_code.tool span 下,因此完整的委托链会出现在同一条追踪中。

Span 默认带有 session.id 属性。当你对同一个会话发出多个 query() 调用时,在后端按 session.id 过滤,可以将其视为一条时间线。如果 OTEL_METRICS_INCLUDE_SESSION_ID 被设置为假值,该属性会被省略。

追踪功能处于 Beta 阶段。Span 名称和属性可能在版本间发生变化。参见监控参考的 追踪(Beta),了解追踪导出器的完整配置变量。

将追踪关联到你的应用

SDK 会自动将 W3C trace context 传播到 CLI 子进程。当你在应用中已有 OpenTelemetry span 的情况下调用 query() 时,SDK 会将 TRACEPARENTTRACESTATE 注入到子进程环境中,CLI 会读取它们,使其 claude_code.interaction span 成为你应用 span 的子 span。这样 Agent 运行就会显示在你的应用追踪内部,而不是一个孤立根。

CLI 还会将 TRACEPARENT 转发给它运行的每个 Bash 和 PowerShell 命令。如果通过 Bash 工具启动的命令本身产生了 OpenTelemetry span,这些 span 会嵌套在包裹命令的 claude_code.tool.execution span 下。

如果你在 options.env 中显式设置了 TRACEPARENT,自动注入会被跳过,因此你可以根据需要固定特定父上下文。交互式 CLI 会话会完全忽略传入的 TRACEPARENT,只有 Agent SDK 和 claude -p 运行才会使用它。完整 span 和属性参考请参见监控参考的追踪(Beta)

为 Agent 的遥测打标签

默认情况下,CLI 报告的 service.nameclaude-code。如果你运行多个 Agent,或者 SDK 与其他服务共享同一个 Collector,可以通过覆盖服务名称并添加资源属性,在后端按 Agent 过滤。

以下示例重命名了服务并附加上部署元数据。这些值会作为 OpenTelemetry 资源属性应用到 Agent 发出的每个 span、指标和事件上:

python
options = ClaudeAgentOptions(
    env={
        # ... 导出器配置 ...
        "OTEL_SERVICE_NAME": "support-triage-agent",
        "OTEL_RESOURCE_ATTRIBUTES": "service.version=1.4.0,deployment.environment=production",
    },
)
typescript
const options = {
  env: {
    ...process.env,
    // ... 导出器配置 ...
    OTEL_SERVICE_NAME: "support-triage-agent",
    OTEL_RESOURCE_ATTRIBUTES:
      "service.version=1.4.0,deployment.environment=production",
  },
};

将操作归属到端用户

CLI 会根据调用 Anthropic 时使用的凭据,为每个事件附加身份属性。当你构建的应用程序通过同一份部署服务多个端用户时,这些属性标识的是你服务的凭据,而不是代表代理操作的端用户。

为了使工具调用和 MCP 活动可归因到你的应用程序的端用户,可以在每次 query() 调用时,将端用户身份作为资源属性注入。由于 OTEL_RESOURCE_ATTRIBUTES 中的逗号、空格和等号是保留字符,请先进行百分号编码。以下示例将一个请求中的用户和租户信息附加到每个 span 和事件上:

python
from urllib.parse import quote

options = ClaudeAgentOptions(
    env={
        # ... 导出器配置 ...
        "OTEL_RESOURCE_ATTRIBUTES": f"enduser.id={quote(request.user_id)},tenant.id={quote(request.tenant_id)}",
    },
)
typescript
const options = {
  env: {
    ...process.env,
    // ... 导出器配置 ...
    OTEL_RESOURCE_ATTRIBUTES: `enduser.id=${encodeURIComponent(request.userId)},tenant.id=${encodeURIComponent(request.tenantId)}`,
  },
};

当端用户身份附加后,tool_decisiontool_resultmcp_server_connectionpermission_mode_changed 事件就成为一条按用户划分的审计轨迹,可以转发到 SIEM(安全信息和事件管理)平台。完整的安全事件列表及每个事件携带的属性,请参见监控参考的审计安全事件

控制导出中的敏感数据

遥测默认只包含结构信息。每个 span 都会记录持续时间、模型名称和工具名称;token 数量在底层 API 请求返回用量数据时才会记录,因此失败或中止的请求可能不包含 token。你的代理读取和写入的内容默认不会记录。以下可选变量可以控制内容是否加入导出数据:

变量添加的内容
OTEL_LOG_USER_PROMPTS=1提示文本出现在 claude_code.user_prompt 事件和 claude_code.interaction span 上
OTEL_LOG_TOOL_DETAILS=1工具输入参数(文件路径、shell 命令、搜索模式)出现在 claude_code.tool_result 事件上
OTEL_LOG_TOOL_CONTENT=1完整的工具输入和输出体作为 span 事件出现在 claude_code.tool 上,截断到 60 KB。需要启用追踪
OTEL_LOG_RAW_API_BODIES完整的 Anthropic Messages API 请求和响应 JSON,作为 claude_code.api_request_bodyclaude_code.api_response_body 日志事件。设为 1 时内联输出,截断到 60 KB;设为 file:<dir> 时写出未截断的 body 文件,事件中只包含 body_ref 路径。Body 包含完整的对话历史,扩展思考内容会被删减。启用此项意味着你同意暴露以上三个变量所有会暴露的数据。

除非你的可观测性管道被批准存储代理处理的这些数据,否则请保持这些变量不设置。完整属性和删减行为列表,请参见监控参考的安全与隐私

相关文档

以下指南涵盖了监控和部署 Agent 的相关主题:

  • 跟踪成本与用量:直接从消息流读取 token 和成本数据,无需外部后端。
  • 托管 Agent SDK:在容器中部署代理,可以在环境层面设置 OpenTelemetry 变量。
  • 监控:CLI 发出的每个环境变量、指标和事件的完整参考。

常见问题

为什么配置了遥测但后端没收到数据?

最常见的原因是缺少 CLAUDE_CODE_ENABLE_TELEMETRY=1。另外检查至少一个导出器变量(如 OTEL_TRACES_EXPORTEROTEL_METRICS_EXPORTEROTEL_LOGS_EXPORTER)是否设置成了 otlp。如果端点地址或认证头错误,CLI 导出会静默失败,可以先用 curl 验证 OTEL_EXPORTER_OTLP_ENDPOINT 是否可达。追踪功能额外需要 CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1

短任务(如单个文件查询)执行太快,数据还没导出进程就退出了怎么办?

CLI 在进程退出时会尝试刷新,但超时很短。可以通过缩短导出间隔让数据更快到达:设置 OTEL_METRIC_EXPORT_INTERVAL=1000(毫秒)、OTEL_LOGS_EXPORT_INTERVAL=1000OTEL_TRACES_EXPORT_INTERVAL=1000。如果进程被强制杀死,丢失数据不可避免,建议将遥测接收 Collector 部署在低延迟网络内。

如何区分不同用户或不同 Agent 实例的遥测数据?

在每次 query() 调用时通过 options.env 设置 OTEL_SERVICE_NAME(例如 "support-triage-agent")和 OTEL_RESOURCE_ATTRIBUTES(例如 enduser.id=xxx),这些会作为 OpenTelemetry 资源属性附加到所有信号上。这样在后端可以按 service.name 和自定义属性进行过滤。注意 TypeScript 中设置 env 会覆盖继承环境,记得先展开 ...process.env