本文信息来源于 OpenObserve,作者为 Gorakhnath Yadav,发布于 2026 年 6 月 22 日。原文是第三方可观测性厂商撰写的指南,不是 Anthropic 官方文章;其中关于 OpenObserve endpoint、存储、关联查询、采集管线和产品能力的表述,均应理解为 OpenObserve 的方案说明。

这篇文章的核心主题是:如何用 OpenTelemetry 观测 Claude Agent SDK 应用。一个 Agent 不是一次 API 调用,而是由多次模型请求、工具调用、MCP 请求、权限决策和子 Agent 组成的执行树。因此,仅记录“Agent 38 秒后完成”没有太多价值。你真正需要看到的是:哪次模型请求花了 12 秒、哪个工具失败、哪个 MCP server 超时、extended thinking 带来了多少 token 成本。

需要注意几点时效性和安全边界:

  • Tracing 仍属于 beta 能力,span 名称、属性、开关和 trace propagation 行为可能随 Claude Code 版本变化。
  • 短任务除了缩短 metric 和 log export interval 外,如果需要 prompt flush trace,也应关注 OTEL_TRACES_EXPORT_INTERVAL
  • Hook spans 需要额外的 detailed beta tracing 配置,不能只依赖普通 telemetry 开关。
  • Claude Code 不会自动把所有 OTEL_* exporter 变量传递给 Bash、hook、MCP 或 language-server 子进程。它可以传递 W3C TRACEPARENT,但被调用的子进程仍需要自己的 exporter 配置。
  • claude_code.cost.usage 是近似使用成本指标,不能替代官方账单对账。
  • prompt、工具参数、工具内容、assistant response 和原始 API body 都有独立的 opt-in 开关。生产环境应先制定敏感数据策略,再决定打开哪些内容日志。

TL;DR

Claude Agent SDK 的可观测性可以拆成三类 OpenTelemetry signal:

  • Traces:追踪 Agent loop 中的模型请求、工具调用、hook、MCP server 和子 Agent。
  • Metrics:记录 token、成本、session、活跃时间、代码修改行数和工具决策等计数。
  • Log events:提供 prompt、API 请求、API 错误、工具结果和权限决策等审计事件。

Claude Agent SDK 会把 Claude Code CLI 作为子进程运行,而 CLI 内置 OpenTelemetry instrumentation。你通过环境变量开启 telemetry,指向 OTLP endpoint,例如 OpenObserve,就可以把模型请求、工具调用、MCP 请求和子 Agent 都变成 span。

为什么观测 Agent SDK 应用不同于观测一次 API 调用

单次 Claude API 调用很容易理解:一个 request、一个 response,加上一些 token 计数。

Claude Agent SDK 构建的 Agent 不是这种形态。一次 query() 会运行一个 loop:

  1. Claude 读取 prompt;
  2. 决定调用工具;
  3. 等待权限决策;
  4. 执行工具;
  5. 读取工具结果;
  6. 再次调用模型;
  7. 可能生成子 Agent;
  8. 可能调用 MCP server;
  9. 最后才产出答案。

一个用户请求可能展开成十几次模型调用和工具执行。当这个 loop 变慢或出错时,一行“agent finished in 38 seconds”的日志无法告诉你问题在哪里。你需要看完整 timeline,而这正是 distributed tracing 对 Agent 的价值:每个步骤都是一个 span,trace 会显示整体执行树。

好消息是,Agent SDK 不需要你手工给每个步骤打点,instrumentation 已经随 Claude Code CLI 提供。

Agent SDK 如何发出 OpenTelemetry

Agent SDK 本身不直接产生 telemetry。它会把 Claude Code CLI 作为子进程运行,并通过本地 pipe 与其通信。真正内置 OpenTelemetry instrumentation 的部分是 CLI:

  • 为每次模型请求和工具执行记录 span;
  • 为 token 和成本发出 counter;
  • 为 prompt 和工具结果写 structured log events。

SDK 的职责,是把配置传给子进程;CLI 则直接把数据导出到 collector。

配置通过环境变量传递。默认情况下,子进程会继承应用环境变量,因此你可以在 shell、container、orchestrator 中设置这些变量,也可以在 Python 的 ClaudeAgentOptions.env 或 TypeScript 的 options.env 中按次传入。

CLI 支持三类独立 signal,每类都有自己的开关和 exporter:

Signal 内容 启用方式
Traces 每次 interaction、模型请求、工具调用和 hook 的 span OTEL_TRACES_EXPORTER + CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1
Metrics token、cost、session、代码行数、工具决策等 counter OTEL_METRICS_EXPORTER
Log events 每次 prompt、API request、API error、tool result 的结构化记录 OTEL_LOGS_EXPORTER

示例应用:调用多个 MCP server 的 coding assistant

原文使用一个小型 coding assistant 作为贯穿示例。它回答关于代码仓库的问题,拥有标准文件工具,并连接两个 MCP server:

  • 一个暴露项目 issue tracker;
  • 一个暴露 CI 系统。

典型问题例如:“上次部署为什么失败?有没有对应 open issue?” Agent 会读取文件,通过 CI MCP server 查询 CI,通过 issue MCP server 搜索 issue,有时还会生成子 Agent 深入分析某段日志。

这一个请求会多次接触模型、运行本地工具,并跨两个进程边界访问 MCP server。平面日志里,它可能只是一行和一个耗时;trace 中,它是可以自上而下阅读的树。

启用 telemetry

Telemetry 默认关闭。你需要设置:

CLAUDE_CODE_ENABLE_TELEMETRY=1

并至少选择一个 exporter。下面示例通过 OTLP HTTP 开启 traces、metrics 和 logs,并通过 options.env 传给 SDK。

import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions

OTEL_ENV = {
    "CLAUDE_CODE_ENABLE_TELEMETRY": "1",
    # Traces are in beta and need this flag. Metrics and logs do not.
    "CLAUDE_CODE_ENHANCED_TELEMETRY_BETA": "1",
    "OTEL_TRACES_EXPORTER": "otlp",
    "OTEL_METRICS_EXPORTER": "otlp",
    "OTEL_LOGS_EXPORTER": "otlp",
    "OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf",
    "OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:4318",
    "OTEL_EXPORTER_OTLP_HEADERS": "Authorization=Bearer your-token",
}

async def main():
    options = ClaudeAgentOptions(env=OTEL_ENV)
    async for message in query(
        prompt="Why did the last deploy fail?", options=options
    ):
        print(message)

asyncio.run(main())

TypeScript 形态类似,但有一个坑:Python 中 env 会叠加到继承环境上;TypeScript 中 env 会替换整个继承环境。因此必须显式展开 process.env,否则子进程可能丢失 PATHANTHROPIC_API_KEY 等变量。

import { query } from "@anthropic-ai/claude-agent-sdk";

const otelEnv = {
  CLAUDE_CODE_ENABLE_TELEMETRY: "1",
  CLAUDE_CODE_ENHANCED_TELEMETRY_BETA: "1",
  OTEL_TRACES_EXPORTER: "otlp",
  OTEL_METRICS_EXPORTER: "otlp",
  OTEL_LOGS_EXPORTER: "otlp",
  OTEL_EXPORTER_OTLP_PROTOCOL: "http/protobuf",
  OTEL_EXPORTER_OTLP_ENDPOINT: "http://localhost:4318",
  OTEL_EXPORTER_OTLP_HEADERS: "Authorization=Bearer your-token",
};

for await (const message of query({
  prompt: "Why did the last deploy fail?",
  options: { env: { ...process.env, ...otelEnv } },
})) {
  console.log(message);
}

不要把 exporter 设置成 consoleconsole exporter 会把 telemetry 写到 stdout,而 SDK 使用 stdout 作为 message channel。通过 SDK 运行时,如果 exporter 写 stdout,会破坏通信通道。想本地看 telemetry,应把 OTLP endpoint 指向本地 collector。

通过 OTLP 发送到 OpenObserve

OpenObserve 原生支持 OpenTelemetry:traces、metrics 和 logs 都可以进入同一个 OTLP endpoint,并在后端一起存储。由于 Agent SDK 输出标准 OTLP,接入 OpenObserve 只需要 endpoint 和 auth header。

OpenObserve Cloud 的 endpoint 是组织路径,OTLP exporter 会自动附加 /v1/traces/v1/metrics/v1/logs

OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_ENDPOINT=https://api.openobserve.ai/api/your_org_slug
OTEL_EXPORTER_OTLP_HEADERS=Authorization=Basic <your_base64_token>

自托管实例可以使用:

OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:5080/api/default

Basic token 是 OpenObserve 中 email:token 的 base64 编码,可以在数据接入页面复制。Agent 运行后,traces、metrics 和 logs 会进入各自 stream,等待查询和关联。

一个 metrics 侧的细节:OpenObserve 会把每个 metric 存成独立 stream,并把 metric 名中的点替换为下划线。例如 claude_code.token.usage 会变成 claude_code_token_usage

短生命周期任务如何 flush telemetry

一次性任务容易遇到 flush 时机问题。CLI 会批量导出 telemetry:默认 metrics 每 60 秒导出一次,logs 每 5 秒导出一次。短任务可能在下一次导出前就结束。干净退出时 CLI 会尝试 flush,但 flush 有短超时;如果进程被直接 kill,buffer 中的数据会丢失。

解决办法是缩短导出间隔:

OTEL_ENV = {
    # ... the exporter configuration from above ...
    "OTEL_METRIC_EXPORT_INTERVAL": "1000",
    "OTEL_LOGS_EXPORT_INTERVAL": "1000",
}

对于短任务和 cron Agent,建议设置更低间隔。长时间运行服务使用默认值即可,过低间隔只会增加导出开销。实际需要快速 flush traces 时,也应关注 trace export interval。

读取 trace:从 client 到 Agent 再到 MCP server

设置 CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1 后,Agent loop 的每一步都会变成 span。层次结构大致如下:

claude_code.interaction
├── claude_code.llm_request
├── claude_code.hook                    (requires detailed beta tracing)
└── claude_code.tool
    ├── claude_code.tool.blocked_on_user
    ├── claude_code.tool.execution
    └── (Agent tool) subagent claude_code.llm_request / claude_code.tool spans

claude_code.interaction 包住 loop 的一轮,从收到 prompt 到产出响应。每次模型调用都是一个 claude_code.llm_request 子 span。它的属性遵循 OpenTelemetry gen_ai.* 语义约定,因此可以看到:

  • gen_ai.request.model
  • gen_ai.operation.name
  • response finish reasons
  • token counts
  • span latency

每次工具调用都是 claude_code.tool span。当 Agent 通过 Agent 工具或旧的 Task 工具生成子 Agent 时,子 Agent 自己的 model 和 tool spans 会嵌套在父级 claude_code.tool span 下,因此整个委派链会表现为一棵 trace tree。

llm_requesttool.executionhook 失败时,它们会设置 OpenTelemetry status 为 ERROR,因此失败步骤不需要读详细属性也能看出来。

真正做到端到端的关键是 trace-context propagation。如果你在自己的应用中已经有一个 active OpenTelemetry span,然后调用 query(),SDK 会把 TRACEPARENTTRACESTATE 注入子进程,CLI 会让 claude_code.interaction 成为你应用 span 的子 span。

from opentelemetry import trace

tracer = trace.get_tracer("coding-assistant")

with tracer.start_as_current_span("handle_request"):
    options = ClaudeAgentOptions(env=OTEL_ENV)
    async for message in query(prompt=user_question, options=options):
        handle(message)
    # The agent's interaction span nests under handle_request automatically.

Propagation 还会继续深入。CLI 会把 TRACEPARENT 传给它运行的 Bash 和 PowerShell 命令,因此被命令调用的已 instrumented 程序可以嵌套在 claude_code.tool.execution 下。它也会把 W3C traceparent header 附加到每次模型请求和 HTTP MCP 请求上,因此已 instrumented 的 MCP server 可以延续同一条 trace。

每个 span 都会带 session.id 属性。多次 query() 使用同一个 session 时,可以在 OpenObserve 中按 session.id 过滤,把它们看成一条时间线。

追踪工具调用:延迟和失败通常藏在这里

Tool spans 是最容易发现 Agent 异常的地方。Agent SDK 会把每次工具调用拆成几个部分:

  • claude_code.tool:工具调用总 span;
  • claude_code.tool.blocked_on_user:等待权限决策的时间;
  • claude_code.tool.execution:工具真正运行的时间。

这种拆分很关键。一个 Agent 感觉很慢,可能是因为每次写文件都要等人工批准,也可能是工具调用卡住。前者会表现为很粗的 blocked_on_user spans;后者会表现为很粗的 execution spans。

在前面的 coding assistant 示例中,你可以看到 CI MCP 查询花了 9 秒,而文件读取只有毫秒级;也可以看到 Agent 是否卡在等待 shell 命令审批。MCP server 侧也一样:只要 MCP 请求在同一条 trace 中,慢 server 或失败 server 就不再是黑盒。

如果要把工具输入输出也记录到 span 上,可以设置:

OTEL_LOG_TOOL_CONTENT=1

它会把完整输入和输出 body 作为 claude_code.tool 的 span events 记录,截断上限为 60 KB。除非你确认存储工具内容符合隐私和安全要求,否则不要打开。

Extended Thinking 在 trace 中如何体现

Extended thinking 不是独立 span,也不是独立 signal。你不会看到 claude_code.thinking 这种 span。

它的可观测信号是成本和延迟。Thinking 发生在模型调用内部,因此会表现为:

  • claude_code.llm_request span 的 token counts 更大;
  • claude_code.token.usage metric 更高;
  • 对应模型请求 span 耗时更长。

当前 Claude 模型上,通常不是设置固定 thinking token budget,而是设置 effort 参数,由模型按请求自适应决定思考深度。因此运维闭环应该是:观察每类 interaction 的 token 和 latency;如果某类请求在 thinking 上花费超过答案价值,就调低该路径的 effort,而不是直接寻找固定 token cap。

Thinking 文本本身默认保持私密。即使开启 raw API body logging,extended-thinking content 也会被 redacted。你能在 trace 中看到 thinking 的形状、成本和延迟,但不会把 thinking 内容泄露到可观测性后端。

Metrics:token、cost 和工具决策计数

Trace 展示单次运行,metrics 展示可放进 dashboard 和 alert 的总体趋势。CLI 会发出这些 counters:

Metric 衡量内容 单位
claude_code.token.usage 使用 token 数 tokens
claude_code.cost.usage session 成本 USD
claude_code.session.count CLI session 数 count
claude_code.active_time.total 活跃时间 seconds
claude_code.lines_of_code.count 修改代码行数 count
claude_code.commit.count 创建 git commit 数 count
claude_code.pull_request.count 创建 PR 数 count
claude_code.code_edit_tool.decision 代码编辑工具权限决策 count

对 coding assistant 来说,日常最值得看的是 claude_code.cost.usageclaude_code.token.usage:每个 session 的成本、每次请求的 token,以及 prompt 或模型调整后的趋势。

claude_code.code_edit_tool.decision 更偏行为信号。被拒绝编辑突然升高,常常意味着 prompt 回归,或者 Agent 正在和权限规则“打架”。

还要注意 cardinality。metric 的每个属性都会成为 label,高基数 label 会增加存储成本。CLI 提供相关开关,例如 OTEL_METRICS_INCLUDE_SESSION_ID 会把 session.id 加入 metrics,默认开启。如果你不会按 session 查询 metrics,可以考虑关闭,以减少 series 数量。

Log events:Agent 的审计轨迹

第三类 signal 是结构化 log events,用来记录 Agent 决策和动作。重要事件包括:

  • claude_code.user_prompt:记录每次提交的 prompt;
  • claude_code.tool_result:工具运行并返回,包含时间和可选参数;
  • claude_code.tool_decision:权限系统接受或拒绝工具调用;
  • claude_code.api_requestclaude_code.api_errorclaude_code.api_refusal:覆盖模型调用及其结束方式;
  • claude_code.permission_mode_changed:session 中途权限模式变化;
  • claude_code.auth:认证事件。

这些事件共享标准属性,并且会增加一个关键字段:prompt.id。一个用户 prompt 可能触发多次 API 调用和多次工具执行;同一轮产生的事件会带相同 prompt.id,因此你可以只靠 log stream 重建一次请求的完整序列。

这也是安全审计和 SIEM 需要的材料:哪个工具、在谁的请求下、运行了什么命令、权限系统如何处理。

Trace 给你时序和耗时,events 给你决策和动作。

在 OpenObserve 中关联 traces、metrics 和 logs

三类 signal 真正有价值的前提,是你能在它们之间跳转。如果 traces 进一个工具、metrics 进另一个工具、logs 又在第三个工具里,排障会很痛苦。

在 OpenObserve 中,Agent traces、metrics 和 logs 可以共享 session.id,events 共享 prompt.id。排查流程就会变得直接:

  1. claude_code.cost.usage 触发某个 session 的成本告警;
  2. session.id 过滤 traces,找到消耗异常的 claude_code.interaction
  3. 在其中看到某个 claude_code.llm_request span 很大,token count 很高;
  4. 说明模型在该请求上花了大量 thinking;
  5. 再用 prompt.id 跳到 log events,查看 tool_decisiontool_result 序列;
  6. 判断 Agent 到底做了什么导致 thinking 增加。

这套路径靠共享 key 关联,不需要在多个工具之间手工对齐 timestamp。

内置 CLI telemetry 与进程内 instrumentation

内置 CLI telemetry 不是唯一观测 Agent SDK 应用的方式。有些库可以在你的 Python 进程内 instrument SDK,让 span 由应用 tracer 创建,而不是由 CLI 子进程导出。Langfuse、LangSmith 等 vendor integration 也可能采用类似方式。

取舍比较清楚:

  • 内置 CLI telemetry:不需要应用代码改造,能拿到 native span tree,例如 interactionllm_requesttool 和子 Agent nesting,并能跨进程和 MCP 边界传播上下文。它是生产默认选择。
  • 进程内 instrumentation:适合希望 Agent span 由应用同一个 tracer 创建、不想依赖 TRACEPARENT 注入、需要在代码里附加自定义属性,或已经深度绑定某个 tracing vendor 的场景。

两者都可以导出 OpenTelemetry,因此都能通过 OTLP 进入 OpenObserve。关键问题是:由哪个 tracer 拥有这些 spans。

控制敏感数据与归因

Agent telemetry 默认是结构化的,这也是更安全的默认值。默认会记录 duration、model name、tool name、token counts,但不会记录 prompt 和工具处理的内容。

内容日志需要逐项 opt in:

  • OTEL_LOG_USER_PROMPTS=1:把 prompt 文本加入 claude_code.user_prompt events 和 interaction span;
  • OTEL_LOG_TOOL_DETAILS=1:把工具输入参数加入 tool events,例如文件路径、shell 命令、搜索模式;
  • OTEL_LOG_TOOL_CONTENT=1:把完整工具输入输出 body 作为 span events 记录,截断到 60 KB,并要求开启 tracing;
  • OTEL_LOG_RAW_API_BODIES:记录完整 Messages API request 和 response JSON,但会 redacted 整段对话历史和 extended-thinking content。

除非你的可观测性管线被批准存储 Agent 处理的数据,否则不要打开这些开关。折中方案是:在 OpenObserve ingestion pipeline 里做 server-side redaction,保留调试需要的工具细节,同时清理路径或参数中的 secret。

归因是另一半。默认情况下,CLI 报告的 service.nameclaude-code,事件上的调用身份是调用 Anthropic 的凭证身份,也就是你的服务,不是你的终端用户。你应该覆盖 service name,并附加元数据,让不同 Agent 可区分,也能把 end-user identity 注入每个请求。

from urllib.parse import quote

options = ClaudeAgentOptions(
    env={
        # ... exporter configuration ...
        "OTEL_SERVICE_NAME": "coding-assistant",
        "OTEL_RESOURCE_ATTRIBUTES": (
            f"enduser.id={quote(request.user_id)},"
            f"tenant.id={quote(request.tenant_id)}"
        ),
    },
)

由于 OTEL_RESOURCE_ATTRIBUTES 中逗号、空格和等号有特殊含义,值需要 percent-encode。加上 end-user identity 后,tool_decisiontool_result 和权限事件就可以成为按用户归因的审计轨迹。

常见问题

如何在 Claude Agent SDK 中启用 OpenTelemetry tracing?

设置 CLAUDE_CODE_ENABLE_TELEMETRY=1,并按 signal 选择 exporter,例如把 OTEL_TRACES_EXPORTEROTEL_METRICS_EXPORTEROTEL_LOGS_EXPORTER 设置为 otlp。Tracing 仍是 beta,因此还需要设置 CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1。然后把 OTEL_EXPORTER_OTLP_ENDPOINT 指向你的 collector 或 backend。SDK 会把这些变量传给 Claude Code CLI,由 CLI 执行 instrumentation 和 export。

Claude Agent SDK 会 trace 工具调用和子 Agent 吗?

会。开启 tracing 后,每次工具调用都会成为 claude_code.tool span,并带有用于权限等待的 claude_code.tool.blocked_on_user 和用于实际执行的 claude_code.tool.execution 子 span。当 Agent 通过 Agent 或 Task 工具生成子 Agent 时,子 Agent 自己的 llm_request 和 tool spans 会嵌套在父级 claude_code.tool span 下。

Extended thinking 在 trace 中怎么看?

Extended thinking 不是单独 span。它通过每个 claude_code.llm_request span 上的 token counts,以及 claude_code.token.usage metric 体现。Thinking output 会增加 token 总数,也可能增加模型请求 latency。当前模型通常通过 effort 参数控制 thinking 深度,而不是固定 token budget。Thinking 文本默认 redacted,即使开启 raw API body logging,也不会直接泄露。

可以把 Claude Agent SDK telemetry 发到自托管后端吗?

可以。SDK 通过标准 OpenTelemetry OTLP 导出,因此任何兼容 OTLP 的后端都可以接收。自托管 OpenObserve 可以设置:

OTEL_EXPORTER_OTLP_ENDPOINT=http://your-host:5080/api/<org>
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf

并配置 Basic auth header。同一个 endpoint 会在 /v1/traces/v1/metrics/v1/logs 路径接收三类 signal。

如何避免 prompt 和工具内容进入 telemetry?

默认 telemetry 只记录结构信息:duration、model name、tool name 和 token counts,不记录 prompt 和工具内容。只有设置 OTEL_LOG_USER_PROMPTSOTEL_LOG_TOOL_DETAILSOTEL_LOG_TOOL_CONTENTOTEL_LOG_RAW_API_BODIES 时,内容才会进入 telemetry。除非你的管线允许存储这些数据,否则保持这些开关关闭;如果需要折中,可以在 OpenObserve ingestion pipeline 中做服务端脱敏。

小结

Claude Agent SDK 应用的可观测性,不应该停留在“任务完成耗时”这一层。真正可用的观测体系需要覆盖:

  • Agent loop 的 trace tree;
  • 模型请求的 token、latency 和 cost;
  • 工具调用的等待时间、执行时间和错误;
  • MCP server 和子 Agent 的嵌套调用;
  • 权限决策、工具结果和 API 错误等审计事件;
  • prompt、工具内容和 end-user identity 的敏感数据策略。

OpenTelemetry 给这套体系提供了统一协议。Claude Code CLI 已经内置对应 telemetry,Agent SDK 只需要把环境变量传给它。对生产 Agent 来说,这不是锦上添花,而是判断“慢在哪里、错在哪里、花在哪里、谁触发了什么动作”的基础设施。