Skip to content

fix: correlate log records with their span context - #129

Open
alexhawat wants to merge 1 commit into
DEVtheOPS:mainfrom
alexhawat:fix/correlate-logs-with-span-context
Open

alexhawat wants to merge 1 commit into
DEVtheOPS:mainfrom
alexhawat:fix/correlate-logs-with-span-context

Conversation

@alexhawat

Copy link
Copy Markdown

Description

OTel log records emitted by the plugin carried no context, so Logger.emit fell back to context.active() — which is always empty under opencode's event dispatch. As a result every user_prompt, tool_result, api_request/api_error, tool_decision, and session lifecycle log exported with no trace/span id and rendered as its own root trace in backends such as Logfire, instead of nesting under the session, run, tool, or LLM span that produced it.

Observed in a Logfire project (all parent_span_id = null):

span_name     kind  n
tool_result   log   2633
user_prompt   log   91

This resolves a context for every emitted log record:

  • an explicit record.context always wins;
  • otherwise the record is parented to the session/run context named by its session.id attribute (resolveLogContext);
  • tool_result nests under its tool span, and api_request/api_error under their LLM span, by attaching that span's context.

The SDK honours LogRecord.context (const currentContext = logRecord.context || context.active() in @opentelemetry/sdk-logs), so the trace/span id now reaches the exporter.

Type of change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)
  • Chore (dependency updates, etc.)

Checklist

  • I have read the CONTRIBUTING.md document
  • My code follows the style guidelines of this project
  • bun run lint passes with no errors
  • bun run check:jsdoc-coverage passes with no errors
  • bun run typecheck passes with no errors
  • bun test passes with no errors
  • I have added tests that prove my fix is effective or that my feature works
  • I have updated the documentation accordingly (no doc change needed — comment-level only)
  • My commits follow the Conventional Commits specification

Additional context

Tests added cover contextForSpanContext/resolveLogContext (explicit context wins, session/run resolution, unknown session fallback) and assert that the emitted tool_result and api_request records carry the tool/LLM span id. Full suite: 334 pass, 0 fail.

Related: #107, #109 (both concern trace-context propagation; this fix only covers the plugin's own log records).

Log records emitted by the plugin carried no OTel context, so
`Logger.emit` fell back to `context.active()`, which is always empty
under opencode's event dispatch. Every `user_prompt`, `tool_result`,
`api_request`, `tool_decision`, and session lifecycle log therefore
exported without a trace/span id and appeared as its own root trace in
backends such as Logfire instead of nesting under the session, run,
tool, or LLM span that produced it.

Resolve a context for every log record: an explicit `record.context`
wins, otherwise the record is parented to the session/run context named
by its `session.id` attribute. `tool_result` and `api_request`/`api_error`
additionally nest under their tool/LLM span by attaching that span's
context.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant