Telemetry and tracing
This page owns the traffic, telemetry, analytics, and tracing/export side of the ops layer. It separates observability transport from local debug logs and from feature-gate decision logic.
Use Diagnostics and debug logs for local debug evidence, Feature gates reference for GrowthBook/tengu_* feature gates, and Environment variables reference for the canonical env-var list.
Source anchors
| Semantic alias | String or symbol | Meaning |
|---|---|---|
| EssentialTrafficGate | CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC | Coarse traffic gate: essential traffic only. |
| DisableTelemetryGate | DISABLE_TELEMETRY | Telemetry disable gate. |
| DoNotTrackGate | DO_NOT_TRACK | Telemetry disable gate. |
| DisableErrorReportingGate | DISABLE_ERROR_REPORTING | Error-reporting disable gate. |
| OtelHeadersEnv | OTEL_EXPORTER_OTLP_HEADERS | OpenTelemetry exporter env surface. |
| TraceExportPath | /v1/traces | Beta/OTEL trace export path. |
| LogExportPath | /v1/logs | Beta/OTEL log export path. |
| TraceparentEnv | TRACEPARENT | Trace-context propagation. |
| AnalyticsFlushHook | flushAnalyticsSinks | Shutdown-time analytics/log/trace flush. |
| OtelHeadersHelperRuntime | getOtelHeadersFromHelper, CLAUDE_CODE_OTEL_HEADERS_HELPER_DEBOUNCE_MS | Settings-provided OTEL headers can come from a debounced helper command. |
Bundle module in cli.renamed.js
| Semantic alias | Loader line | Representative renamed exports | Atlas entry |
|---|---|---|---|
AnalyticsEventSink | 3474 | logEvent, logEventAsync, createAnalyticsState, attachAnalyticsSink, stripProtoFields, _setGlobalAnalyticsStateForTesting, setGlobalAnalyticsStateForTesting | Bundle module map — observability and ops |
Traffic policy
The traffic gate visible near line ~133 distinguishes coarse network policy from telemetry-specific disablement.
| Condition | Runtime traffic mode |
|---|---|
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC is set | essential-traffic |
DISABLE_TELEMETRY is set | no-telemetry |
DO_NOT_TRACK is truthy | no-telemetry |
| none of the above | default |
This distinction matters because essential traffic can still allow network calls required for core operation, while telemetry/error-reporting sinks are reduced or disabled.
Telemetry signal families
cli.renamed.js contains many tengu_* strings. In this wiki, they are treated as operational signal names unless surrounding source confirms a product behavior. Common families include:
| Family | Examples | Interpretation |
|---|---|---|
| Tool/security | tengu_tool_use_can_use_tool_allowed, tengu_tool_use_can_use_tool_rejected | Tool permission outcomes and security decisions. |
| MCP/plugins | tengu_mcp_tool_call_auth_error, plugin install/update signals | Integration health and auth failures. |
| Agents/scheduling | scheduled-task, auto-mode, background-agent signals | Agent/task runtime observability. |
| Remote/bridge | bridge state, remote backend, CCR-style signals | Remote Control and bridge operation. |
| Updater | tengu_native_auto_updater_* | Native updater lifecycle. |
| API/model usage | API request duration, token, cost, retry, and quota signals | Provider-call accounting and support evidence. |
OpenTelemetry and trace export
| Surface | Role |
|---|---|
TRACEPARENT, TRACESTATE | W3C trace-context propagation into subprocess/session env when available. |
BETA_TRACING_ENDPOINT | If set, beta exporter delegates to ${endpoint}/v1/traces and ${endpoint}/v1/logs. |
OTEL_EXPORTER_OTLP_ENDPOINT | Standard OTLP endpoint. |
OTEL_EXPORTER_OTLP_PROTOCOL and per-signal protocol envs | Selects grpc, http/json, or http/protobuf exporters. |
OTEL_EXPORTER_OTLP_HEADERS | Header configuration for OTLP export. |
OTEL_METRICS_EXPORTER, OTEL_LOGS_EXPORTER | Selects metrics/log exporters such as console, OTLP, or prometheus. |
OTEL_LOG_TOOL_DETAILS, OTEL_LOG_TOOL_CONTENT, OTEL_LOG_USER_PROMPTS | Controls how much tool/user detail can be logged. |
--add-trace-attribute key=value | Adds runtime trace attributes. |
The source installs final flush hooks on exit paths, so logs/traces get a best-effort drain during shutdown.
otelHeadersHelper is a settings-provided command helper for OTEL headers. The decoded runtime debounces helper execution with CLAUDE_CODE_OTEL_HEADERS_HELPER_DEBOUNCE_MS, requires the helper to return a JSON object with string values, and logs helper failures as OpenTelemetry header errors. Project/local helper commands are also workspace-trust gated; see Settings, policy, and integrations.
Error reporting
Error reporting is controlled separately from telemetry. DISABLE_ERROR_REPORTING, provider-specific modes, and essential-traffic mode can short-circuit external error reporting even when local debug logs exist.
Safe interpretation rules
- Treat
tengu_*as evidence of telemetry or a feature gate, not as a stable public event schema. - Do not infer live network behavior from static endpoint strings alone.
- Keep sensitive values out of logs; env-var names can be documented, values should not be captured.
- Use Diagnostics and debug logs for file/stdout/stderr debug evidence.
OpenTelemetry stack internals
The TelemetryStack module (cli.renamed.js:374149 onward) is the OpenTelemetry bootstrap. It is gated by CLAUDE_CODE_ENABLE_TELEMETRY and by a separate first-party BigQuery metrics path. The runtime ships an OTLP exporter for each of the three OTel signals (metrics, logs, traces) and chooses protocol/transport at startup from the standard OTEL_EXPORTER_OTLP_* env vars.
Bootstrap (bootstrapTelemetry)
- Defaults
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE = "delta"when unset. - When
vX()(beta tracing predicate) is true AND no global tracer provider exists, installs a beta tracer + logger provider plus a 5 s scheduled span delay. BETA_TRACING_ENDPOINTenv, if set, wires the beta tracer to<endpoint>/v1/tracesand the beta event logger to<endpoint>/v1/logs.
Resource attributes (sT7)
Computed once and cached. Include: service.name = "claude-code", service.version = "2.1.143" (bundle version); OS attributes via zg.osDetector.detect(); host arch only (SEMRESATTRS_HOST_ARCH) — no host id, to avoid identifying the workstation; env detector output, optionally filtered to keep only user.* / identity.* keys when first-party user attributes (T$8()) are present; wsl.version when running under WSL.
Exporter selection (parseExporterTypes / getOtlpLogExporters / sibling helpers)
parseExporterTypes(env) splits comma-separated lists from OTEL_METRICS_EXPORTER, OTEL_LOGS_EXPORTER, OTEL_TRACES_EXPORTER, dropping "none" entries. For each surviving type:
| Type | Protocol(s) | Notes |
|---|---|---|
console | n/a | Inline console exporter; metric variant additionally logs Resource Attributes once for debugging. |
otlp | grpc / http/json / http/protobuf | Picked from OTEL_EXPORTER_OTLP_<SIGNAL>_PROTOCOL (fallback OTEL_EXPORTER_OTLP_PROTOCOL). Each protocol lazily imports its own OTLPMetric/Log/Trace exporter. Headers come from parseOtelHeadersEnvVar(); URL is <endpoint>/v1/<signal>. When getGatewayAuth() exists, the URL is rewritten to the gateway and an async header callback signs each request. |
prometheus (metrics only) | local pull | Adds a PrometheusExporter wrapped into the same PeriodicExportingMetricReader. |
Unknown protocol or type strings throw a descriptive error so misconfigured env vars surface at startup.
initializeTelemetry()
profileCheckpoint("telemetry_init_start").bootstrapTelemetry().- Install the W3C trace context propagator.
- When
C$q()(subprocess-spawn predicate) is true, stripconsolefrom everyOTEL_*_EXPORTERenv so subprocesses do not double-print to the parent terminal. - Build OTLP exporters only when
isTelemetryEnabled()is true. - If
isBigQueryMetricsEnabled()is true (PmH()ORisClaudeAISubscriber() && org_type in (enterprise, team)), also add a 5-minute periodic exporter shipping first-party metrics to BigQuery. - When
vX()(beta tracing) is on, build only the meter provider on top of the beta tracer. - Otherwise build the full pipeline:
MeterProviderwith readers; OTLP log exporters wrapped in batch processors withOTEL_LOGS_EXPORT_INTERVAL(default 5 s); OTLP trace exporters withOTEL_TRACES_EXPORT_INTERVAL(default 5 s). - Register a shutdown hook that flushes and shuts down all providers within
CLAUDE_CODE_OTEL_SHUTDOWN_TIMEOUT_MS(default 2 s). - Return a
Meternamedcom.anthropic.claude_code.
flushTelemetry()
Forces a flush of the meter, logger, and tracer providers concurrently. Bounded by CLAUDE_CODE_OTEL_FLUSH_TIMEOUT_MS (default 5 s). On timeout logs a warning; on other failures logs an error. Called from session-end and gracefulShutdown paths.
First-party event sampling (getEventSamplingConfig, shouldSampleEvent, logGrowthBookExperimentTo1P)
The EventSamplingControl module (loaders at cli.renamed.js:167043, 167244) gates first-party event logging:
getEventSamplingConfig()resolvestengu_event_sampling_configviagetFeatureValue_CACHED_WITH_REFRESHand validates against a Zod schema.shouldSampleEvent(eventName)returns true when the config opts the event in.logEventTo1PAwaitable(eventName, payload)is the awaitable variant for callers that need to confirm the 1P backend received the event.logGrowthBookExperimentTo1P(experimentId, variationId, attributes)is the bridge from GrowthBook experiment exposure to the 1P pipeline.shutdown1PEventLogging()andreinitialize1PEventLoggingIfConfigChanged()drain the queue on shutdown and re-arm when the sampling config changes mid-session.
The 1P pipeline runs in parallel with the OTel pipeline: one logEvent call can fan out to BigQuery metrics, the 1P event sink, and any operator-configured OTLP backend.
Environment variables that shape the stack
| Env var | Effect |
|---|---|
CLAUDE_CODE_ENABLE_TELEMETRY | Master switch for operator-configured OTel exporters. |
OTEL_METRICS_EXPORTER / OTEL_LOGS_EXPORTER / OTEL_TRACES_EXPORTER | Per-signal exporter type list. |
OTEL_EXPORTER_OTLP_PROTOCOL / OTEL_EXPORTER_OTLP_<SIGNAL>_PROTOCOL | grpc / http/json / http/protobuf. |
OTEL_EXPORTER_OTLP_ENDPOINT | Base URL; signal path is appended. |
OTEL_EXPORTER_OTLP_HEADERS | Comma-separated k=v headers. |
OTEL_METRIC_EXPORT_INTERVAL, OTEL_LOGS_EXPORT_INTERVAL, OTEL_TRACES_EXPORT_INTERVAL | Per-signal flush schedule. |
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE | Defaults to delta if unset. |
CLAUDE_CODE_OTEL_SHUTDOWN_TIMEOUT_MS / CLAUDE_CODE_OTEL_FLUSH_TIMEOUT_MS | Shutdown / flush bounds. |
BETA_TRACING_ENDPOINT | Beta tracer + logger destination. |
Error log sink and MCP error log fan-out
Separately from the OTel / 1P pipelines, the runtime persists per-session error and MCP logs to disk via the sink installed by initializeErrorLogSink (cli.renamed.js line 600854). The sink is wired during startup right next to the analytics sink (initializeErrorLogSink(), initializeAnalyticsSink()).
Files are named by today’s date stamp (zV4 = eYq(new Date())) and rooted under the user data directory:
getErrorsPath() = path.join(claudeDir.errors(), DATE + ".jsonl")— catches everylogError(err)call. The line carries an axios-aware prefix when the error came fromo8.isAxiosError(H):[url=...,status=...,body=...] error.stack. That prefix is what makes 4xx/5xx responses self-diagnostic when a user shares anerrors.jsonlslice.getMCPLogsPath(serverName) = path.join(claudeDir.mcpLogs(serverName), DATE + ".jsonl")— fanout target forlogMCPError(server, err)andlogMCPDebug(server, msg). Each line is{ error?, debug?, timestamp, sessionId, cwd }.
Both writers share the buffered file-writer factory YF6(path): per-path lazy creation, batched writes (flushIntervalMs: 1000, maxBufferSize: 50), appendFileSync with an inline mkdirSync fallback on the first ENOENT, and a one-shot suppressed-error mode (if (!K) ((K = !0), onDebug("Dropping log batch for ${path}: ..."))) so a disk-full or permission failure logs once but never spams the debug log. Every writer registers SK(async () => $?.dispose()) so the shutdown coordinator drains buffered lines on SIGTERM.
initializeErrorLogSink() itself does not write to disk — it calls Hfq({ logError, logMCPError, logMCPDebug, getErrorsPath, getMCPLogsPath }) to register the five functions with the global error-routing surface that logError/logEvent paths look up. Tests get explicit hooks: _flushLogWritersForTesting() forces every cached writer to flush, and _clearLogWritersForTesting() disposes them and clears the map.
Related docs
Created and maintained by Yingting Huang.