What you will be able to do
- Name the three OpenTelemetry signals that Claude Code and the Agent SDK emit, and say how to enable each one
- Configure exporters, the OTLP endpoint and export intervals, through the process environment or per-call options
- Use claude_code.llm_request span attributes to diagnose latency, token use, caching and failures
- Choose a backend type that fits each signal
Key concept
OpenTelemetry export — Claude Code and the Agent SDK can export three opt-in telemetry signals to a backend you choose: metrics, structured log events and trace spans. You turn them on and route them with environment variables. You don't need to add instrumentation code.
1.Three signals, three questions
An agent in production fails in ways one log line can't explain. It might call the wrong tool, a model request might stall, or it might spend far more tokens than expected. The Agent SDK observability guide lists what telemetry lets you see: which tools the agent called, how long each model request took, how many tokens were spent, and where failures occurred.
Claude Code and the Agent SDK answer these questions with three separate OpenTelemetry signals. Each has a different shape, and each is switched on separately. Metrics are aggregate counters, which suit trends and alerts. Log events are one structured record per occurrence, which suit investigating a single request. Traces are timed spans nested in parent/child order, which show where time went within one turn.
| Signal | What it contains | Enable with |
|---|---|---|
| Metrics | Counters for tokens, cost, sessions, lines of code, and tool decisions | OTEL_METRICS_EXPORTER |
| Log events | Structured records for each prompt, API request, API error, and tool result | OTEL_LOGS_EXPORTER |
| Traces | Spans for each interaction, model request, tool call, and hook (beta) | OTEL_TRACES_EXPORTER plus CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1 |
None of these signals is on by default. All three need CLAUDE_CODE_ENABLE_TELEMETRY=1, which the configuration reference marks as required. Traces are in beta, so they also need a second flag that metrics and log events don't.
Checkpoint 1 of 6· Check yourself
You set CLAUDE_CODE_ENABLE_TELEMETRY=1 and OTEL_TRACES_EXPORTER=otlp, but no spans reach your collector. Metrics and log events arrive normally. What is missing?
Traces are in beta. Besides a traces exporter, they need CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1. Metrics and log events don't.
“OTEL_TRACES_EXPORTER plus CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1”Source: code.claude.com
Checkpoint 2 of 6· Exam question
A platform engineer sets CLAUDE_CODE_ENABLE_TELEMETRY=1 on every CI runner but the team's OTLP collector never receives any spans, metrics, or logs from Claude Code jobs. No other telemetry variables were configured. What is the most likely cause of the missing data?
Correct answer: B — Telemetry is enabled but no exporter was selected, so OTEL_METRICS_EXPORTER and OTEL_LOGS_EXPORTER default to producing no export at all
- A. Incorrect. The enhanced telemetry beta flag is only required to unlock distributed tracing spans; metrics and log events do not depend on it, and it has no bearing on whether an exporter is configured.
- B. Correct. Enabling telemetry alone does not choose a destination. Each signal has its own exporter switch (OTEL_METRICS_EXPORTER, OTEL_LOGS_EXPORTER, OTEL_TRACES_EXPORTER), and leaving them unset means nothing is exported even though telemetry generation is on.
- C. Incorrect. The console exporter is one optional choice among several (otlp, prometheus, console, none); it is not a prerequisite the CLI requires before other exporters will function.
- D. Incorrect. OTEL_EXPORTER_OTLP_HEADERS carries authentication for the collector endpoint, not an Anthropic Admin API key, and its absence would cause auth failures at the collector, not a total absence of any emitted telemetry.
2.Wiring telemetry to a collector
After you enable telemetry, you pick an exporter for each signal. The choices include otlp, console and none, and metrics can also use prometheus. With OTLP you also give a collector endpoint, a protocol and any authentication headers. Set the protocol explicitly. The reference says Claude Code has no default protocol, so you must set this or the signal-specific protocol variable for each otlp exporter you enable.
The Agent SDK gives you two places to set these variables. The guide recommends the process environment for production: set the variables in your shell, container or orchestrator, and every query() call picks them up. Per-call options (ClaudeAgentOptions.env in Python, options.env in TypeScript) are for cases where different agents in one process need different settings.
OTEL_ENV = {
"CLAUDE_CODE_ENABLE_TELEMETRY": "1",
# Required for traces, which are in beta. Metrics and log events do not need this.
"CLAUDE_CODE_ENHANCED_TELEMETRY_BETA": "1",
# Choose an exporter per signal. Use otlp for the SDK; see the Note below.
"OTEL_TRACES_EXPORTER": "otlp",
"OTEL_METRICS_EXPORTER": "otlp",
"OTEL_LOGS_EXPORTER": "otlp",
# Standard OTLP transport configuration.
"OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf",
"OTEL_EXPORTER_OTLP_ENDPOINT": "http://collector.example.com:4318",
"OTEL_EXPORTER_OTLP_HEADERS": "Authorization=Bearer your-token",
}The two SDKs treat per-call env differently. In Python, env is merged on top of the inherited environment. In TypeScript, env replaces the inherited environment entirely, so you have to spread process.env into it. Otherwise PATH, ANTHROPIC_API_KEY and other variables are lost.
Export intervals control how quickly data appears. Metrics export every 60000 ms by default, and logs every 5000 ms. The setup guide suggests shortening the intervals while debugging and resetting them before production.
Checkpoint 3 of 6· Put it in order
Put the Claude Code telemetry setup steps in the order the monitoring guide lists them
- 1.Configure the OTLP protocol and collector endpoint
- 2.Run Claude Code
- 3.Set authentication headers if the collector requires them
- 4.Choose the metrics and logs exporters
- 5.Enable telemetry with CLAUDE_CODE_ENABLE_TELEMETRY=1
The guide numbers the steps: enable telemetry, choose exporters, configure the OTLP endpoint, set authentication, optionally shorten intervals for debugging, then run Claude Code.
“# 2. Choose exporters (both are optional - configure only what you need)”Source: code.claude.com
3.Reading a trace: where the time and tokens went
Traces divide one agent turn into nested spans. The root is claude_code.interaction, which covers one turn from prompt to response. Under it are a claude_code.llm_request span for each call to the Claude API and a claude_code.tool span for each tool invocation. Each tool span splits into the permission wait and the actual execution. So a slow turn can be traced to the model, to a tool, or to a person who hasn't approved a tool yet. Subagents launched with the Agent tool show up as their own llm_request and tool spans inside the parent tool span. The claude_code.hook span appears only with detailed beta tracing, which also changes where logs and traces are sent.
Most of the performance data is on the llm_request span.
| Attribute | Meaning | Diagnoses |
|---|---|---|
| duration_ms | Wall-clock duration including retries | Slow model calls |
| ttft_ms | Time to first token in milliseconds | Perceived responsiveness |
| input_tokens / output_tokens | Token counts from the API usage block | Cost and context growth |
| cache_read_tokens / cache_creation_tokens | Tokens read from / written to prompt cache | Whether caching is working |
| attempt | Total attempts made for this request | Retries hiding behind latency |
| error_class | Short error class token, such as api_timeout or server_overload | Failure categories |
| stop_reason | end_turn, tool_use, max_tokens, stop_sequence, pause_turn, or refusal | Truncated or refused output |
| agent_id | Subagent or teammate that issued the request; absent on the main session | Which agent spent the tokens |
Because duration_ms includes retries, a high value doesn't by itself mean the model was slow. Check attempt and error_class before you blame the model. A stop_reason of max_tokens explains a truncated answer that the latency numbers would not reveal.
Checkpoint 4 of 6· Match them up
Match each llm_request attribute to what it tells you
Tap a term, then the definition that fits it.
Each definition comes from the span attribute table for claude_code.llm_request.
“Short error class token when the request failed, such as api_timeout or server_overload.”Source: code.claude.com
Checkpoint 5 of 6· Exam question
A metrics backend's bill spikes because every exported time series carries a unique session.id, fragmenting cardinality across thousands of short-lived CLI sessions. The team still needs cost and token totals broken down by model. Which change addresses the cardinality problem without losing the model breakdown?
Correct answer: A — Set OTEL_METRICS_INCLUDE_SESSION_ID=false so session.id is dropped from exported metrics while other attributes like model remain
- A. Correct. This variable specifically controls whether session.id is included as a metric attribute; turning it off removes the high-cardinality dimension while attributes like model, which are needed for the breakdown, are unaffected.
- B. Incorrect. This abandons real-time CLI metrics entirely and substitutes a different, higher-latency data source; it also does not solve the underlying cardinality configuration issue for teams that still want live metrics.
- C. Incorrect. A longer export interval changes how often the same set of time series is flushed, not how many distinct series are created; cardinality is driven by attribute values, not export frequency.
- D. Incorrect. The Usage and Cost Admin API is a separate, unrelated data source for billing reports; its bucket_width parameter has no effect on the CLI's own OpenTelemetry metrics pipeline or its cardinality.
4.Matching each signal to a backend
The signals have different shapes, so they suit different storage. The monitoring guide lists which backend types fit each signal. Many teams send all three to one full-featured observability platform so they can correlate them, for example by jumping from a cost spike on a metric chart to the trace that caused it.
| Signal | Backend type | Strength |
|---|---|---|
| Metrics | Time series databases | Rate calculations, aggregated metrics |
| Metrics | Columnar stores | Complex queries, unique user analysis |
| Log events | Log aggregation systems | Full-text search, log analysis |
| Log events | Columnar stores | Structured event analysis |
| Traces | Distributed tracing systems | Span visualization, request waterfalls, latency analysis |
| All signals | Full-featured observability platforms | Querying, visualization, alerting, and correlation across signals |
Checkpoint 6 of 6· Check yourself
You want a request waterfall that shows a slow tool call nested inside an agent turn. Which backend type is listed for this?
Request waterfalls and latency analysis come from span visualization, which is the strength listed for distributed tracing systems.
“Distributed tracing systems: Span visualization, request waterfalls, latency analysis”Source: code.claude.com
Sources2
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.Setting OTEL_TRACES_EXPORTER=otlp is enough to start exporting spans.Why is that wrong?
Traces are in beta. They also need CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1 on top of CLAUDE_CODE_ENABLE_TELEMETRY=1.
Covered in Three signals, three questions
2.Per-call env in the TypeScript Agent SDK is merged with the inherited environment, as it is in Python.Why is that wrong?
In TypeScript, env replaces the inherited environment. Spread process.env into it or you lose PATH and ANTHROPIC_API_KEY.
Covered in Wiring telemetry to a collector
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.
“Counters for tokens, cost, sessions, lines of code, and tool decisions”
↩︎ Three signals, three questions“This is the recommended approach for production deployments.”
↩︎ Wiring telemetry to a collector“wraps each call to the Claude API, with model name, latency, and token counts as attributes.”
↩︎ Reading a trace: where the time and tokens went“Export traces, metrics, and events from the Agent SDK to your observability backend using OpenTelemetry.”
↩︎ Key concept“OTEL_TRACES_EXPORTER plus CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1”
↩︎ Exam trap 1“In TypeScript, env replaces the inherited environment entirely, so include ...process.env in the object you pass.”
↩︎ Exam trap 2“OTEL_TRACES_EXPORTER plus CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1”
↩︎ Checkpoint“with child spans for the permission wait (claude_code.tool.blocked_on_user) and the execution itself (claude_code.tool.execution).”
↩︎ Prediction - 2.https://code.claude.com/docs/en/monitoring-usageOfficial docs
“Enables telemetry collection (required)”
↩︎ Three signals, three questions“Claude Code has no default protocol, so set this or the signal-specific protocol variable for each otlp exporter you enable”
↩︎ Wiring telemetry to a collector“For debugging: reduce export intervals, and reset them for production use”
↩︎ Wiring telemetry to a collector“Wall-clock duration including retries”
↩︎ Reading a trace: where the time and tokens went“Full-featured observability platforms: Correlation between metrics and events”
↩︎ Matching each signal to a backend“# 2. Choose exporters (both are optional - configure only what you need)”
↩︎ Checkpoint“Short error class token when the request failed, such as api_timeout or server_overload.”
↩︎ Checkpoint“Distributed tracing systems: Span visualization, request waterfalls, latency analysis”
↩︎ Checkpoint