
Ship Agent Telemetry in Three Env Vars: Claude Agent SDK's Built-in OpenTelemetry
Chris Harper
3 min read
Jun 29, 2026 · 20:05 UTC
TL;DR: Three env vars activate the Agent SDK's built-in OpenTelemetry export — every tool call, model request, and token cost becomes a span in Grafana, Honeycomb, Datadog, or Jaeger without an extra library.
Every production service needs metrics, traces, and logs. If you're deploying agents via the Claude Agent SDK's query() API, you have all three — the CLI backing each call has OpenTelemetry instrumentation built in. No extra library; just env vars.
Enable telemetry
CLAUDE_CODE_ENABLE_TELEMETRY=1
CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1 # required for span traces
OTEL_TRACES_EXPORTER=otlp
OTEL_METRICS_EXPORTER=otlp
OTEL_LOGS_EXPORTER=otlp
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_ENDPOINT=http://your-collector:4318
Set these in your shell, Dockerfile, or Kubernetes manifest and every query() call exports automatically. Or inject them per-call when different agents in the same process need different settings:
from claude_agent_sdk import query, ClaudeAgentOptions
options = ClaudeAgentOptions(env={
"CLAUDE_CODE_ENABLE_TELEMETRY": "1",
"CLAUDE_CODE_ENHANCED_TELEMETRY_BETA": "1",
"OTEL_TRACES_EXPORTER": "otlp",
"OTEL_METRICS_EXPORTER": "otlp",
"OTEL_LOGS_EXPORTER": "otlp",
"OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf",
"OTEL_EXPORTER_OTLP_ENDPOINT": "http://your-collector:4318",
})
async for message in query("Summarize the diff in git HEAD", options=options):
print(message)
What you get
| Signal | Contents |
|---|---|
| Metrics | Token counters, cost, session counts, lines-of-code, tool decisions |
| Log events | Structured records per prompt, API request, API error, tool result |
| Traces | claude_code.interaction → child spans for each llm_request and tool |
Traces give the most detail: one turn produces a parent claude_code.interaction span with child spans for every model call and tool invocation. When an agent spawns a subagent via the Task tool, the subagent's spans nest under the parent's claude_code.tool span — the full delegation chain appears as one connected tree.
Linked to your application's traces
The SDK automatically propagates W3C trace context (TRACEPARENT) into the CLI subprocess. Call query() while an active OpenTelemetry span exists in your application and the agent's spans appear as children of your span — one connected trace, not two disconnected roots. This is the key advantage over external plugins: the agent run shows up inside your API request's trace, not as a separate stream.
For short-lived processes
By default, metrics export every 60 seconds. Shorten the interval for batch jobs or CI steps so data isn't lost before the process exits:
OTEL_METRIC_EXPORT_INTERVAL=1000 # milliseconds
OTEL_LOGS_EXPORT_INTERVAL=1000
OTEL_TRACES_EXPORT_INTERVAL=1000
Content your agent reads and writes is not recorded by default — only structure (durations, model names, token counts). Set OTEL_LOG_USER_PROMPTS=1 or OTEL_LOG_TOOL_DETAILS=1 to opt in if your pipeline is approved to store that data.
Sources: Observability with OpenTelemetry — Claude Agent SDK Docs | Monitoring reference | Agent SDK overview