Skip to content

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 aliasString or symbolMeaning
EssentialTrafficGateCLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFICCoarse traffic gate: essential traffic only.
DisableTelemetryGateDISABLE_TELEMETRYTelemetry disable gate.
DoNotTrackGateDO_NOT_TRACKTelemetry disable gate.
DisableErrorReportingGateDISABLE_ERROR_REPORTINGError-reporting disable gate.
OtelHeadersEnvOTEL_EXPORTER_OTLP_HEADERSOpenTelemetry exporter env surface.
TraceExportPath/v1/tracesBeta/OTEL trace export path.
LogExportPath/v1/logsBeta/OTEL log export path.
TraceparentEnvTRACEPARENTTrace-context propagation.
AnalyticsFlushHookflushAnalyticsSinksShutdown-time analytics/log/trace flush.
OtelHeadersHelperRuntimegetOtelHeadersFromHelper, CLAUDE_CODE_OTEL_HEADERS_HELPER_DEBOUNCE_MSSettings-provided OTEL headers can come from a debounced helper command.
AssistantResponseLogclaude_code.assistant_response, OTEL_LOG_ASSISTANT_RESPONSESOptional assistant-response content event.
OtelContentLimitCLAUDE_CODE_OTEL_CONTENT_MAX_LENGTHConfigures the default 60 KB content-attribute truncation bound.
WorkflowCorrelationworkflow.run_id, workflow.nameCorrelates telemetry emitted by workflow-spawned agents.
FirstPartyInitinitialize1PEventLoggingStarts the sampled first-party event route during general initialization.
ThirdPartyInitinitializeTelemetryAfterTrust, tKaSeparately initializes configured OTel providers after trust/managed-settings handling.
PendingOtelDisposition`discardPendingOTelEvents(“not_configured""init_failed”)`

Bundle module in cli.renamed.js

Semantic aliasLoader lineRepresentative renamed exportsAtlas entry
AnalyticsEventSink3474logEvent, logEventAsync, createAnalyticsState, attachAnalyticsSink, stripProtoFields, _setGlobalAnalyticsStateForTesting, setGlobalAnalyticsStateForTestingBundle module map — observability and ops

Traffic policy

The traffic gate visible near line ~133 distinguishes coarse network policy from telemetry-specific disablement.

ConditionRuntime traffic mode
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC is setessential-traffic
DISABLE_TELEMETRY is setno-telemetry
DO_NOT_TRACK is truthyno-telemetry
none of the abovedefault

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:

FamilyExamplesInterpretation
Tool/securitytengu_tool_use_can_use_tool_allowed, tengu_tool_use_can_use_tool_rejectedTool permission outcomes and security decisions.
MCP/pluginstengu_mcp_tool_call_auth_error, plugin install/update signalsIntegration health and auth failures.
Agents/schedulingscheduled-task, auto-mode, background-agent signalsAgent/task runtime observability.
Remote/bridgebridge state, remote backend, CCR-style signalsRemote Control and bridge operation.
Updatertengu_native_auto_updater_*Native updater lifecycle.
API/model usageAPI request duration, token, cost, retry, and quota signalsProvider-call accounting and support evidence.

Route and initialization boundaries

“Telemetry” is not one universal event bus. The readable source exposes these distinct routes:

RouteInitialization and dispatch
First-party eventsPgS() starts initialize1PEventLogging() during general initialization and reinitializes it when GrowthBook configuration changes. The analytics sink applies event sampling and calls the first-party event logger.
Datadog eventsThe same analytics sink additionally calls trackDatadogEvent() only when shouldTrackDatadog() passes its traffic/gate checks.
OpenTelemetryinitializeTelemetryAfterTrust() invokes the separate tKa()/initializeTelemetry() path. Instrumentation must explicitly emit OTel metrics, logs, or spans; an arbitrary logEvent() call is not automatically copied to all three signals.
BigQuery metricsWhen eligible, a five-minute BigQuery metric reader is added to the OTel meter provider. It exports emitted metric instruments, not the generic first-party event payload by implication.
Debug loglogForDebugging() writes the redacted text log described in Diagnostics and debug logs.
Error/MCP JSONLinitializeErrorLogSink() registers separate date-stamped local writers for errors and per-server MCP debug/error records.

When remote managed settings are active, third-party telemetry waits for those settings, reapplies their environment variables, captures the administrative steering snapshot, reloads extra CA and mTLS material, and refreshes global agents if network material changed before calling tKa(). Without that remote-settings path, tKa() is called directly after the trust-oriented entry point.

tKa() is guarded against duplicate initialization. Its finally block closes the pending-event window: queued OTel events are discarded as not_configured if no configured path materialized, or init_failed when configured telemetry initialization threw. This is bounded pre-initialization handling, not a promise that arbitrary telemetry remains buffered until a sink recovers.

Current log correlation and content controls

2.1.215 emits or enriches these source-visible OTel log surfaces:

SurfacePurpose
claude_code.assistant_responseAssistant response text. Redacted unless content logging is enabled.
message.uuidStable message-level correlation key.
client_request_idConnects runtime logs to one provider request/control path.
tool_sourceIdentifies built-in, MCP, plugin, workflow, or other tool provenance.
workflow.run_id, workflow.nameReconstructs activity emitted by all agents in one workflow run.

OTEL_LOG_ASSISTANT_RESPONSES=1 opts into assistant text. If unset, it follows OTEL_LOG_USER_PROMPTS; deployments that already log prompts therefore also receive assistant-response events after upgrade unless they set it to 0. CLAUDE_CODE_OTEL_CONTENT_MAX_LENGTH changes the shipped 60 KB truncation limit for content attributes.

OpenTelemetry and trace export

SurfaceRole
TRACEPARENT, TRACESTATEW3C trace-context propagation into subprocess/session env when available.
BETA_TRACING_ENDPOINTIf set, beta exporter delegates to ${endpoint}/v1/traces and ${endpoint}/v1/logs.
OTEL_EXPORTER_OTLP_ENDPOINTStandard OTLP endpoint.
OTEL_EXPORTER_OTLP_PROTOCOL and per-signal protocol envsSelects grpc, http/json, or http/protobuf exporters.
OTEL_EXPORTER_OTLP_HEADERSHeader configuration for OTLP export.
OTEL_METRICS_EXPORTER, OTEL_LOGS_EXPORTERSelects metrics/log exporters such as console, OTLP, or prometheus.
OTEL_LOG_TOOL_DETAILS, OTEL_LOG_TOOL_CONTENT, OTEL_LOG_USER_PROMPTSControls how much tool/user detail can be logged.
--add-trace-attribute key=valueAdds runtime trace attributes.

The source installs final flush hooks on exit paths, so initialized OTel providers get a bounded best-effort drain during shutdown. Other routes have their own shutdown/drain functions.

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

  1. Treat tengu_* as evidence of telemetry or a feature gate, not as a stable public event schema.
  2. Do not infer live network behavior from static endpoint strings alone.
  3. Keep sensitive values out of logs; env-var names can be documented, values should not be captured.
  4. 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_ENDPOINT env, if set, wires the beta tracer to <endpoint>/v1/traces and the beta event logger to <endpoint>/v1/logs.

Resource attributes (sT7)

Computed once and cached. Include: service.name = "claude-code", service.version = "2.1.215" (bundle version); OS attributes via the OS detector; host arch only (no host id, to avoid identifying the workstation); env detector output, optionally filtered to keep only user/identity keys when first-party attributes 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:

TypeProtocol(s)Notes
consolen/aInline console exporter; metric variant additionally logs Resource Attributes once for debugging.
otlpgrpc / http/json / http/protobufPicked 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 pullAdds a PrometheusExporter wrapped into the same PeriodicExportingMetricReader.

Unknown protocol or type strings throw a descriptive error so misconfigured env vars surface at startup.

initializeTelemetry()

  1. profileCheckpoint("telemetry_init_start").
  2. bootstrapTelemetry().
  3. Install the W3C trace context propagator.
  4. When C$q() (subprocess-spawn predicate) is true, strip console from every OTEL_*_EXPORTER env so subprocesses do not double-print to the parent terminal.
  5. Build OTLP exporters only when isTelemetryEnabled() is true.
  6. If isBigQueryMetricsEnabled() is true (PmH() OR isClaudeAISubscriber() && org_type in (enterprise, team)), also add a 5-minute periodic exporter shipping first-party metrics to BigQuery.
  7. When vX() (beta tracing) is on, build only the meter provider on top of the beta tracer.
  8. Otherwise build the full pipeline: MeterProvider with readers; OTLP log exporters wrapped in batch processors with OTEL_LOGS_EXPORT_INTERVAL (default 5 s); OTLP trace exporters with OTEL_TRACES_EXPORT_INTERVAL (default 5 s).
  9. Register a shutdown hook that flushes and shuts down all providers within CLAUDE_CODE_OTEL_SHUTDOWN_TIMEOUT_MS (default 2 s).
  10. Return a Meter named com.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() resolves tengu_event_sampling_config via getFeatureValue_CACHED_WITH_REFRESH and 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() and reinitialize1PEventLoggingIfConfigChanged() drain the queue on shutdown and re-arm when the sampling config changes mid-session.

The first-party route and OTel stack can both be active in one process, but they are not automatic mirrors. A generic analytics logEvent is sampled into the first-party event sink and may additionally go to Datadog. OTel logs, spans, and metric instruments are emitted at explicit OTel call sites; the BigQuery reader consumes metric instruments from that meter pipeline.

Environment variables that shape the stack

Env varEffect
CLAUDE_CODE_ENABLE_TELEMETRYMaster switch for operator-configured OTel exporters.
OTEL_METRICS_EXPORTER / OTEL_LOGS_EXPORTER / OTEL_TRACES_EXPORTERPer-signal exporter type list.
OTEL_EXPORTER_OTLP_PROTOCOL / OTEL_EXPORTER_OTLP_<SIGNAL>_PROTOCOLgrpc / http/json / http/protobuf.
OTEL_EXPORTER_OTLP_ENDPOINTBase URL; signal path is appended.
OTEL_EXPORTER_OTLP_HEADERSComma-separated k=v headers.
OTEL_METRIC_EXPORT_INTERVAL, OTEL_LOGS_EXPORT_INTERVAL, OTEL_TRACES_EXPORT_INTERVALPer-signal flush schedule.
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCEDefaults to delta if unset.
CLAUDE_CODE_OTEL_SHUTDOWN_TIMEOUT_MS / CLAUDE_CODE_OTEL_FLUSH_TIMEOUT_MSShutdown / flush bounds.
BETA_TRACING_ENDPOINTBeta tracer + logger destination.
OTEL_LOG_ASSISTANT_RESPONSESEnables/disables assistant-response content logs; inherits prompt-content opt-in when unset.
CLAUDE_CODE_OTEL_CONTENT_MAX_LENGTHContent attribute truncation length (60 KB default).

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 every logError(err) call. The line carries an axios-aware prefix when the error came from o8.isAxiosError(H): [url=...,status=...,body=...] error.stack. That prefix is what makes 4xx/5xx responses self-diagnostic when a user shares an errors.jsonl slice.
  • getMCPLogsPath(serverName) = path.join(claudeDir.mcpLogs(serverName), DATE + ".jsonl") — fanout target for logMCPError(server, err) and logMCPDebug(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.

Created and maintained by Yingting Huang.