Skip to content

ref(opentelemetry)!: Streamline OTEL context managment into setOpenTelemetryContextAsyncContextStrategy() - #22586

Merged
mydea merged 9 commits into
developfrom
fn/wrap-context-manager
Jul 29, 2026
Merged

ref(opentelemetry)!: Streamline OTEL context managment into setOpenTelemetryContextAsyncContextStrategy()#22586
mydea merged 9 commits into
developfrom
fn/wrap-context-manager

Conversation

@mydea

@mydea mydea commented Jul 24, 2026

Copy link
Copy Markdown
Member

This removes browser compatibility from the @sentry/opentelemetry package. The package is not usable in the browser anyway and will be fully removed as a browser target in the future — for now we drop the browser shim layer of the context strategy. Concretely, this removes index.browser.ts (which only contained stubs that logged "not supported in the browser"), simplifies the exports map in package.json to plain import/require conditions, and drops the browser entrypoint from the rollup config.

With the browser variant gone, setOpenTelemetryContextAsyncContextStrategy() can own the whole context-management setup directly. It now creates the AsyncLocalStorage instance, wires up the SentryAsyncLocalStorageContextManager, sets it as the global context manager, and returns the lookup — rather than having callers construct and register a context manager themselves and pass a getTracingChannelBinding factory back in.

Because the strategy now owns the AsyncLocalStorage, we no longer need the separate node vs. custom async-context-strategy split (nodeAsyncContextStrategy.ts is removed), nor the wrapContextManagerClass shim used to graft Sentry scope handling onto an externally-provided context manager.

For vercel-edge, it turns out we no longer need any custom handling for async local storage — since Next 13 this is allowlisted, so we can import it normally on the edge and just use the same default strategy as node. This lets us drop the vendored abstract-async-hooks-context-manager / async-local-storage-context-manager files and the skipOpenTelemetrySetup special-casing in the edge SDK.

The context manager also no longer checks for an EventEmitter instance (which does not work in vercel-edge) and instead inspects the shape of the object.

Note that this uncovered a "bug" with the fs instrumentation, where it was instrumented too early. This was masked by undefined behavior due to suppressTracing, as things were not fully set up yet.

Comment thread packages/vercel-edge/src/sdk.ts Outdated
Comment thread packages/opentelemetry/src/exports.ts
@mydea
mydea marked this pull request as ready for review July 24, 2026 11:29
@mydea
mydea requested a review from a team as a code owner July 24, 2026 11:29
@mydea
mydea requested review from JPeer264 and isaacs and removed request for a team July 24, 2026 11:29

@isaacs isaacs left a comment

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.

I think hardening the _bindEventEmitter() checks would be good, and it's worth considering the ordering of precedence in the ALS argument passed in.

Also, maybe this is fine since it's just a stub and explicitly warns that it doesn't do anything, but I notice that the SentryAsyncLocalStorageContextManager has more functions now, and the stub at packages/opentelemetry/src/index.browser.ts doesn't implement them. Could be good to put some no-ops on it.

(getAsyncContextStrategy(getMainCarrier()).getTracingChannelBinding?.()
?.asyncLocalStorage as AsyncLocalStorage<Context>) ?? new AsyncLocalStorage<Context>();
?.asyncLocalStorage as AsyncLocalStorage<Context>) ??
(asyncLocalStorage || new AsyncLocalStorage());

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.

If I'm reading this right, it'll only use the passed-in ALS if there isn't already an asyncLocalStorage on the ACS.

It's higher priority than creating a new one, but shouldn't it use the explicitly provided value as the highest priority?

Comment thread packages/opentelemetry/src/asyncLocalStorageContextManager.ts Outdated
Comment thread packages/opentelemetry/src/exports.ts
Comment thread packages/opentelemetry/src/asyncLocalStorageContextManager.ts
Comment thread packages/vercel-edge/src/sdk.ts Outdated
Comment thread packages/opentelemetry/src/exports.ts
Comment thread packages/opentelemetry/src/index.ts Outdated
@mydea
mydea force-pushed the fn/wrap-context-manager branch from 3f7d8fa to f20d4b0 Compare July 27, 2026 08:42
Comment thread packages/opentelemetry/src/asyncLocalStorageContextManager.ts
@mydea
mydea force-pushed the fn/wrap-context-manager branch from f20d4b0 to 2474b3a Compare July 27, 2026 09:53
@mydea
mydea requested review from a team as code owners July 27, 2026 09:53
@mydea
mydea requested review from nicohrubec and s1gr1d and removed request for a team July 27, 2026 09:53
Comment thread packages/vercel-edge/src/async-local-storage.ts Outdated
Comment thread packages/opentelemetry/src/asyncLocalStorageContextManager.ts
Comment thread packages/vercel-edge/src/sdk.ts Outdated
@mydea
mydea force-pushed the fn/wrap-context-manager branch from 2474b3a to 9c0910d Compare July 27, 2026 10:39
Comment thread packages/opentelemetry/src/asyncLocalStorageContextManager.ts Outdated
@mydea
mydea force-pushed the fn/wrap-context-manager branch from 2143fde to f25d2c8 Compare July 27, 2026 11:54
Comment thread packages/vercel-edge/test/async.test.ts Outdated
Comment thread packages/opentelemetry/src/asyncLocalStorageContextManager.ts
@mydea
mydea force-pushed the fn/wrap-context-manager branch from ca90926 to c25e41a Compare July 27, 2026 12:25
@mydea

mydea commented Jul 27, 2026

Copy link
Copy Markdown
Member Author

@isaacs I updated this now to be more holistic, actually removing more stuff. for vercel-edge, turns out we do not need this globals stuff anymore and can just use async hooks directly, much nicer!

@mydea
mydea requested a review from isaacs July 27, 2026 12:41
@mydea mydea changed the title ref(opentelemetry): Remove wrapContextManager export ref(opentelemetry)!: Remove wrapContextManager export Jul 27, 2026

@isaacs isaacs left a comment

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.

Some merge conflicts (likely due to oxlint changes, I think?) and a suggestion to harden the test a bit, but easily fixed and nothing worth gating on. Previous concerns addressed, looks good :)

this._asyncLocalStorage =
(getAsyncContextStrategy(getMainCarrier()).getTracingChannelBinding?.()
?.asyncLocalStorage as AsyncLocalStorage<Context>) ?? new AsyncLocalStorage<Context>();
?.asyncLocalStorage as AsyncLocalStorage<Context>) ?? new AsyncLocalStorage();

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.

This is a big improvement from last review, definitely solves the issue I raised.

The only remaining concern, which is much less, is that the ordering is load bearing. That is, if the strategy is set first, then the manager and the tracing-channel bindStore path will share a context, and everything's good. If not, then it silently falls back to a newly constructed instance, and the context is effectively hidden.

I checked the cases where this might happen, and it looks like they're all correct, so I don't think this is much of a hazard. But it might still be worthwhile to add a debug log here, if that does happen.

    const existingALS: AsyncLocalStorage<Context> | undefined = getAsyncContextStrategy(getMainCarrier()).getTracingChannelBinding?.()?.asyncLocalStorage;
    if (!existingALS) {
      // debug log here
    }
    this._asyncLocalStorage = existingALS ?? new AsyncLocalStorage();

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.

yeah, this is def. not ideal. the problem is that this is hard to make fully type safe I suppose as in browser this is not set but in other cases it should always be set I suppose 🤔 maybe we can harden this even more in a future iteration of this, making sure that this can never be set without having this defined on ACS 🤔 the debug log makes sense to me though, will add 🙏

Comment thread packages/vercel-edge/test/async.test.ts Outdated
Comment on lines 15 to 17

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.

This is the reverse of how it works in production (see packages/vercel-edge/src/sdk.ts, where init sets up the ACS and then setupOtel does new SentryAsyncLocalStorageContextManager() later).

It's fine in the test, but would recommend swapping these two lines to match.

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.

Would probably also be good to add an assertion here to verify that manager.getAsyncLocalStorageLookup().asyncLocalStorage === getAsyncContextStrategy(getMainCarrier()).getTracingChannelBinding().asyncLocalStorage, since that's the goal, and would prevent it from regressing.

mydea added 3 commits July 29, 2026 08:42
Instead, the context manager accepts the async local storage instance now, so we can also use this in vercel-edge. this will eventually go away/move to server-utils likely so we can revisit this in a follow up.
actually other approach

we just depend on async local storage

rely on acs stuff for edge handling

fixes

fixes

oops

fix stuff

small ref
@mydea mydea changed the title ref(opentelemetry)!: Remove wrapContextManager export ref(opentelemetry)!: Streamline OTEL context managment into setOpenTelemetryContextAsyncContextStrategy() Jul 29, 2026
@mydea
mydea force-pushed the fn/wrap-context-manager branch from c25e41a to 7c1fa7e Compare July 29, 2026 07:47
Comment thread packages/vercel-edge/src/sdk.ts

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

Reviewed by Cursor Bugbot for commit 7c1fa7e. Configure here.

Comment thread packages/opentelemetry/src/index.browser.ts
@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

⚠️ Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

Path Size % Change Change
@sentry/browser 29.84 kB -0.01% -2 B 🔽
@sentry/browser - with treeshaking flags 28.05 kB -0.01% -2 B 🔽
@sentry/browser (incl. Tracing) 47.12 kB -0.01% -2 B 🔽
@sentry/browser (incl. Tracing + Span Streaming) 47.13 kB -0.01% -2 B 🔽
@sentry/browser (incl. Tracing, Profiling) 51.83 kB -0.01% -3 B 🔽
@sentry/browser (incl. Tracing, Replay) 86.43 kB -0.01% -2 B 🔽
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 75.86 kB -0.01% -2 B 🔽
@sentry/browser (incl. Tracing, Replay with Canvas) 91.15 kB -0.01% -2 B 🔽
@sentry/browser (incl. Tracing, Replay, Feedback) 103.79 kB -0.01% -1 B 🔽
@sentry/browser (incl. Feedback) 47.15 kB -0.01% -1 B 🔽
@sentry/browser (incl. sendFeedback) 34.68 kB -0.01% -2 B 🔽
@sentry/browser (incl. FeedbackAsync) 39.78 kB -0.01% -2 B 🔽
@sentry/browser (incl. Metrics) 30.91 kB -0.01% -2 B 🔽
@sentry/browser (incl. Logs) 31.14 kB -0.01% -2 B 🔽
@sentry/browser (incl. Metrics & Logs) 31.82 kB -0.01% -1 B 🔽
@sentry/react 31.63 kB -0.01% -2 B 🔽
@sentry/react (incl. Tracing) 49.35 kB -0.01% -2 B 🔽
@sentry/vue 34.76 kB -0.01% -2 B 🔽
@sentry/vue (incl. Tracing) 49.08 kB -0.01% -3 B 🔽
@sentry/svelte 29.87 kB -0.01% -2 B 🔽
CDN Bundle 31.89 kB -0.01% -2 B 🔽
CDN Bundle (incl. Tracing) 47.47 kB -0.01% -3 B 🔽
CDN Bundle (incl. Logs, Metrics) 33.44 kB -0.01% -2 B 🔽
CDN Bundle (incl. Tracing, Logs, Metrics) 48.84 kB -0.01% -1 B 🔽
CDN Bundle (incl. Replay, Logs, Metrics) 72.8 kB -0.01% -3 B 🔽
CDN Bundle (incl. Tracing, Replay) 85.11 kB -0.01% -4 B 🔽
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 86.4 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 90.87 kB -0.01% -3 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 92.19 kB -0.01% -1 B 🔽
CDN Bundle - uncompressed 95.11 kB -0.01% -4 B 🔽
CDN Bundle (incl. Tracing) - uncompressed 142.33 kB -0.01% -4 B 🔽
CDN Bundle (incl. Logs, Metrics) - uncompressed 99.82 kB -0.01% -4 B 🔽
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 146.31 kB -0.01% -4 B 🔽
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 224.58 kB -0.01% -4 B 🔽
CDN Bundle (incl. Tracing, Replay) - uncompressed 261.58 kB -0.01% -4 B 🔽
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 265.55 kB -0.01% -4 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 275.29 kB -0.01% -4 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 279.24 kB -0.01% -4 B 🔽
@sentry/nextjs (client) 51.94 kB -0.01% -3 B 🔽
@sentry/sveltekit (client) 47.54 kB -0.01% -2 B 🔽
@sentry/core/server 79.78 kB -0.01% -2 B 🔽
@sentry/core/browser 51.61 kB -0.01% -2 B 🔽
@sentry/node 121.86 kB +0.66% +796 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 166 B - -
@sentry/node - without tracing 85.3 kB +0.97% +815 B 🔺
@sentry/aws-serverless 93.52 kB +0.88% +814 B 🔺
@sentry/cloudflare (withSentry) - minified 197.8 kB -0.01% -4 B 🔽
@sentry/cloudflare (withSentry) 486.19 kB -0.01% -8 B 🔽

View base workflow run

@mydea
mydea requested a review from a team as a code owner July 29, 2026 08:39
@mydea
mydea requested review from logaretm and msonnb and removed request for a team July 29, 2026 08:39
Comment thread packages/node/src/sdk/index.ts
@mydea

mydea commented Jul 29, 2026

Copy link
Copy Markdown
Member Author

I have refactored this further based on @isaacs feedback. Now, this removes some old cruft we had around for browser compatibility (which we no longer need/will have) and combines the acs + context manager code into a single call, which allows us to avoid some conditionals for implicit binding we had before which is now explicit. This leave some things in a bit of a weird in-between state - mostly, skipping otel setup will still set up the context manager now. but since this will fully go away this is fine right now IMHO.

},
"dependencies": {
"@opentelemetry/api": "1.9.0",
"@opentelemetry/api": "1.9.1",

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.

intermediate state: this is needed because the api versions need to align. will go away once this is no longer supported so this is OK for now.

@mydea
mydea merged commit ab2fc1d into develop Jul 29, 2026
222 of 223 checks passed
@mydea
mydea deleted the fn/wrap-context-manager branch July 29, 2026 09:37
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