feat(node)!: Default most SDKs to a no-tracer-provider setup - #22957
Draft
andreiborza wants to merge 14 commits into
Draft
feat(node)!: Default most SDKs to a no-tracer-provider setup#22957andreiborza wants to merge 14 commits into
andreiborza wants to merge 14 commits into
Conversation
Flip the default OpenTelemetry setup for server SDKs: most now run without a Sentry tracer provider, using an AsyncLocalStorage context strategy for scope isolation. Reuses the existing `skipOpenTelemetrySetup` option with a flipped default (true for node/cloudflare, false for nextjs/sveltekit). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Member
Author
|
batman begin |
…er-provider # Conflicts: # packages/cloudflare/src/sdk.ts
Contributor
size-limit report 📦
|
In the no-provider default, setupEventContextTrace read the OpenTelemetry active span before scope data was applied, so a user's own OTel span could override the Sentry trace on error events. Only set up this hook when Sentry owns the provider.
Assert the actual user-visible effect: a foreign OpenTelemetry active span must not override the Sentry trace on error events in the no-provider default, and is adopted when the tracer provider is enabled. Verified to fail without the gating.
Channel-based instrumentation stamps OTel semantic attributes on native spans but leaves the Sentry-convention fields (e.g. sentry.op) to be inferred by the provider pipeline. Without a tracer provider that inference never ran, so outgoing http/fetch and other channel spans were half-formed. Run the same applyOtelSpanData / backfillStreamedSpanDataFromOtel hooks via the client in no-provider mode.
tracer-start-active-span-error drives the raw OpenTelemetry tracer, and http-otel-double-instrumentation exercises coexistence with a user-owned OTel HttpInstrumentation whose spans reach Sentry via the tracer provider. Both require the provider, so they opt into it explicitly under the new no-provider default.
A trace continued without incoming baggage freezes an empty DSC. Reading it back short-circuited before sample_rand was added, so downstream requests in no-provider mode propagated baggage without sentry-sample_rand. Backfill it from the captured scope's propagation context, matching what the OTel span sampler writes to trace state.
In no-provider mode the AsyncLocalStorage tracing-channel binding planted every channel span as the active span, including ignored ones. Children and outgoing requests then propagated from the ignored span, dropping the continued positive sampling decision. Skip ignored spans so propagation falls back to the nearest emitted parent, matching the OTel context manager.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit e7a39a6. Configure here.
The previous approach backfilled sample_rand in getDynamicSamplingContextFromSpan, which also ran in OTel mode and wrongly added sample_rand to remote-parent DSCs (breaking opentelemetry unit tests). Move the backfill to the root-span freeze site and gate it on a genuinely empty DSC, so only continued traces without incoming baggage get sample_rand and populated frozen DSCs are left untouched. Also opt the Cloudflare Vercel AI v6 integration test into the tracer provider: the AI SDK emits spans via @opentelemetry/api, which need the provider to be captured.
The `otel` transaction context carries the OpenTelemetry SDK resource attributes, which are only set when Sentry owns the tracer provider. Under the no-provider default these Astro server SDKs no longer emit it, so the assertion is removed.
The event-loop-block watchdog reads the active scope out of the client's `asyncLocalStorageLookup`, which was only populated when the OpenTelemetry context strategy was set up. Without a tracer provider it was undefined, so ANR events fell back to the global scope and dropped per-isolation-scope user data and breadcrumbs. Return the AsyncLocalStorage from `setAsyncLocalStorageAsyncContextStrategy` and set `asyncLocalStorageLookup` in the no-provider branch. The lookup now carries a generic `stateLookup` key path (empty for the ALS store, which already is the scopes object; `['_currentContext', ...]` for the OTel context) instead of an OTel-specific symbol.
Continuing a trace without an incoming Sentry DSC now folds the scope's sample_rand into the propagated (otherwise empty) DSC so downstream sampling stays consistent. Update the sveltekit handle and browser tracing tests that asserted a strictly empty DSC in that case.
The server middleware index counter was stored on the OpenTelemetry context, so it reset to 0 on every middleware under the no-provider default (no context propagation without a tracer provider), producing indices like [0,0,0]. Key it by the incoming Request in a WeakMap instead, mirroring the client instrumentation, so it works in both modes.
Keying the per-request middleware counter by the incoming Request did not increment in the real server (each middleware hook sees a distinct request object), so indices stayed [0,0,0]. Key by the request's root span instead, which is the single stable transaction all of a request's middlewares run under, in both provider modes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

What
Flip the default OpenTelemetry setup for server SDKs: most now run without a Sentry tracer provider, using an AsyncLocalStorage context strategy for scope isolation. Controlled by reusing the existing
skipOpenTelemetrySetupoption, whose default is flipped.skipOpenTelemetrySetuptotruefor@sentry/node(and inheritors: bun, serverless, nuxt, solidstart, astro, remix) and@sentry/cloudflare; no tracer provider is registered.falsefor@sentry/nextjsand@sentry/sveltekit, which emit their own OTel spans and need the provider to pick them up.setAsyncLocalStorageAsyncContextStrategyinstead of the OTel context strategy. Instrumentation still emits spans via corestartSpan; only the OTel provider and propagator are dropped.skipOpenTelemetrySetupso auto perf stays on by default.spans: falseopt-out for bring-your-own-OTel users inMIGRATION.md.Why
In v11 Sentry no longer takes over your OpenTelemetry setup. Most SDKs do not need a tracer provider to produce spans, so defaulting to none lets users run their own OTel cleanly alongside Sentry without Sentry spans leaking into their pipeline. SDKs that rely on OTel span pickup keep the provider. This also unblocks the light-mode goal from #22418.
Closes: #22486