第三十六章:Telemetry 为什么是正式基础设施
把 analytics、OTEL 和 attribution 放回同一张观测与治理地图里。
1. 为什么这章必须单独拆
前面的专题里已经不断碰到 telemetry、analytics、event logging、Datadog、OTEL。
但如果只把它们理解成“到处埋点”,就会低估 Claude Code 在这层投入的工程组织。
从源码看,这里至少同时有三条线:
- analytics 事件总线
- OpenTelemetry 级别的 traces / metrics / logs
- commit attribution 这类和代码产出有关的治理记录
这三条线都和“观测”有关,但目标完全不一样。
相关源码锚点:
src/services/analytics/index.tssrc/services/analytics/sink.tssrc/services/analytics/config.tssrc/services/analytics/firstPartyEventLogger.tssrc/services/analytics/firstPartyEventLoggingExporter.tssrc/services/analytics/datadog.tssrc/utils/telemetry/instrumentation.tssrc/utils/telemetryAttributes.tssrc/utils/commitAttribution.tssrc/entrypoints/init.tssrc/main.tsx
2. 先说结论
我对这一块的判断是:
Claude Code 把“可观测性”做成了正式基础设施,而不是散落在业务代码里的日志调用。
最值得抓住的几个结论是:
- analytics 入口故意零依赖,先排队、后挂 sink。
- 事件会按 sink 分层脱敏,不是所有后端都拿同样的数据。
- OTEL 是另一条更底层的性能与 tracing 管线。
- 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.tssrc/services/analytics/firstPartyEventLoggingExporter.ts
Claude Code 在这里做得很克制。
5.1 元数据类型是刻意收紧的
源码里甚至专门有这种类型标记:
AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHSAnalyticsMetadata_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.tssrc/services/analytics/sinkKillswitch.ts
这一层统一处理:
- attach / detach sink
- 队列 drain
- sampling
- killswitch
- Datadog gating
尤其值得注意的是:
- Datadog 是更严格的一条支路
- GrowthBook killswitch 会动态关某些 sink
- 不是所有环境都允许 analytics 打开
所以这里更像一个观测路由器,而不是“发 HTTP”的薄封装。
7. OTEL:另一条更底层的运行时观测面
源码锚点:
src/utils/telemetry/instrumentation.tssrc/utils/telemetryAttributes.tssrc/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.tssrc/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.tsxsrc/entrypoints/init.tssrc/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 这一层最值得学的,不是“打了多少点”,而是它把观测、脱敏、路由和治理做成了一条正式运行时管线。