Book 第三十六章:Telemetry 为什么是正式基础设施
第六部分:迁移与附录

第三十六章:Telemetry 为什么是正式基础设施

把 analytics、OTEL 和 attribution 放回同一张观测与治理地图里。

1. 为什么这章必须单独拆

前面的专题里已经不断碰到 telemetry、analytics、event logging、Datadog、OTEL。

但如果只把它们理解成“到处埋点”,就会低估 Claude Code 在这层投入的工程组织。

从源码看,这里至少同时有三条线:

  1. analytics 事件总线
  2. OpenTelemetry 级别的 traces / metrics / logs
  3. commit attribution 这类和代码产出有关的治理记录

这三条线都和“观测”有关,但目标完全不一样。

相关源码锚点:

  • src/services/analytics/index.ts
  • src/services/analytics/sink.ts
  • src/services/analytics/config.ts
  • src/services/analytics/firstPartyEventLogger.ts
  • src/services/analytics/firstPartyEventLoggingExporter.ts
  • src/services/analytics/datadog.ts
  • src/utils/telemetry/instrumentation.ts
  • src/utils/telemetryAttributes.ts
  • src/utils/commitAttribution.ts
  • src/entrypoints/init.ts
  • src/main.tsx

2. 先说结论

我对这一块的判断是:

Claude Code 把“可观测性”做成了正式基础设施,而不是散落在业务代码里的日志调用。

最值得抓住的几个结论是:

  1. analytics 入口故意零依赖,先排队、后挂 sink。
  2. 事件会按 sink 分层脱敏,不是所有后端都拿同样的数据。
  3. OTEL 是另一条更底层的性能与 tracing 管线。
  4. commit attribution 虽然也算观测,但它更偏治理与审计。

3. 总体结构图

flowchart TD
    A["runtime / tools / commands"] --> B["analytics/index.ts<br/>零依赖入口"]
    B --> C["内存队列"]
    C --> D["sink.ts<br/>路由 / sampling / killswitch"]
    D --> E["First-party event logger"]
    D --> F["Datadog"]

    G["OTEL instrumentation"] --> H["traces / metrics / logs / perfetto"]

    I["git / session / permission data"] --> J["commitAttribution"]

    E --> K["受控字段上报"]
    F --> L["更严格白名单事件"]
    H --> M["性能与运行时诊断"]
    J --> N["代码贡献归因 / trailer"]

4. Analytics 入口:为什么要故意做成零依赖

源码锚点:

  • src/services/analytics/index.ts

这里最值钱的设计不是 API 长什么样,而是它刻意保持:

  • 零依赖
  • 可早期调用
  • sink 未初始化时先排队

这直接解决了两个实际问题:

  • 启动期 import cycle
  • sink 还没 attach 前丢事件

所以这里的重点不是“立刻发出去”,而是先把事件安全接住。

5. 数据分层:不是所有 sink 都能看到原始元数据

源码锚点:

  • src/services/analytics/sink.ts
  • src/services/analytics/firstPartyEventLoggingExporter.ts

Claude Code 在这里做得很克制。

5.1 元数据类型是刻意收紧的

源码里甚至专门有这种类型标记:

  • AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS
  • AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED

这不是形式主义,而是在逼调用方显式承认:

  • 这段字符串是不是代码
  • 是不是路径
  • 是不是 PII

5.2 _PROTO_* 字段会按 sink 分层处理

处理方式大致是:

  • first-party logger 可以拿到更完整的 payload
  • 通用 sink 会剥掉 _PROTO_*
  • exporter 再把允许的 _PROTO_skill_name_PROTO_plugin_name_PROTO_marketplace_name 提升到受控字段

这说明作者不是单纯做“脱敏”,而是在做:

按接收端能力和信任级别分发不同视图。

6. sink.ts 是真正的路由层

源码锚点:

  • src/services/analytics/sink.ts
  • src/services/analytics/sinkKillswitch.ts

这一层统一处理:

  • attach / detach sink
  • 队列 drain
  • sampling
  • killswitch
  • Datadog gating

尤其值得注意的是:

  • Datadog 是更严格的一条支路
  • GrowthBook killswitch 会动态关某些 sink
  • 不是所有环境都允许 analytics 打开

所以这里更像一个观测路由器,而不是“发 HTTP”的薄封装。

7. OTEL:另一条更底层的运行时观测面

源码锚点:

  • src/utils/telemetry/instrumentation.ts
  • src/utils/telemetryAttributes.ts
  • src/entrypoints/init.ts

OTEL 这一层在做的事情,和 analytics 不是一回事。

它负责的是:

  • meter / tracer / logger 初始化
  • traces / metrics / logs
  • perfetto
  • beta tracing
  • 优雅 flush / shutdown

初始化时还会看宿主形态,比如:

  • 格式化输出模式下去掉 console exporter
  • 等 remote managed settings 就位后再做部分初始化

这说明作者很清楚:

  • 有些观测能早开
  • 有些观测必须等 trust / settings / provider 确认后再开

8. Analytics 配置:它默认就在帮你收口风险

源码锚点:

  • src/services/analytics/config.ts

analytics 并不是默认无脑开启。

源码里明确会因为这些条件关闭或收紧:

  • test 环境
  • Bedrock / Vertex / Foundry
  • no-telemetry
  • 只保 essential traffic 的隐私模式

这说明作者对 provider 边界和隐私边界是有意识的。

9. Datadog 与 first-party logger:两条不同策略

源码锚点:

  • src/services/analytics/datadog.ts
  • src/services/analytics/firstPartyEventLogger.ts

Datadog 这条线明显更保守:

  • 只有白名单事件
  • 只在 production
  • 只在 first-party provider
  • 批量上报
  • 会做 model / tool 名归一化,避免高基数

而 first-party event logger 更像 Claude 自己的正式产品事件流水:

  • 在 log 时补齐全量 metadata
  • 配置变化时可重建 provider / logger
  • 与 org metrics opt-out 的语义又不完全一样

10. Attribution:这是“观测”,但更接近治理

源码锚点:

  • src/utils/commitAttribution.ts

这一层特别值得单列,因为它不只是发事件。

它会跟踪:

  • 文件级 Claude / human 贡献
  • 当前 session baseline
  • permission prompts
  • escape count
  • 开始时的 HEAD SHA

还有一个很关键的防线:

  • 只有 allowlist 内的内部仓库才允许写内部模型名
  • 仓库分类失败时默认 fail-closed

这说明 attribution 不是“统计好看”,而是直接触碰审计和对外暴露边界。

11. 初始化与 flush 点:这层真正嵌进了 runtime 生命周期

源码锚点:

  • src/main.tsx
  • src/entrypoints/init.ts
  • src/commands/logout/logout.tsx

几个很值钱的观察:

  • 早期 init 会尽早准备 1P logging
  • 子命令在 preAction 挂 sink,避免排队事件丢失
  • flushTelemetry() 会在 logout / org switch 这类节点前显式触发

也就是说,这层不是后台可有可无的东西,而是被接进了 runtime 生命周期的关键收口点。

12. 对 C# 拆分最有用的建议

如果做 C# 版,这一层至少应该拆成四层:

12.1 AnalyticsFacade

负责:

  • 零依赖事件 API
  • 早期排队
  • 最小调用面

12.2 TelemetryRouter

负责:

  • sink attach
  • sampling
  • killswitch
  • sink-specific redaction

12.3 OTelRuntime

负责:

  • tracing / metrics / logs
  • init / flush / shutdown
  • exporter lifecycle

12.4 AttributionService

负责:

  • repo/session 级归因
  • commit trailer 策略
  • 内部模型名暴露保护

13. 一句话收口

Claude Code 在 telemetry 这一层最值得学的,不是“打了多少点”,而是它把观测、脱敏、路由和治理做成了一条正式运行时管线。