Skip to content

[BUG] OTEL log records (api_request_body, user_prompt, tool_result) emitted without trace_id/span_id, breaking log↔trace correlation #55269

@andasv

Description

@andasv

[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

  1. Enable telemetry + body capture as above; point OTLP at any collector.
  2. Run any Claude Code prompt that triggers an LLM call.
  3. 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
rows  with_trace
231   0

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:corebugSomething isn't workinghas reproHas detailed reproduction steps

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions