Skip to content

Fix trace_id mismatch for logs emitted before Sentry::Rails::CaptureExceptions runs - #3016

Open
runephilosof-abtion wants to merge 1 commit into
getsentry:masterfrom
runephilosof-abtion:fix/early-request-log-trace-context
Open

Fix trace_id mismatch for logs emitted before Sentry::Rails::CaptureExceptions runs#3016
runephilosof-abtion wants to merge 1 commit into
getsentry:masterfrom
runephilosof-abtion:fix/early-request-log-trace-context

Conversation

@runephilosof-abtion

@runephilosof-abtion runephilosof-abtion commented Jul 7, 2026

Copy link
Copy Markdown

Fixes #3015

Problem

Sentry::Rails::CaptureExceptions is deliberately positioned right after ActionDispatch::ShowExceptions (to skip transaction creation for static asset requests). That means anything logged before it runs - most notably Rails' own Rails::Rack::Logger "Started ..." line - gets a trace_id/span_id unrelated to the rest of the request, because no trace context has been established yet.

There's also a second, compounding issue: even once CaptureExceptions does run, if the request isn't continuing an incoming distributed trace, Hub#start_transaction's fallback (Transaction.new(**options)) generates its own independent trace_id, ignoring whatever was already on the scope's propagation context.

Fix

  • New Sentry::Rails::CaptureContext middleware, unshifted to the very front of the Rails middleware stack. It only establishes the propagation context (from incoming sentry-trace/baggage headers, if present) as early as possible - it doesn't start a transaction or capture exceptions, so CaptureExceptions's existing position/behavior is untouched.
  • Sentry::PropagationContext::ESTABLISHED_ENV_KEY - a new env flag signaling that trace context was already established for this exact request.
  • Sentry::Rack::CaptureExceptions reads and deletes this flag from env atomically, as the very first thing it does (before anything else that could raise), so it can never leak into later, unrelated reuses of the same env (e.g. Action Cable holding onto the handshake env for a connection's lifetime). It skips re-cloning the hub when the flag was present, and passes the result explicitly as an established: argument to Hub#continue_trace and Hub#start_transaction.
  • Hub#continue_trace skips regenerating the propagation context when established: true, instead of clobbering what CaptureContext set up.
  • Hub#start_transaction, when not continuing a distributed trace and established: true, builds the new transaction with the already-established trace_id/sample_rand instead of generating an unrelated one.

The start_transaction change is intentionally scoped to only apply when the established: flag is passed (rather than always reusing whatever's on the scope), to avoid incorrectly linking together unrelated transactions that happen to share a thread (e.g. separate background jobs) - each of those still gets its own independent trace_id as before. This was verified against a regression in sentry-rails' ActiveJob distributed-tracing specs during development.

Testing

  • Added sentry-rails/spec/sentry/rails/capture_context_spec.rb, including an integration-style test that reproduces the original bug (probe middleware inserted before/after CaptureExceptions) both with and without tracing enabled.
  • Added unit coverage in sentry-ruby's hub/capture_exceptions specs for the new established-context guard behavior.
  • Updated sentry-rails' existing middleware-ordering spec to assert CaptureContext is the first middleware.
  • Ran the full sentry-ruby and sentry-rails suites locally; no regressions (Redis-dependent specs fail identically with/without this change due to no local Redis server, pre-existing/environment-only).

Comment thread sentry-rails/lib/sentry/rails/capture_context.rb
@runephilosof-abtion
runephilosof-abtion force-pushed the fix/early-request-log-trace-context branch from 096916b to 080acef Compare July 8, 2026 19:08
@runephilosof-abtion

Copy link
Copy Markdown
Author

Should I cut down on the inline code comments?

@solnic

solnic commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Should I cut down on the inline code comments?

@runephilosof yes please 🙏🏻 😄 public APIs should only get proper YARD coverage, any other inline comment is not needed except situations where some bit is really tricky to understand by just looking at the code.

@runephilosof-abtion
runephilosof-abtion force-pushed the fix/early-request-log-trace-context branch from 080acef to f855b07 Compare August 6, 2026 06:34
@runephilosof-abtion

Copy link
Copy Markdown
Author

I have changed how the already established info is passed around, to make it less confusing and force pushed.

Comment thread sentry-ruby/lib/sentry/rack/capture_exceptions.rb

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit f855b07. Configure here.

Comment thread sentry-ruby/lib/sentry/rack/capture_exceptions.rb
Rails::Rack::Logger's "Started ..." line (and anything logged before
CaptureExceptions runs) got an unrelated trace_id, since
CaptureExceptions runs after ActionDispatch::ShowExceptions.

Add Sentry::Rails::CaptureContext, a minimal middleware unshifted to
the front of the stack that establishes the propagation context early.
CaptureExceptions now consumes and reuses it instead of regenerating
a new trace_id/span_id.

Co-Authored-By: GitHub Copilot <noreply@example.com>
@runephilosof-abtion
runephilosof-abtion force-pushed the fix/early-request-log-trace-context branch from f855b07 to 99526a9 Compare August 6, 2026 08:35
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.

Rails::Rack::Logger's "Started ..." log line (and anything else logged before Sentry::Rails::CaptureExceptions) gets an unrelated trace_id/span_id

3 participants