Skip to main content
When you run agents in production, you need visibility into what they did:
  • which tools they called
  • how long each model request took
  • how many tokens were spent
  • where failures occurred
The Agent SDK can export this data as OpenTelemetry traces, metrics, and log events to any backend that accepts the OpenTelemetry Protocol (OTLP), such as Honeycomb, Datadog, Grafana, Langfuse, or a self-hosted collector. This guide explains how the SDK emits telemetry, how to configure the export, and how to tag and filter the data once it reaches your backend. To read token usage and cost directly from the SDK response stream instead of exporting to a backend, see Track cost and usage.

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) or options.env (TypeScript). Use this when different agents in the same process need different telemetry settings. In Python, env is merged on top of the inherited environment. In TypeScript, env replaces the inherited environment entirely, so include ...process.env in the object you pass.
The CLI exports three independent OpenTelemetry signals. Each has its own enable switch and its own exporter, so you can turn on only the ones you need. For the complete list of metric names, event names, and attributes, see the Claude Code Monitoring reference. The Agent SDK emits the same data because it runs the same CLI. Span names are listed in Read agent traces below.

Enable telemetry export

Telemetry is off until you set CLAUDE_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:
Because the child process inherits your application’s environment by default, you can achieve the same result by exporting these variables in a Dockerfile, Kubernetes manifest, or shell profile and omitting options.env entirely. To confirm that export is working, check your collector’s logs for incoming spans, metrics, and log events after the task completes. The CLI fails silently on export errors by default: if the endpoint is unreachable or rejects the data, the agent still runs normally and the CLI drops the telemetry without surfacing an error in your application. To surface exporter errors, set CLAUDE_CODE_OTEL_DIAG_STDERR=1 alongside the exporter variables and read the diagnostics through the SDK’s stderr callback (Python) or stderr option (TypeScript). Requires Claude Code v2.1.179 or later.
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. On a clean process exit it attempts to flush pending data, but the flush is bounded by a short timeout, so spans can still be dropped if the collector is slow to respond. If your process is killed before the CLI shuts down, anything still in the batch buffer is lost. Lowering the export intervals reduces both windows. 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: