Skip to content
源码分析手册

监控与成本追踪:从 OTel 遥测到 Token 计费的端到端流程

对应官方文档:claude-code-docs/docs/monitoring-usage.md 里的 Monitoring

它解决了什么问题

2.1.88 运行时中,monitoring 的本质不是简单的 OTEL_* 环境变量透传,而是一条在“信任(Trust)”建立后才接通的本地遥测分流管线。

这套系统服务于两个平行的目标:

  1. 性能与健康度监控(Monitoring):支持用户自定义的 OpenTelemetry (OTel) Exporter,上报 Metrics、Logs 和 Traces,用于分析工具执行耗时和模型响应延迟。
  2. 资源管理与费用追踪(Cost Tracking):在本地实时累加 Token 消耗、API 耗时、工具执行时长及 USD 成本。这套计费逻辑是纯本地的,不依赖云端反向通知。

代码里的真实逻辑

这套机制的实现跨越了 bootstrap/state.tsutils/telemetry/cost-tracker.ts

  1. 信任后的初始化(After-trust Telemetry)src/entrypoints/init.ts 只做基础变量加载。真正的 OTel 初始化(initializeTelemetryAfterTrust())要等交互模式下的 Trust Dialog 确认之后才触发。这是为了安全:防止恶意的 otelHeadersHelper(自定义本地命令)在获得信任前被执行。
  2. 动态 Header 助手(OTel Headers Helper)src/utils/auth.ts 支持 otelHeadersHelper 环境变量。它不是启动时算一次,而是一个带 29 分钟 Debounce 的动态 Header 供应器,专门为需要定期刷新 Token 的 OTLP 后端设计。
  3. 三路信号分流(Signal Splitting)
    • Metrics:走 MeterProvider,强制默认 delta 暂时性(Temporality)。
    • Logs:走 LoggerProvider,所有“事件(Events)”都被包装成 Log Record 发出。
    • Traces:只有在 CLAUDE_CODE_ENABLE_TELEMETRY=1 且开启 Enhanced Telemetry 时才会记录详细的 Interaction Span。
  4. Token 实时计数(Token Counter)src/bootstrap/state.ts 中的 getTokenCounter() 维护了一个按模型和类型(Input、Output、Cache-read、Cache-write)分类的内存计数器。每次模型响应后,addToTotalSessionCost 会立即被调用。
  5. 成本换算逻辑(Cost Calculation)src/utils/modelCost.ts 维护了一张定价查找表。即使模型不在表中,它也会继续累加 Token 并标记为“估算”。
  6. 会话持久化(Cost Tracker)src/cost-tracker.ts 负责将当前 Session 的 Usage 实时落入 ~/.claude.json。这支持了 --resume 时的成本连续性:Claude 知道这整场实验已经花了多少钱。
  7. 组织级 Opt-outsrc/services/api/metricsOptOut.ts 实现了组织级开关。如果是 Claude for Teams/Enterprise,系统会通过磁盘缓存记录该组织是否允许内部 BigQuery Metrics 上报。

实战注意事项

  1. 开关差异CLAUDE_CODE_ENABLE_TELEMETRY 控制的是 Customer OTel,而内部 Analytics(Datadog/1P)受另一套 Privacy 逻辑控制。
  2. 计费非实时云同步:Token 和 Cost 追踪完全基于 SDK 返回的 usage 信息在本地计算,不代表云端账单的最终核算。
  3. 延迟初始化:Telemetry 初始化是分段的。如果 Trust 没过,Customer OTel Exporter 根本不会被创建。
  4. Flush 退出策略:OTel 退出时尝试 Flush 的超时是 2 秒。系统优先保住主流程退出,而不是死等监控数据发完。

接下来看什么

  • 如果你关心具体事件是如何产出的,下一篇该看 Analytics 管线(event-pipeline.md)
  • 如果你关心 Token 计费如何与 Compaction(上下文压缩)联动,去看 Context Compaction 策略
  • 如果你关心如何配置多层级的 Settings,去看 Settings Hierarchy

源码锚点

  • claude-code-opensource/src/utils/telemetry/instrumentation.ts:Customer OTel 的主初始化流程:Metrics、Logs、Traces、Headers Helper。
📄 src/utils/telemetry/instrumentation.ts — Customer OTel 的主初始化流程:Metrics、Logs、Traces、Headers Helper。L45-48 of 826
typescript
  is1PApiCustomer,
  isClaudeAISubscriber,
} from 'src/utils/auth.js'
import { getPlatform, getWslVersion } from 'src/utils/platform.js'
  • claude-code-opensource/src/cost-tracker.ts:Session 级成本与 Token 累加与持久化管理。
📄 src/cost-tracker.ts — Session 级成本与 Token 累加与持久化管理。L9-30 of 324
typescript
  getSessionId,
  getTokenCounter,
  getTotalAPIDuration,
  getTotalAPIDurationWithoutRetries,
  getTotalCacheCreationInputTokens,
  getTotalCacheReadInputTokens,
  getTotalCostUSD,
  getTotalDuration,
  getTotalInputTokens,
  getTotalLinesAdded,
  getTotalLinesRemoved,
  getTotalOutputTokens,
  getTotalToolDuration,
  getTotalWebSearchRequests,
  getUsageForModel,
  hasUnknownModelCost,
  resetCostState,
  resetStateForTests,
  setCostStateForRestore,
  setHasUnknownModelCost,
} from './bootstrap/state.js'
import type { ModelUsage } from './entrypoints/agentSdkTypes.js'
  • claude-code-opensource/src/utils/modelCost.ts:从 Token 到 USD 的转换算法及定价表。
📄 src/utils/modelCost.ts — 从 Token 到 USD 的转换算法及定价表。L28-36 of 232
typescript
  inputTokens: number
  outputTokens: number
  promptCacheWriteTokens: number
  promptCacheReadTokens: number
  webSearchRequests: number
}

// Standard pricing tier for Sonnet models: $3 input / $15 output per Mtok
export const COST_TIER_3_15 = {
  • claude-code-opensource/src/bootstrap/state.ts:内存中的 Token 计数器与 Cost 状态定义。
📄 src/bootstrap/state.ts — 内存中的 Token 计数器与 Cost 状态定义。L86-99 of 1759
typescript
  sessionIngressToken: string | null | undefined
  oauthTokenFromFd: string | null | undefined
  apiKeyFromFd: string | null | undefined
  // Telemetry state
  meter: Meter | null
  sessionCounter: AttributedCounter | null
  locCounter: AttributedCounter | null
  prCounter: AttributedCounter | null
  commitCounter: AttributedCounter | null
  costCounter: AttributedCounter | null
  tokenCounter: AttributedCounter | null
  codeEditToolDecisionCounter: AttributedCounter | null
  activeTimeCounter: AttributedCounter | null
  statsStore: { observe(name: string, value: number): void } | null
  • claude-code-opensource/src/utils/auth.tsotelHeadersHelper 的 Trust 检查与 Debounce 机制。
📄 src/utils/auth.ts — `otelHeadersHelper` 的 Trust 检查与 Debounce 机制。L1752-1755 of 2003
typescript
  const otelHeadersHelper = getConfiguredOtelHeadersHelper()
  if (!otelHeadersHelper) {
    return false
  }
  • claude-code-opensource/src/utils/telemetry/bigqueryExporter.ts:内部 BigQuery Metrics Exporter 的上报控制。
📄 src/utils/telemetry/bigqueryExporter.ts — 内部 BigQuery Metrics Exporter 的上报控制。L40-69 of 253
typescript
export class BigQueryMetricsExporter implements PushMetricExporter {
  private readonly endpoint: string
  private readonly timeout: number
  private pendingExports: Promise<void>[] = []
  private isShutdown = false

  constructor(options: { timeout?: number } = {}) {
    const defaultEndpoint = 'https://api.anthropic.com/api/claude_code/metrics'

    if (
      process.env.USER_TYPE === 'ant' &&
      process.env.ANT_CLAUDE_CODE_METRICS_ENDPOINT
    ) {
      this.endpoint =
        process.env.ANT_CLAUDE_CODE_METRICS_ENDPOINT +
        '/api/claude_code/metrics'
    } else {
      this.endpoint = defaultEndpoint
    }

    this.timeout = options.timeout || 5000
  }

  async export(
    metrics: ResourceMetrics,
    resultCallback: (result: ExportResult) => void,
  ): Promise<void> {
    if (this.isShutdown) {
      resultCallback({
        code: ExportResultCode.FAILED,

基于 Claude Code v2.1.88 开源快照的深度分析