[BUG] OTEL log records (api_request_body, user_prompt, tool_result) emitted without trace_id/span_id, breaking log↔trace correlation
Summary
When OTEL_LOG_RAW_API_BODIES=1 (and the related OTEL_LOG_USER_PROMPTS / OTEL_LOG_TOOL_DETAILS / OTEL_LOG_TOOL_CONTENT flags) are enabled alongside CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1, Claude Code correctly emits log records carrying the prompt/response/tool bodies (e.g. claude_code.api_request_body). However the log records arrive at the collector with empty trace_id and span_id fields, so observability backends cannot correlate the body with the parent claude_code.llm_request / claude_code.tool span that produced it.
This makes the captured content effectively unreachable from the trace UI — clicking a span never surfaces the body, and the "Correlated Logs" / "Related Signals" panels show nothing.
Environment
- Claude Code:
2.1.126
- OS: macOS 25.1.0 (Darwin)
- Collector:
otel/opentelemetry-collector-contrib:0.115.1
- Backends tested: HyperDX (ClickStack), SigNoz, Tempo+Loki+Grafana, Jaeger
- Transport: OTLP gRPC →
localhost:4317 → fan-out to all backends
~/.claude/settings.json env block:
"CLAUDE_CODE_ENABLE_TELEMETRY": "1",
"CLAUDE_CODE_ENHANCED_TELEMETRY_BETA": "1",
"OTEL_TRACES_EXPORTER": "otlp",
"OTEL_LOGS_EXPORTER": "otlp",
"OTEL_METRICS_EXPORTER": "otlp",
"OTEL_LOG_USER_PROMPTS": "1",
"OTEL_LOG_TOOL_DETAILS": "1",
"OTEL_LOG_TOOL_CONTENT": "1",
"OTEL_LOG_RAW_API_BODIES": "1"
Reproduction
- Enable telemetry + body capture as above; point OTLP at any collector.
- Run any Claude Code prompt that triggers an LLM call.
- Inspect the resulting
otel_logs and otel_traces tables (or equivalent in your backend).
Observed
ClickHouse query against HyperDX-bundled storage covering a single 5-minute window with active Claude Code usage:
SELECT count(*) AS rows,
countIf(TraceId != '') AS with_trace
FROM default.otel_logs
WHERE Timestamp >= now() - INTERVAL 5 MINUTE
A sample log row contains the full request body:
Body: claude_code.api_request_body
LogAttributes: {
'app.version':'2.1.126',
'app.session.id':'…',
'body':'{"model":"claude-opus-4-7","messages":[...],"system":[...],"tools":[...]}'
}
TraceId: (empty)
SpanId: (empty)
The matching span in otel_traces has the expected TraceId populated, e.g. a74cd28d63d1d7ed73fc9c88fa0d68d6, span name claude_code.llm_request. Both records are emitted within ~1 second of each other; the join key is simply not set on the log side.
Expected
Per OTel spec Trace Context in Non-OTLP Log Formats and the LogRecord data model, log records emitted from inside an active span SHOULD carry the active trace_id and span_id. With those populated, HyperDX's "Correlated Log Source", SigNoz's "Related Signals → Logs", and Grafana's Tempo↔Loki link would all light up automatically.
Why this matters
OTEL_LOG_RAW_API_BODIES (added in 2.1.98 per the changelog) is documented as a debugging aid. Today its output is effectively write-only — body events land in storage but cannot be navigated to from the trace they belong to, defeating the debugging use case. Customers building on Claude Code observability have to fall back to time-window + session-id heuristics in their backend's log search.
Suggested fix
Inside the body/event emission path, pass the currently active Span's SpanContext into the LogRecord's traceId / spanId / traceFlags fields. In OpenTelemetry JS this is one line via trace.getActiveSpan()?.spanContext() at the time of emission, or by using the SDK's LoggerProvider with a Logger that auto-attaches active span context.
References
[BUG] OTEL log records (api_request_body, user_prompt, tool_result) emitted without
trace_id/span_id, breaking log↔trace correlationSummary
When
OTEL_LOG_RAW_API_BODIES=1(and the relatedOTEL_LOG_USER_PROMPTS/OTEL_LOG_TOOL_DETAILS/OTEL_LOG_TOOL_CONTENTflags) are enabled alongsideCLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1, Claude Code correctly emits log records carrying the prompt/response/tool bodies (e.g.claude_code.api_request_body). However the log records arrive at the collector with emptytrace_idandspan_idfields, so observability backends cannot correlate the body with the parentclaude_code.llm_request/claude_code.toolspan that produced it.This makes the captured content effectively unreachable from the trace UI — clicking a span never surfaces the body, and the "Correlated Logs" / "Related Signals" panels show nothing.
Environment
2.1.126otel/opentelemetry-collector-contrib:0.115.1localhost:4317→ fan-out to all backends~/.claude/settings.jsonenv block:Reproduction
otel_logsandotel_tracestables (or equivalent in your backend).Observed
ClickHouse query against HyperDX-bundled storage covering a single 5-minute window with active Claude Code usage:
A sample log row contains the full request body:
The matching span in
otel_traceshas the expectedTraceIdpopulated, e.g.a74cd28d63d1d7ed73fc9c88fa0d68d6, span nameclaude_code.llm_request. Both records are emitted within ~1 second of each other; the join key is simply not set on the log side.Expected
Per OTel spec Trace Context in Non-OTLP Log Formats and the
LogRecorddata model, log records emitted from inside an active span SHOULD carry the activetrace_idandspan_id. With those populated, HyperDX's "Correlated Log Source", SigNoz's "Related Signals → Logs", and Grafana's Tempo↔Loki link would all light up automatically.Why this matters
OTEL_LOG_RAW_API_BODIES(added in 2.1.98 per the changelog) is documented as a debugging aid. Today its output is effectively write-only — body events land in storage but cannot be navigated to from the trace they belong to, defeating the debugging use case. Customers building on Claude Code observability have to fall back to time-window + session-id heuristics in their backend's log search.Suggested fix
Inside the body/event emission path, pass the currently active
Span'sSpanContextinto theLogRecord'straceId/spanId/traceFlagsfields. In OpenTelemetry JS this is one line viatrace.getActiveSpan()?.spanContext()at the time of emission, or by using the SDK'sLoggerProviderwith aLoggerthat auto-attaches active span context.References
OTEL_LOG_RAW_API_BODIEStool_use_id,stop_reason,user_system_promptto events