Skip to content

0013. OpenTelemetry for Claude Code

Status

Accepted

Context

The dev-monitor dashboard (monitor/main.go) reads Claude Code JSONL logs to surface token usage and cost estimates. Users working with multiple projects want richer observability: distributed traces spanning agent loops and tool calls, aggregated token counters across sessions, and structured log events that can feed a SIEM.

Two approaches were considered:

  1. Add Go OpenTelemetry SDK to monitor/main.go — instrument the Go binary to re-emit parsed JSONL data as OTLP spans and metrics to an external collector.
  2. Use Claude Code CLI's built-in OTEL instrumentation — configure the existing instrumentation via environment variables; no code changes.

Decision

Use the Claude Code CLI's built-in OpenTelemetry instrumentation (option 2). No changes to monitor/main.go, go.mod, or go.sum.

The Claude Code CLI already emits three independent OTEL signals when enabled:

Signal Enable env var Content
Metrics OTEL_METRICS_EXPORTER=otlp Token counters, cost, sessions, tool decisions
Log events OTEL_LOGS_EXPORTER=otlp Prompts, API requests, API errors, tool results
Traces (beta) OTEL_TRACES_EXPORTER=otlp + CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1 Spans per interaction, model request, tool call

The master enable switch is CLAUDE_CODE_ENABLE_TELEMETRY=1. All variables are inherited by the CLI child process — no wrapping or SDK integration is required.


Configuration

Set in the host shell, devcontainer.json remoteEnv, or a container orchestrator:

CLAUDE_CODE_ENABLE_TELEMETRY=1
CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1    # traces (beta)
OTEL_TRACES_EXPORTER=otlp
OTEL_METRICS_EXPORTER=otlp
OTEL_LOGS_EXPORTER=otlp
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_ENDPOINT=http://collector.example.com:4318
OTEL_EXPORTER_OTLP_HEADERS=Authorization=Bearer your-token
OTEL_SERVICE_NAME=claude-code             # override default

Compatible backends: Honeycomb, Datadog, Grafana, Langfuse, Jaeger, or any OTLP-capable collector. Add the collector hostname to proxy.yaml and egress-allowlist.txt.

Sensitive data opt-ins (off by default)

Variable Adds to telemetry
OTEL_LOG_USER_PROMPTS=1 Prompt text on interaction spans
OTEL_LOG_TOOL_DETAILS=1 Tool input arguments (file paths, commands)
OTEL_LOG_TOOL_CONTENT=1 Full tool input/output bodies (requires traces)

Leave these unset unless your observability pipeline is approved to handle that data.


Consequences

Accepted: - Zero code changes to the monitor binary. - No new Go dependencies — go.mod and go.sum unchanged. - Immediate: any project can opt in by setting env vars, no rebuild required. - Works with any OTLP-compatible backend. - Traces are beta; span names and attributes may change between Claude Code releases.

Rejected (option 1 rationale): - Adding the Go OTEL SDK would duplicate instrumentation the CLI already provides. - It would add external dependencies to the otherwise stdlib-only monitor binary. - It would only see parsed JSONL data, not live spans with accurate latencies.


References

  • Claude Code observability docs: https://docs.claude.com/en/docs/claude-code/monitoring-usage
  • OTEL env var reference: https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/
  • ADR 0009: proxy–mitm–monitor observability stack