Skip to content

feat(opentelemetry)!: Simplify propagation and remove custom OTel tracing utils - #22702

Open
mydea wants to merge 11 commits into
fn/streamline-acs-isolation-scopefrom
fn/propagator
Open

feat(opentelemetry)!: Simplify propagation and remove custom OTel tracing utils#22702
mydea wants to merge 11 commits into
fn/streamline-acs-isolation-scopefrom
fn/propagator

Conversation

@mydea

@mydea mydea commented Jul 27, 2026

Copy link
Copy Markdown
Member

Simplifies the OpenTelemetry SentryPropagator down to a thin TextMapPropagator that delegates all trace-data serialization to core's getTraceData(), and removes the custom OTel-specific tracing/propagation overrides (continueTrace, startNewTrace, getTraceData, getTraceContextForScope) in favor of core's implementations. The net effect is that OTel-powered SDKs (Node, Next.js, SvelteKit, etc.) now share the same propagation and trace-continuation code paths as the rest of the SDK instead of maintaining a parallel OTel-only variant.

What changed

  • SentryPropagator no longer extends W3CBaggagePropagator. It now implements TextMapPropagator directly. inject() reads headers from getTraceData() and only operates on the active context — if called with a non-active context it warns and skips.
    • Injecting with a non-active context was supported before, but we intentionally drop it. The main remaining consumers of this propagator (Next.js and SvelteKit) don't need it, so rather than carry the complexity of handling arbitrary non-active contexts, we focus the propagator on the main use case and skip the edge cases that would drastically increase its complexity.
  • URL / tracePropagationTargets filtering and propagateTraceparent are no longer the propagator's concern — outgoing-request filtering already lives in the Node HTTP layer (inject-trace-propagation-headers.ts), so the duplicated logic, the _urlMatchesTargetsMap LRU cache, and the sentry.url trace-state (SENTRY_TRACE_STATE_URL) are removed.
  • Removed the OTel-specific getTraceData, continueTrace, and startNewTrace overrides from the async context strategy. Core's implementations are now used directly. To make this work, continueTrace/startNewTrace in core were reworked to route through withActiveSpan(null, …), and OTel's non-recording (TwP) spans fall back to the scope's propagation-context trace id.
  • Removed getTraceContextForScope (and NodeClient._getTraceInfoFromScope); scope→trace-context resolution now goes through the shared core path.
  • _startSpan now runs the callback with the started span set active on the unsuppressed context, fixing event trace-context attaching to a stale ancestor span across startNewTrace/continueTrace boundaries.

Aligning Node and Browser behavior

Previously, the OTel/Node path and the browser (core) path had subtly diverging propagation behavior because they ran through different implementations of getTraceData, continueTrace, and startNewTrace. The OTel variants derived trace data from the OTel context/span graph, while the browser used core's scope- and span-based logic — so edge cases (TwP sampling, DSC freezing, which trace id a fresh root span lands on) could resolve differently between the two.

By deleting the OTel-specific overrides and routing everything through core, Node and Browser now produce trace headers and continue/start traces via a single common code path. There is one source of truth for how a sentry-trace/baggage pair is derived from the current scope and span, which removes a class of Node-vs-Browser discrepancies and makes future changes apply uniformly to both.

Test adjustments

Because this aligns Node's propagation/trace-continuation behavior with the common core path, a number of Node integration tests had to be updated to match the new (now shared) behavior — primarily expectations around trace ids for parallel/root spans and outgoing-request propagation (parallel-root-spans, parallel-spans-in-scope, fetch-sampled-no-active-span, etc.). These changes reflect the corrected/unified behavior, not regressions.

Breaking changes

  • getTraceContextForScope is no longer exported from @sentry/opentelemetry.
  • The sentry.url trace-state and the getInjectionData / OTel-specific getTraceData internals of the propagator are removed.
  • The SentryPropagator no longer injects when called with a non-active context.

@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 29.82 kB -0.02% -3 B 🔽
@sentry/browser - with treeshaking flags 28.02 kB -0.01% -2 B 🔽
@sentry/browser (incl. Tracing) 47.07 kB -0.01% -4 B 🔽
@sentry/browser (incl. Tracing + Span Streaming) 47.08 kB -0.02% -7 B 🔽
@sentry/browser (incl. Tracing, Profiling) 51.81 kB -0.02% -10 B 🔽
@sentry/browser (incl. Tracing, Replay) 86.38 kB -0.02% -14 B 🔽
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 75.82 kB -0.02% -11 B 🔽
@sentry/browser (incl. Tracing, Replay with Canvas) 91.1 kB -0.02% -11 B 🔽
@sentry/browser (incl. Tracing, Replay, Feedback) 103.75 kB -0.01% -8 B 🔽
@sentry/browser (incl. Feedback) 47.13 kB -0.01% -1 B 🔽
@sentry/browser (incl. sendFeedback) 34.66 kB -0.01% -3 B 🔽
@sentry/browser (incl. FeedbackAsync) 39.76 kB -0.01% -2 B 🔽
@sentry/browser (incl. Metrics) 30.89 kB - -
@sentry/browser (incl. Logs) 31.12 kB -0.01% -1 B 🔽
@sentry/browser (incl. Metrics & Logs) 31.8 kB -0.02% -6 B 🔽
@sentry/react 31.6 kB -0.04% -11 B 🔽
@sentry/react (incl. Tracing) 49.31 kB -0.03% -13 B 🔽
@sentry/vue 34.74 kB -0.01% -2 B 🔽
@sentry/vue (incl. Tracing) 49.04 kB -0.04% -15 B 🔽
@sentry/svelte 29.85 kB -0.01% -2 B 🔽
CDN Bundle 31.88 kB +0.03% +8 B 🔺
CDN Bundle (incl. Tracing) 47.44 kB -0.03% -11 B 🔽
CDN Bundle (incl. Logs, Metrics) 33.42 kB -0.01% -2 B 🔽
CDN Bundle (incl. Tracing, Logs, Metrics) 48.82 kB -0.01% -4 B 🔽
CDN Bundle (incl. Replay, Logs, Metrics) 72.78 kB +0.01% +1 B 🔺
CDN Bundle (incl. Tracing, Replay) 85.07 kB -0.02% -10 B 🔽
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 86.37 kB -0.02% -11 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback) 90.84 kB -0.02% -12 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 92.15 kB -0.02% -10 B 🔽
CDN Bundle - uncompressed 95.04 kB -0.08% -68 B 🔽
CDN Bundle (incl. Tracing) - uncompressed 142.13 kB -0.15% -210 B 🔽
CDN Bundle (incl. Logs, Metrics) - uncompressed 99.75 kB -0.07% -68 B 🔽
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 146.11 kB -0.15% -210 B 🔽
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 224.51 kB -0.04% -68 B 🔽
CDN Bundle (incl. Tracing, Replay) - uncompressed 261.39 kB -0.09% -210 B 🔽
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 265.35 kB -0.08% -210 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 275.09 kB -0.08% -210 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 279.05 kB -0.08% -210 B 🔽
@sentry/nextjs (client) 51.91 kB -0.02% -6 B 🔽
@sentry/sveltekit (client) 47.5 kB -0.03% -12 B 🔽
@sentry/core/server 79.61 kB -0.02% -14 B 🔽
@sentry/core/browser 51.71 kB +0.02% +6 B 🔺
@sentry/node 120.39 kB -0.84% -1.01 kB 🔽
@sentry/node/import (ESM hook with diagnostics-channel injection) 166 B - -
@sentry/node - without tracing 83.86 kB -1.24% -1.05 kB 🔽
@sentry/aws-serverless 92.15 kB -1.12% -1.04 kB 🔽
@sentry/cloudflare (withSentry) - minified 198.33 kB -0.02% -25 B 🔽
@sentry/cloudflare (withSentry) 487.82 kB +0.02% +72 B 🔺

View base workflow run

Comment thread packages/opentelemetry/src/propagator.ts
Comment thread packages/node/src/sdk/initOtel.ts
Comment thread packages/nextjs/src/server/index.ts Outdated
Comment thread packages/opentelemetry/src/propagator.ts
Comment thread packages/opentelemetry/src/propagator.ts
Comment thread packages/opentelemetry/src/propagator.ts
Comment thread packages/opentelemetry/src/propagator.ts
Comment thread packages/opentelemetry/src/propagator.ts
Comment thread packages/opentelemetry/src/asyncContextStrategy.ts
@mydea
mydea force-pushed the fn/propagator branch 2 times, most recently from c4a4b13 to 93d6542 Compare July 29, 2026 11:15
@mydea
mydea changed the base branch from develop to fn/continue-start-trace-tests July 29, 2026 11:15
Comment thread packages/opentelemetry/src/index.ts
@mydea
mydea force-pushed the fn/propagator branch 2 times, most recently from afdbe2c to 7aa618c Compare July 29, 2026 14:13
scope.setPropagationContext({
traceId: generateTraceId(),
sampleRand: safeMathRandom(),
return withActiveSpan(null, () => {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

just switching this up, it should not make concrete difference but seems more resilient, as it makes us more independent of what withActiveSpan does under the hood!

}

/** Custom implementation for OTEL, so we can handle scope-span linking. */
protected _getTraceInfoFromScope(

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

it seems this was not used anywhere anymore!

Base automatically changed from fn/continue-start-trace-tests to develop July 30, 2026 07:49
@mydea mydea changed the title feat(opentelemetry)!: Remove SentryPropagator feat(opentelemetry)!: Simplify propagation and remove custom OTel tracing utils Jul 30, 2026
@mydea
mydea marked this pull request as ready for review July 30, 2026 07:54

@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.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

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

Reviewed by Cursor Bugbot for commit dff4c15. Configure here.

Comment thread packages/opentelemetry/src/propagator.ts
@mydea
mydea requested review from a team as code owners July 30, 2026 07:54
Comment thread packages/opentelemetry/src/index.ts
consoleSandbox(() => {
// oxlint-disable-next-line no-console
console.warn(
'SentryPropagator: Injecting with a different context than the active one - this is not supported. Skipping injection.',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We could make this more descriptive. A suggestion:

Suggested change
'SentryPropagator: Injecting with a different context than the active one - this is not supported. Skipping injection.',
'SentryPropagator: Injecting trace data of a different context than the active one is not supported. Skipping injection.',

Comment thread packages/opentelemetry/src/propagator.ts
@mydea
mydea changed the base branch from develop to fn/bun-http-integration July 30, 2026 12:55
mydea and others added 6 commits July 31, 2026 12:09
Bun does not emit the `http.server.request.start` diagnostics channel that the
Node SDK relies on to isolate each incoming request. As a result, servers built
on `node:http` (such as Next.js running via `bun --bun`) do not get a fresh
isolation scope and trace per request, so unrelated requests can share one trace.

`bunHttpIntegration` closes this gap by patching `http.Server.prototype.emit` and,
on each server's first `request` event, handing the server to the same core
instrumentation the Node SDK uses (`getHttpServerSubscriptions` → `instrumentServer`).
We patch the prototype rather than `createServer` because the server is typically
created and `listen()`ed before Sentry initializes (e.g. Next.js creates its server
before running the `instrumentation.ts` `register()` hook).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds an assertion that an `http.server` span is created for incoming requests
when spans are enabled, alongside the existing isolation and trace-continuation
coverage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: isaacs <i@izs.me>
@mydea
mydea requested a review from a team as a code owner July 31, 2026 10:19
@mydea
mydea requested review from logaretm and msonnb and removed request for a team July 31, 2026 10:19
@mydea
mydea changed the base branch from fn/bun-http-integration to fn/streamline-acs-isolation-scope July 31, 2026 10:20
mydea added 2 commits July 31, 2026 13:25
remove getTraceData & fixes

fix traceparent propagation
@mydea
mydea force-pushed the fn/propagator branch 2 times, most recently from cf12656 to 5d24bc4 Compare July 31, 2026 11:26
Comment thread packages/opentelemetry/src/propagator.ts
Comment on lines 72 to 73
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bug: The check for a continuing trace in SentryPropagator.extract is unreliable, potentially causing new requests to inherit incorrect trace IDs from a pre-existing active context.
Severity: LOW

Suggested Fix

The check for a continuing trace should be more specific. Instead of just checking for the existence of a span context, verify that the context was actually modified by getContextWithRemoteActiveSpan. A better condition would be: const isContinuingTrace = ctxWithRemoteSpan !== ctx && trace.getSpanContext(ctxWithRemoteSpan) !== undefined;.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: packages/opentelemetry/src/propagator.ts#L129-L130

Potential issue: In the `SentryPropagator.extract` method, the logic to determine if a
trace is being continued is flawed. The check `trace.getSpanContext(ctxWithRemoteSpan)
!== undefined` cannot distinguish between a new remote span being added versus the input
context already containing a span. If `extract` is called with a context that already
has an active span but no incoming trace headers are present, the check will incorrectly
evaluate to true. This prevents the creation of a new trace ID, causing the new request
to erroneously inherit the trace ID from the pre-existing, unrelated context. While
`AsyncLocalStorage` in Node.js mitigates this in most cases by providing context
isolation, the implementation remains technically incorrect and could lead to trace
context bleeding between requests under specific interleaving scenarios.

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.

2 participants