Telemetry / OTel
遥测导出与团队分析——CC 是「文档完备的 OTel 导出 + 1495 个内部 tengu_* 事件」双平面;Codex 把 OTel 做成内置 crate 但配置面零文档,内部 analytics 默认把指标发给 Statsig
Telemetry / OTel
证据标签:[一手源码] [一手文档] [推断]。CC 源码为重建快照(src_2026-03-31),仅作架构描述与短引用;Codex 源码 codex-rs @ b89ce9a (2026-06-06)。
结论
两个 runtime 都有两套互不相干的遥测平面:一套「给用户看的」(导出到你自己的 OTel 后端),一套「给厂商看的」(内部 analytics 回传厂商)。差别在成熟度与透明度完全倒置——CC 的用户面 OTel 是行业标杆(8 个 metrics、20+ 个 events、beta traces,文档逐字段穷举,含隐私分级开关 OTEL_LOG_*),内部面则是约 1495 个 tengu_* 事件名(仅源码可见)经 Datadog + 第一方 BigQuery 双 sink 回传;Codex 的用户面 OTel 内置成 codex-otel crate(config.toml [otel] 配 OTLP HTTP/gRPC),但在官方 config 文档里零覆盖,且 metrics exporter 的默认值是 Statsig——即默认状态下指标流向 OpenAI 的 Statsig,内部面则是 codex-analytics crate 的 track_* 事件直接 POST 回 Codex backend(仅 ChatGPT 登录时发送)。Harness 想做团队级观测:接 CC 用 OTel 环境变量即可零代码拿到全量指标;接 Codex 要么写 [otel] TOML(无文档,照源码抄),要么放弃推送、回读磁盘 rollout(见 transcript 章)。
研究问题
- 两边各自导出哪些 metrics / events / traces?怎么开、怎么配?
- 内部 analytics(厂商回传)各自记什么、发到哪、怎么关?
- 隐私边界:prompt / 命令 / 文件内容默认露不露,靠什么开关分级?
- hook 能否作为第三条观测通道?
各 Agent 设计与实现
Claude Code
平面一:用户侧 OTel 导出(opt-in,文档完备)。[一手文档] monitoring-usage.md:15-36——CLAUDE_CODE_ENABLE_TELEMETRY=1 总开关 + 标准 OTEL_METRICS_EXPORTER / OTEL_LOGS_EXPORTER(otlp/prometheus/console)+ OTEL_EXPORTER_OTLP_ENDPOINT 等标准 OTel 变量。源码侧实现在 utils/telemetry/instrumentation.ts [一手源码]::325 读 CLAUDE_CODE_ENABLE_TELEMETRY,:563 Meter 命名 com.anthropic.claude_code,OTLP exporter 动态 import(:158-170)。
导出内容三层 [一手文档] monitoring-usage.md:
- Metrics(8 个)(:417-426):
claude_code.session.count/lines_of_code.count/pull_request.count/commit.count/cost.usage(USD)/token.usage/code_edit_tool.decision/active_time.total。counter 创建点[一手源码]bootstrap/state.ts:955-983,与文档逐名对得上。 - Events(20+ 个)(:537-997):
user_prompt、tool_result、api_request(含cost_usd/4 类 token 数/request_id)、api_error、api_refusal、tool_decision(permission 决策来源六分类)、permission_mode_changed、auth、mcp_server_connection、hook 生命周期四件套、compaction等。prompt.id把一次用户输入触发的所有事件串起来(:525-535)。 - Traces(beta)(:119-157):再加
CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1,span 树claude_code.interaction → llm_request / tool → tool.blocked_on_user + tool.execution;Bash 子进程自动注入TRACEPARENT实现端到端分布式追踪(:135)。
隐私分级是设计亮点 [一手文档] monitoring-usage.md:87-90、:1161-1173:默认 prompt 文本、工具参数、工具内容全部脱敏,按需逐级打开 OTEL_LOG_USER_PROMPTS → OTEL_LOG_TOOL_DETAILS → OTEL_LOG_TOOL_CONTENT → OTEL_LOG_RAW_API_BODIES(最后者直接落整段 Messages API 请求/响应,文档明示「enabling this implies consent to everything」)。这些数据只去你配置的 OTel endpoint,不去 Anthropic。企业可经 managed settings 强制下发且用户不可覆盖(:44-65)。注意:CC 不向子进程传播 OTEL_* 变量(:67)。
平面二:内部 tengu_* analytics(opt-out,仅源码可见)。[一手源码] 全源码树 grep 出 1495 个唯一 tengu_* 事件名。架构:services/analytics/index.ts 是零依赖入口(事件先排队、sink attach 后排空,:95-123);sink.ts:48-72 双路 fanout——Datadog(受 Statsig gate tengu_log_datadog_events 控制,:20)+ 第一方 BigQuery exporter(firstPartyEventLoggingExporter.ts)。两个防泄漏机制值得抄:① 类型标记 AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS(index.ts:19)——metadata 字段类型禁止裸 string,强迫开发者显式声明「这不是代码/路径」;② _PROTO_* 前缀字段只进有特权访问控制的 1P PII 列,发 Datadog 前一律 strip(index.ts:21-58)。关闭方式 [一手文档] data-usage.md:98:DISABLE_TELEMETRY=1;Bedrock/Vertex/Foundry 下默认整体关闭(:108-120);CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC 一键关全部非必要流量。
平面三:hook 即观测探针。[一手文档] hooks.md——PreToolUse/PostToolUse/Stop 等 hook 的 stdin JSON 携带 tool_use_id,与 OTel tool_result/tool_decision 事件的 tool_use_id 同值(monitoring-usage.md:567),官方文档明示二者可 join。即不开 OTel 也能用 hook 把工具级遥测推到任意自建管道。
Codex CLI
平面一:内置 codex-otel crate(opt-in,零文档)。[一手源码] otel/README.md + otel/src/config.rs:51-61(OtelSettings:exporter / trace_exporter / metrics_exporter 三信号独立配置,支持 OtlpHttp/OtlpGrpc/Statsig/None,含 mTLS OtelTlsConfig)。用户配置面是 config.toml [otel] 表 [一手源码] config/src/types.rs:513-535(OtelConfigToml:log_user_prompt / environment / exporter / trace_exporter / metrics_exporter / span_attributes / tracestate)。默认值是关键 [一手源码] core/src/config/otel.rs:13-23:
let exporter = config.exporter.unwrap_or(OtelExporterKind::None); // 日志:默认不导出
let trace_exporter = config.trace_exporter.unwrap_or(OtelExporterKind::None);
let metrics_exporter = config.metrics_exporter.unwrap_or(OtelExporterKind::Statsig); // 指标:默认发 Statsig即不写任何配置时,logs/traces 不出门,metrics 默认流向 Statsig(OpenAI 侧);总闸是 analytics.enabled=false——core/src/otel_init.rs:70-77 在 analytics 关闭时把 metrics exporter 强制改成 None。grep -i otel codex-rs/config.md 与 codex/docs/*.md 均零命中 [一手文档缺失]——整个 [otel] 配置面只能读源码。
导出的业务事件由 SessionTelemetry 发射 [一手源码] otel/src/events/session_telemetry.rs,共 15 个 codex.* 事件名:codex.api_request、codex.sse_event、codex.websocket_connect/request/event、codex.user_prompt、codex.tool_decision、codex.tool_result、codex.conversation_starts、codex.turn_ttft、codex.startup_phase、codex.sandbox_outcome、codex.auth_recovery、codex.plugin_install_*。prompt 文本默认不记(log_user_prompt 默认 false,config/otel.rs:13)。导出过滤器只放行 codex_otel target 的事件(otel_init.rs:97-101 codex_export_filter),不会把全量 tracing 日志倒给你。
平面二:codex-analytics crate(厂商回传,opt-out)。[一手源码] analytics/src/client.rs:约 25 个 track_* 方法——track_turn_token_usage、track_turn_profile(采样耗时分解七字段,facts.rs:108-116)、track_compaction、track_guardian_review、track_plugin_*、track_skill_invocations、track_hook_run 等。发送目标是 Codex 自家 backend:
// analytics/src/client.rs:416-423
if !auth.uses_codex_backend() { return; }
let url = format!("{base_url}/codex/analytics-events/events");即仅 ChatGPT 登录态发送(API-key/第三方 provider 不发);机器级开关 analytics.enabled [一手源码] core/src/config/mod.rs:1023-1025(注释自述 "disables analytics across Codex product surfaces in this machine"),同样无公开文档。
差异矩阵
| 维度 | Claude Code | Codex CLI |
|---|---|---|
| 用户侧导出协议 | OTel metrics + logs/events + traces(beta),标准 OTEL_* 环境变量 | OTel 三信号,config.toml [otel](TOML 配 endpoint/headers/mTLS) |
| 配置面文档 | monitoring-usage.md 逐字段穷举(1178 行) | 零文档,只能读 config/src/types.rs:513 |
| 默认行为 | 全关(CLAUDE_CODE_ENABLE_TELEMETRY 必须显式置 1) | logs/traces 关,metrics 默认 → Statsig(config/otel.rs:21) |
| 导出 metrics | 8 个 claude_code.*(含 USD cost) | MetricsClient counter/histogram + 15 个 codex.* 事件(无 USD) |
| 隐私分级 | 4 级开关 OTEL_LOG_{USER_PROMPTS,TOOL_DETAILS,TOOL_CONTENT,RAW_API_BODIES} | 1 个 log_user_prompt(默认 false) |
| 内部 analytics | tengu_* ×1495,Datadog + 1P BigQuery 双 sink | track_* ×~25,POST /codex/analytics-events/events |
| 内部 analytics 开关 | DISABLE_TELEMETRY=1(有文档) | analytics.enabled=false(无文档);仅 ChatGPT 登录态才发 |
| 防泄漏机制 | 类型标记 + _PROTO_* PII 分列 + 事件采样配置 | 事件白名单(敏感 ServerNotification 显式 return,client.rs:395-399) |
| hook 作观测通道 | ✅ tool_use_id 与 OTel 事件可 join(有文档) | hook 有 track_hook_run 回传,但无对外 join 契约 |
| 管理员强制下发 | managed settings(MDM 分发,用户不可覆盖) | 无对应机制 [推断](cloud-config 层未见 otel 强制项) |
最小复现
# CC:console exporter 1 秒级验证(monitoring-usage.md:347-350)
export CLAUDE_CODE_ENABLE_TELEMETRY=1 OTEL_METRICS_EXPORTER=console OTEL_METRIC_EXPORT_INTERVAL=1000
claude # 终端可见 claude_code.* 指标 dump
# Codex:~/.codex/config.toml(字段名照 config/src/types.rs:513,无文档)
# [otel]
# exporter = { otlp-http = { endpoint = "http://localhost:4318/v1/logs", protocol = "binary" } }
# trace_exporter = { otlp-grpc = { endpoint = "http://localhost:4317" } }
# 关厂商回传:
# [analytics]
# enabled = falseHarness 接入建议(Yoda 实践)
- Yoda 当前不消费任何一边的 OTel 流——会话级观测全部走磁盘 transcript/rollout 解析(
yoda/src/main/core/conversations/,见 transcript 章)、成本走 transcript usage 重算(见 cost 章)。理由:OTel 是机器级配置(环境变量/全局 TOML),harness 注入会污染用户自己的遥测配置;且 Codex 侧无文档、CC 侧 traces 还在 beta。 - 如果要做团队/机群级看板(多人多机),CC 直接经 managed settings 下发
OTEL_*是正解,零代码改造;Codex 需要给每台机器写[otel]TOML,并接受「字段随版本漂移且无 changelog」。 - 单机 harness 想要 turn 级耗时/token 推送,CC 更轻的路是 statusline + hooks(
tool_use_idjoin),不必起 OTLP collector。 - 隐私默认值要尊重:harness 自动开
OTEL_LOG_TOOL_DETAILS等于替用户决定上报 Bash 全命令——必须显式征得同意(CC 文档同款立场)。 - 注意 Codex 的
metrics_exporter默认 Statsig:对隐私敏感客户交付时,harness 应主动写analytics.enabled=false或metrics_exporter = "none"。
反例 / 边界
- CC 的两套平面开关互不影响:
DISABLE_TELEMETRY只关 Anthropic 回传,不影响你自配的 OTel 导出;反之CLAUDE_CODE_ENABLE_TELEMETRY不开也不会阻止 tengu_* 回传[一手文档]monitoring-usage.md:1163。 - CC OTel 的
user.email在 OAuth 登录时总是带上(monitoring-usage.md:1165)——合规场景要在 collector 层脱敏,客户端没有开关。 - Codex 的
codex.*OTel 事件与track_*analytics 事件是两份不同 schema(前者 tracing event,后者 JSON POST),字段不要混用。 - 第三方 provider(CC 走 Bedrock/Vertex、Codex 用 API key)下,内部回传各自默认关闭/不发送——机群统计若混用认证模式会出现系统性漏计。
失效条件
- CC traces 仍标 beta(
CLAUDE_CODE_ENHANCED_TELEMETRY_BETA),span 名/属性可能变更——发版后回归 monitoring-usage.md。 - CC 源码快照为 2026-03-31 重建版,
tengu_*计数(1495)与 sink 拓扑(Datadog gate 名)随 live 版本漂移。 - Codex
[otel]默认值(metrics→Statsig)位于core/src/config/otel.rs:21,任何一次 config 重构都可能翻转该默认——升级后 diff 此文件。 - Codex analytics 仅 ChatGPT 登录态发送的判断依赖
auth.uses_codex_backend()(client.rs:417),认证体系改版需复核。
参考资料
[一手文档]claude-code-docs/docs/monitoring-usage.md(OTel 全量字段)、data-usage.md(opt-out 矩阵)、hooks.md[一手源码]CC:utils/telemetry/instrumentation.ts、services/analytics/{index,sink}.ts、bootstrap/state.ts:955-983[一手源码]Codex:otel/README.md、otel/src/config.rs、otel/src/events/session_telemetry.rs、core/src/config/otel.rs、core/src/otel_init.rs、config/src/types.rs:513-560、analytics/src/{client,facts,lib}.rs- Anthropic 官方 ROI 监控指南:github.com/anthropics/claude-code-monitoring-guide