- which tools they called
- how long each model request took
- how many tokens were spent
- where failures occurred
How telemetry flows from the SDK
The Agent SDK runs the Claude Code CLI as a child process and communicates with it over a local pipe. The CLI has OpenTelemetry instrumentation built in: it records spans around each model request and tool execution, emits metrics for token and cost counters, and emits structured log events for prompts and tool results. The SDK does not produce telemetry of its own. Instead, it passes configuration through to the CLI process, and the CLI exports directly to your collector. Configuration is passed as environment variables. By default, the child process inherits your application’s environment, so you can configure telemetry in either of two places:- Process environment: set the variables in your shell, container, or orchestrator before your application starts. Every
query()call picks them up automatically with no code change. This is the recommended approach for production deployments. - Per-call options: set the variables in
ClaudeAgentOptions.env(Python) oroptions.env(TypeScript). Use this when different agents in the same process need different telemetry settings. In Python,envis merged on top of the inherited environment. In TypeScript,envreplaces the inherited environment entirely, so include...process.envin the object you pass.
| 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 |
Enable telemetry export
Telemetry is off until you setCLAUDE_CODE_ENABLE_TELEMETRY=1 and choose at least one exporter. The most common configuration sends all three signals over OTLP HTTP to a collector.
The following example sets the variables in a dictionary and passes them through options.env. The agent runs a single task, and the CLI exports spans, metrics, and events to the collector at collector.example.com while the loop consumes the response stream:
options.env entirely.
The
console exporter writes telemetry to standard output, which the SDK uses
as its message channel. Do not set console as an exporter value when running
through the SDK. To inspect telemetry locally, point
OTEL_EXPORTER_OTLP_ENDPOINT at a local collector or an all-in-one Jaeger
container instead.Flush telemetry from short-lived calls
The CLI batches telemetry and exports on an interval. It flushes any pending data when the process exits cleanly, so aquery() call that completes normally does not lose spans. However, if your process is killed before the CLI shuts down, anything still in the batch buffer is lost. Lowering the export intervals reduces that window.
By default, metrics export every 60 seconds and traces and logs export every 5 seconds. The following example shortens all three intervals so that data reaches the collector while a short task is still running:
Read agent traces
Traces give you the most detailed view of an agent run. WithCLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1 set, each step of the agent loop becomes a span you can inspect in your tracing backend:
claude_code.interaction: wraps a single turn of the agent loop, from receiving a prompt to producing a response.claude_code.llm_request: wraps each call to the Claude API, with model name, latency, and token counts as attributes.claude_code.tool: wraps each tool invocation, with child spans for the permission wait (claude_code.tool.blocked_on_user) and the execution itself (claude_code.tool.execution).claude_code.hook: wraps each hook execution.
session.id attribute. When you make several query() calls against the same session, filter on session.id in your backend to see them as one timeline.
Tracing is in beta. Span names and attributes may change between releases. See
Traces (beta) in the Monitoring reference
for the trace exporter configuration variables.
Tag telemetry from your agent
By default, the CLI reportsservice.name as claude-code. If you run several agents, or run the SDK alongside other services that export to the same collector, override the service name and add resource attributes so you can filter by agent in your backend.
The following example renames the service and attaches deployment metadata. These values are applied as OpenTelemetry resource attributes on every span, metric, and event the agent emits:
Control sensitive data in exports
Telemetry is structural by default. Token counts, durations, model names, and tool names are always recorded, but the content your agent reads and writes is not. Three opt-in variables add content to the exported data:| Variable | Adds |
|---|---|
OTEL_LOG_USER_PROMPTS=1 | Prompt text on claude_code.user_prompt events and on the claude_code.interaction span |
OTEL_LOG_TOOL_DETAILS=1 | Tool input arguments (file paths, shell commands, search patterns) on claude_code.tool_result events |
OTEL_LOG_TOOL_CONTENT=1 | Full tool input and output bodies as span events on claude_code.tool, truncated at 60 KB. Requires tracing to be enabled |
Related documentation
These guides cover adjacent topics for monitoring and deploying agents:- Track cost and usage: read token and cost data from the message stream without an external backend.
- Hosting the Agent SDK: deploy agents in containers where you can set OpenTelemetry variables at the environment level.
- Monitoring: the complete reference for every environment variable, metric, and event the CLI emits.