feat(node): Wire up SentryTracerProvider - #21680

Merged
andreiborza merged 29 commits into
ab/sentry-trace-provider-sealfrom
ab/sentry-trace-provider-node
Jul 2, 2026
Merged

feat(node): Wire up SentryTracerProvider#21680
andreiborza merged 29 commits into
ab/sentry-trace-provider-sealfrom
ab/sentry-trace-provider-node

Conversation

@andreiborza

@andreiborzaandreiborza commented Jun 22, 2026

Copy link
Copy Markdown
Member

What

Makes Sentry's minimal SentryTracerProvider the default OpenTelemetry tracer provider for @sentry/node, replacing the full OTel SDK BasicTracerProvider. The SDK provider stays available via the new openTelemetryBasicTracerProvider option (and is used automatically when custom openTelemetrySpanProcessors are provided).

Why

The minimal provider creates native Sentry spans and skips the OTel SDK span pipeline. This PR turns it on for Node and re-homes the work that pipeline used to do (op/name/source/status inference, resource and response context, the orphan-fetch and status-code rules) onto client hooks and the instrumentation, so it holds without an OTel SDK provider present. BasicTracerProvider remains for setups that rely on OTel SDK features the minimal provider does not run.

@andreiborza
andreiborza requested a review from a team as a code ownerJune 22, 2026 09:35
@andreiborza
andreiborza requested review from JPeer264 and mydea and removed request for a teamJune 22, 2026 09:35
@nicohrubec
nicohrubec self-requested a review June 22, 2026 09:46
@github-actions

github-actionsBot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser27.59 kB--
@sentry/browser - with treeshaking flags26.03 kB--
@sentry/browser (incl. Tracing)46.24 kB+0.46%+209 B 🔺
@sentry/browser (incl. Tracing + Span Streaming)48 kB+0.47%+222 B 🔺
@sentry/browser (incl. Tracing, Profiling)51.01 kB+0.41%+205 B 🔺
@sentry/browser (incl. Tracing, Replay)85.49 kB+0.27%+224 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags75.1 kB+0.31%+225 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas)90.19 kB+0.26%+232 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback)102.84 kB+0.23%+226 B 🔺
@sentry/browser (incl. Feedback)44.76 kB--
@sentry/browser (incl. sendFeedback)32.38 kB--
@sentry/browser (incl. FeedbackAsync)37.51 kB--
@sentry/browser (incl. Metrics)28.67 kB--
@sentry/browser (incl. Logs)28.91 kB--
@sentry/browser (incl. Metrics & Logs)29.59 kB--
@sentry/react29.38 kB--
@sentry/react (incl. Tracing)48.51 kB+0.35%+169 B 🔺
@sentry/vue33.03 kB+0.64%+207 B 🔺
@sentry/vue (incl. Tracing)48.11 kB+0.43%+205 B 🔺
@sentry/svelte27.61 kB--
CDN Bundle30 kB--
CDN Bundle (incl. Tracing)48.22 kB+0.46%+219 B 🔺
CDN Bundle (incl. Logs, Metrics)31.57 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)49.53 kB+0.4%+194 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics)70.77 kB--
CDN Bundle (incl. Tracing, Replay)85.68 kB+0.24%+200 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)86.97 kB+0.24%+204 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback)91.5 kB+0.22%+200 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.77 kB+0.24%+222 B 🔺
CDN Bundle - uncompressed89.35 kB--
CDN Bundle (incl. Tracing) - uncompressed145.92 kB+0.45%+649 B 🔺
CDN Bundle (incl. Logs, Metrics) - uncompressed94.05 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed149.89 kB+0.44%+649 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed218.59 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed264.93 kB+0.25%+649 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed268.89 kB+0.25%+649 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed278.63 kB+0.24%+651 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed282.58 kB+0.24%+651 B 🔺
@sentry/nextjs (client)50.95 kB+0.43%+218 B 🔺
@sentry/sveltekit (client)46.65 kB+0.49%+226 B 🔺
@sentry/core/server78.28 kB+0.72%+552 B 🔺
@sentry/core/browser64.62 kB+0.88%+563 B 🔺
@sentry/node-core63.17 kB+1.28%+796 B 🔺
@sentry/node122.76 kB+1.27%+1.54 kB 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)69.95 kB--
@sentry/node/light50.7 kB+0.47%+233 B 🔺
@sentry/node - without tracing74.22 kB+2.11%+1.53 kB 🔺
@sentry/aws-serverless85.01 kB+1.73%+1.45 kB 🔺
@sentry/cloudflare (withSentry) - minified181.52 kB+0.5%+900 B 🔺
@sentry/cloudflare (withSentry)449.12 kB+0.52%+2.29 kB 🔺

View base workflow run

@andreiborzaandreiborza changed the title feat(node): Wire up _experiments.useSentryTracerProviderfeat(node): Wire up SentryTracerProviderJun 22, 2026
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from e05567e to dc3cd9dCompareJune 22, 2026 11:32
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from dc3cd9d to f3c0c65CompareJune 22, 2026 11:49
Comment threadpackages/node/src/sdk/initOtel.ts Outdated
Comment threadpackages/node/src/sdk/initOtel.ts
Comment threadpackages/node/src/sdk/initOtel.ts Outdated
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from e200c8f to 502dca9CompareJune 22, 2026 18:20
Comment threadpackages/node/src/sdk/initOtel.ts
Comment threadpackages/node/src/sdk/initOtel.ts
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from 502dca9 to 6ae8302CompareJune 22, 2026 18:30
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from 6ae8302 to e2f91c0CompareJune 22, 2026 18:46
Comment threadpackages/node/test/sdk/init.test.ts
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch 2 times, most recently from 4a3010c to d2384e8CompareJune 22, 2026 22:34
Comment threadpackages/node/src/sdk/initOtel.ts
Comment threadpackages/core/src/types/options.ts Outdated
* @default false
* @experimental
*/
useSentryTracerProvider?: boolean;

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.

should this live in core? is this not a node-specific option?

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.

also, why experimental? this can just be a regular option, and as discussed I'd actually make it opt-out (or more specifically, make the default dynamic based on if any options are set that require the more fully features tracer, e.g. spanProcessors)

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.

Wouldn't this be a breaking change? I thought the opt-out would be rather for v11 and in v10 it's opt-in

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

No, it's not a breaking change. The only case where it would be breaking is if people added span processors, but in that case we default back to the otel sdk tracer provider.

@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from 06f6f64 to 6759aa8CompareJune 23, 2026 19:13
Outside of span streaming, an outgoing fetch (`http.client`) span with no local
parent is no longer recorded as a standalone transaction — the downstream
sampling decision is left to the server. This is enforced via `onlyIfParent`,
which still creates a non-recording span so trace propagation headers are
injected.
This rule already lives in `SentrySampler`, but that only runs when an
OpenTelemetry SDK tracer provider is set up. Enforcing it in the instrumentation
makes it hold for the `SentryTracerProvider` and for SDKs that don't use an
OpenTelemetry tracer provider at all. The sampler rule is kept for OpenTelemetry
SDK / custom OpenTelemetry setups.
The transaction is assembled synchronously from the live span tree when the root
span ends, dropping child spans whose instrumentation closes them after it - in the
same tick (diagnostics-channel `asyncEnd`) or on a later tick (e.g. prisma engine
spans). A per-client debounced timer (the one the OpenTelemetry span exporter uses)
delays the snapshot so those children land first, and drains on the client `flush`
hook so `Sentry.flush()` / `close()` stays safe.
Enabled on the NodeClient rather than the SentryTracerProvider so it applies with or
without a tracer provider; the browser keeps its synchronous capture.
Under the SentryTracerProvider, streamed spans carry `sentry.origin` as a
first-class attribute including the default `manual` value, whereas the
OpenTelemetry SDK path omits the `manual` default. The `mysql` (v1) db spans
and the `pg.connect` span set no explicit origin, so they surface as
`manual` here.
Assert it for now. When those instrumentations are reworked to set an
explicit `auto.db.otel.*` origin (e.g. #21568 for mysql), these expectations
will be updated to the real origin then.
These assert prisma's engine spans (replayed asynchronously by
`@prisma/instrumentation`), which the SentryTracerProvider drops because it
assembles transactions synchronously on root-span end with no SpanExporter
buffer to wait for late children. They pass on the OpenTelemetry SDK
(`BasicTracerProvider`) path. Skip them here until the general
"complete span-tree capture without a SpanExporter" follow-up lands; v7 is
left enabled as it currently captures the engine spans in time.
Re-enabled now that the streamlined fastify integration (#21706) names spans at
creation instead of renaming via updateName(), so the SentryTracerProvider no
longer stamps sentry.source: 'custom'. Verified locally via e2e (11/11 pass each).
Moves the _INTERNAL_setDeferSegmentSpanCapture call out of initOtel (which only runs
on Sentry.init and only wires the first client) into the NodeClient constructor, which
runs for every client — first, second, or manually constructed — so each defers correctly.
Sign up for freeto 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.

6 participants

@andreiborza@mydea@Lms24@JPeer264@chargome@nicohrubec
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat(node): Wire up SentryTracerProvider - #21680

Merged
andreiborza merged 29 commits into
ab/sentry-trace-provider-sealfrom
ab/sentry-trace-provider-node
Jul 2, 2026
Merged

feat(node): Wire up SentryTracerProvider#21680
andreiborza merged 29 commits into
ab/sentry-trace-provider-sealfrom
ab/sentry-trace-provider-node

Conversation

@andreiborza

@andreiborzaandreiborza commented Jun 22, 2026

Copy link
Copy Markdown
Member

What

Makes Sentry's minimal SentryTracerProvider the default OpenTelemetry tracer provider for @sentry/node, replacing the full OTel SDK BasicTracerProvider. The SDK provider stays available via the new openTelemetryBasicTracerProvider option (and is used automatically when custom openTelemetrySpanProcessors are provided).

Why

The minimal provider creates native Sentry spans and skips the OTel SDK span pipeline. This PR turns it on for Node and re-homes the work that pipeline used to do (op/name/source/status inference, resource and response context, the orphan-fetch and status-code rules) onto client hooks and the instrumentation, so it holds without an OTel SDK provider present. BasicTracerProvider remains for setups that rely on OTel SDK features the minimal provider does not run.

@andreiborza
andreiborza requested a review from a team as a code ownerJune 22, 2026 09:35
@andreiborza
andreiborza requested review from JPeer264 and mydea and removed request for a teamJune 22, 2026 09:35
@nicohrubec
nicohrubec self-requested a review June 22, 2026 09:46
@github-actions

github-actionsBot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser27.59 kB--
@sentry/browser - with treeshaking flags26.03 kB--
@sentry/browser (incl. Tracing)46.24 kB+0.46%+209 B 🔺
@sentry/browser (incl. Tracing + Span Streaming)48 kB+0.47%+222 B 🔺
@sentry/browser (incl. Tracing, Profiling)51.01 kB+0.41%+205 B 🔺
@sentry/browser (incl. Tracing, Replay)85.49 kB+0.27%+224 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags75.1 kB+0.31%+225 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas)90.19 kB+0.26%+232 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback)102.84 kB+0.23%+226 B 🔺
@sentry/browser (incl. Feedback)44.76 kB--
@sentry/browser (incl. sendFeedback)32.38 kB--
@sentry/browser (incl. FeedbackAsync)37.51 kB--
@sentry/browser (incl. Metrics)28.67 kB--
@sentry/browser (incl. Logs)28.91 kB--
@sentry/browser (incl. Metrics & Logs)29.59 kB--
@sentry/react29.38 kB--
@sentry/react (incl. Tracing)48.51 kB+0.35%+169 B 🔺
@sentry/vue33.03 kB+0.64%+207 B 🔺
@sentry/vue (incl. Tracing)48.11 kB+0.43%+205 B 🔺
@sentry/svelte27.61 kB--
CDN Bundle30 kB--
CDN Bundle (incl. Tracing)48.22 kB+0.46%+219 B 🔺
CDN Bundle (incl. Logs, Metrics)31.57 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)49.53 kB+0.4%+194 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics)70.77 kB--
CDN Bundle (incl. Tracing, Replay)85.68 kB+0.24%+200 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)86.97 kB+0.24%+204 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback)91.5 kB+0.22%+200 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.77 kB+0.24%+222 B 🔺
CDN Bundle - uncompressed89.35 kB--
CDN Bundle (incl. Tracing) - uncompressed145.92 kB+0.45%+649 B 🔺
CDN Bundle (incl. Logs, Metrics) - uncompressed94.05 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed149.89 kB+0.44%+649 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed218.59 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed264.93 kB+0.25%+649 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed268.89 kB+0.25%+649 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed278.63 kB+0.24%+651 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed282.58 kB+0.24%+651 B 🔺
@sentry/nextjs (client)50.95 kB+0.43%+218 B 🔺
@sentry/sveltekit (client)46.65 kB+0.49%+226 B 🔺
@sentry/core/server78.28 kB+0.72%+552 B 🔺
@sentry/core/browser64.62 kB+0.88%+563 B 🔺
@sentry/node-core63.17 kB+1.28%+796 B 🔺
@sentry/node122.76 kB+1.27%+1.54 kB 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)69.95 kB--
@sentry/node/light50.7 kB+0.47%+233 B 🔺
@sentry/node - without tracing74.22 kB+2.11%+1.53 kB 🔺
@sentry/aws-serverless85.01 kB+1.73%+1.45 kB 🔺
@sentry/cloudflare (withSentry) - minified181.52 kB+0.5%+900 B 🔺
@sentry/cloudflare (withSentry)449.12 kB+0.52%+2.29 kB 🔺

View base workflow run

@andreiborzaandreiborza changed the title feat(node): Wire up _experiments.useSentryTracerProviderfeat(node): Wire up SentryTracerProviderJun 22, 2026
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from e05567e to dc3cd9dCompareJune 22, 2026 11:32
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from dc3cd9d to f3c0c65CompareJune 22, 2026 11:49
Comment threadpackages/node/src/sdk/initOtel.ts Outdated
Comment threadpackages/node/src/sdk/initOtel.ts
Comment threadpackages/node/src/sdk/initOtel.ts Outdated
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from e200c8f to 502dca9CompareJune 22, 2026 18:20
Comment threadpackages/node/src/sdk/initOtel.ts
Comment threadpackages/node/src/sdk/initOtel.ts
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from 502dca9 to 6ae8302CompareJune 22, 2026 18:30
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from 6ae8302 to e2f91c0CompareJune 22, 2026 18:46
Comment threadpackages/node/test/sdk/init.test.ts
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch 2 times, most recently from 4a3010c to d2384e8CompareJune 22, 2026 22:34
Comment threadpackages/node/src/sdk/initOtel.ts
Comment threadpackages/core/src/types/options.ts Outdated
* @default false
* @experimental
*/
useSentryTracerProvider?: boolean;

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.

should this live in core? is this not a node-specific option?

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.

also, why experimental? this can just be a regular option, and as discussed I'd actually make it opt-out (or more specifically, make the default dynamic based on if any options are set that require the more fully features tracer, e.g. spanProcessors)

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.

Wouldn't this be a breaking change? I thought the opt-out would be rather for v11 and in v10 it's opt-in

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

No, it's not a breaking change. The only case where it would be breaking is if people added span processors, but in that case we default back to the otel sdk tracer provider.

@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from 06f6f64 to 6759aa8CompareJune 23, 2026 19:13
Outside of span streaming, an outgoing fetch (`http.client`) span with no local
parent is no longer recorded as a standalone transaction — the downstream
sampling decision is left to the server. This is enforced via `onlyIfParent`,
which still creates a non-recording span so trace propagation headers are
injected.
This rule already lives in `SentrySampler`, but that only runs when an
OpenTelemetry SDK tracer provider is set up. Enforcing it in the instrumentation
makes it hold for the `SentryTracerProvider` and for SDKs that don't use an
OpenTelemetry tracer provider at all. The sampler rule is kept for OpenTelemetry
SDK / custom OpenTelemetry setups.
The transaction is assembled synchronously from the live span tree when the root
span ends, dropping child spans whose instrumentation closes them after it - in the
same tick (diagnostics-channel `asyncEnd`) or on a later tick (e.g. prisma engine
spans). A per-client debounced timer (the one the OpenTelemetry span exporter uses)
delays the snapshot so those children land first, and drains on the client `flush`
hook so `Sentry.flush()` / `close()` stays safe.
Enabled on the NodeClient rather than the SentryTracerProvider so it applies with or
without a tracer provider; the browser keeps its synchronous capture.
Under the SentryTracerProvider, streamed spans carry `sentry.origin` as a
first-class attribute including the default `manual` value, whereas the
OpenTelemetry SDK path omits the `manual` default. The `mysql` (v1) db spans
and the `pg.connect` span set no explicit origin, so they surface as
`manual` here.
Assert it for now. When those instrumentations are reworked to set an
explicit `auto.db.otel.*` origin (e.g. #21568 for mysql), these expectations
will be updated to the real origin then.
These assert prisma's engine spans (replayed asynchronously by
`@prisma/instrumentation`), which the SentryTracerProvider drops because it
assembles transactions synchronously on root-span end with no SpanExporter
buffer to wait for late children. They pass on the OpenTelemetry SDK
(`BasicTracerProvider`) path. Skip them here until the general
"complete span-tree capture without a SpanExporter" follow-up lands; v7 is
left enabled as it currently captures the engine spans in time.
Re-enabled now that the streamlined fastify integration (#21706) names spans at
creation instead of renaming via updateName(), so the SentryTracerProvider no
longer stamps sentry.source: 'custom'. Verified locally via e2e (11/11 pass each).
Moves the _INTERNAL_setDeferSegmentSpanCapture call out of initOtel (which only runs
on Sentry.init and only wires the first client) into the NodeClient constructor, which
runs for every client — first, second, or manually constructed — so each defers correctly.
Sign up for freeto 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.

6 participants

@andreiborza@mydea@Lms24@JPeer264@chargome@nicohrubec
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(node): Wire up SentryTracerProvider - #21680

Merged
andreiborza merged 29 commits into
ab/sentry-trace-provider-sealfrom
ab/sentry-trace-provider-node
Jul 2, 2026
Merged

feat(node): Wire up SentryTracerProvider#21680
andreiborza merged 29 commits into
ab/sentry-trace-provider-sealfrom
ab/sentry-trace-provider-node

Conversation

@andreiborza

@andreiborzaandreiborza commented Jun 22, 2026

Copy link
Copy Markdown
Member

What

Makes Sentry's minimal SentryTracerProvider the default OpenTelemetry tracer provider for @sentry/node, replacing the full OTel SDK BasicTracerProvider. The SDK provider stays available via the new openTelemetryBasicTracerProvider option (and is used automatically when custom openTelemetrySpanProcessors are provided).

Why

The minimal provider creates native Sentry spans and skips the OTel SDK span pipeline. This PR turns it on for Node and re-homes the work that pipeline used to do (op/name/source/status inference, resource and response context, the orphan-fetch and status-code rules) onto client hooks and the instrumentation, so it holds without an OTel SDK provider present. BasicTracerProvider remains for setups that rely on OTel SDK features the minimal provider does not run.

@andreiborza
andreiborza requested a review from a team as a code ownerJune 22, 2026 09:35
@andreiborza
andreiborza requested review from JPeer264 and mydea and removed request for a teamJune 22, 2026 09:35
@nicohrubec
nicohrubec self-requested a review June 22, 2026 09:46
@github-actions

github-actionsBot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser27.59 kB--
@sentry/browser - with treeshaking flags26.03 kB--
@sentry/browser (incl. Tracing)46.24 kB+0.46%+209 B 🔺
@sentry/browser (incl. Tracing + Span Streaming)48 kB+0.47%+222 B 🔺
@sentry/browser (incl. Tracing, Profiling)51.01 kB+0.41%+205 B 🔺
@sentry/browser (incl. Tracing, Replay)85.49 kB+0.27%+224 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags75.1 kB+0.31%+225 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas)90.19 kB+0.26%+232 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback)102.84 kB+0.23%+226 B 🔺
@sentry/browser (incl. Feedback)44.76 kB--
@sentry/browser (incl. sendFeedback)32.38 kB--
@sentry/browser (incl. FeedbackAsync)37.51 kB--
@sentry/browser (incl. Metrics)28.67 kB--
@sentry/browser (incl. Logs)28.91 kB--
@sentry/browser (incl. Metrics & Logs)29.59 kB--
@sentry/react29.38 kB--
@sentry/react (incl. Tracing)48.51 kB+0.35%+169 B 🔺
@sentry/vue33.03 kB+0.64%+207 B 🔺
@sentry/vue (incl. Tracing)48.11 kB+0.43%+205 B 🔺
@sentry/svelte27.61 kB--
CDN Bundle30 kB--
CDN Bundle (incl. Tracing)48.22 kB+0.46%+219 B 🔺
CDN Bundle (incl. Logs, Metrics)31.57 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)49.53 kB+0.4%+194 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics)70.77 kB--
CDN Bundle (incl. Tracing, Replay)85.68 kB+0.24%+200 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)86.97 kB+0.24%+204 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback)91.5 kB+0.22%+200 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.77 kB+0.24%+222 B 🔺
CDN Bundle - uncompressed89.35 kB--
CDN Bundle (incl. Tracing) - uncompressed145.92 kB+0.45%+649 B 🔺
CDN Bundle (incl. Logs, Metrics) - uncompressed94.05 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed149.89 kB+0.44%+649 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed218.59 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed264.93 kB+0.25%+649 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed268.89 kB+0.25%+649 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed278.63 kB+0.24%+651 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed282.58 kB+0.24%+651 B 🔺
@sentry/nextjs (client)50.95 kB+0.43%+218 B 🔺
@sentry/sveltekit (client)46.65 kB+0.49%+226 B 🔺
@sentry/core/server78.28 kB+0.72%+552 B 🔺
@sentry/core/browser64.62 kB+0.88%+563 B 🔺
@sentry/node-core63.17 kB+1.28%+796 B 🔺
@sentry/node122.76 kB+1.27%+1.54 kB 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)69.95 kB--
@sentry/node/light50.7 kB+0.47%+233 B 🔺
@sentry/node - without tracing74.22 kB+2.11%+1.53 kB 🔺
@sentry/aws-serverless85.01 kB+1.73%+1.45 kB 🔺
@sentry/cloudflare (withSentry) - minified181.52 kB+0.5%+900 B 🔺
@sentry/cloudflare (withSentry)449.12 kB+0.52%+2.29 kB 🔺

View base workflow run

@andreiborzaandreiborza changed the title feat(node): Wire up _experiments.useSentryTracerProviderfeat(node): Wire up SentryTracerProviderJun 22, 2026
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from e05567e to dc3cd9dCompareJune 22, 2026 11:32
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from dc3cd9d to f3c0c65CompareJune 22, 2026 11:49
Comment threadpackages/node/src/sdk/initOtel.ts Outdated
Comment threadpackages/node/src/sdk/initOtel.ts
Comment threadpackages/node/src/sdk/initOtel.ts Outdated
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from e200c8f to 502dca9CompareJune 22, 2026 18:20
Comment threadpackages/node/src/sdk/initOtel.ts
Comment threadpackages/node/src/sdk/initOtel.ts
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from 502dca9 to 6ae8302CompareJune 22, 2026 18:30
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from 6ae8302 to e2f91c0CompareJune 22, 2026 18:46
Comment threadpackages/node/test/sdk/init.test.ts
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch 2 times, most recently from 4a3010c to d2384e8CompareJune 22, 2026 22:34
Comment threadpackages/node/src/sdk/initOtel.ts
Comment threadpackages/core/src/types/options.ts Outdated
* @default false
* @experimental
*/
useSentryTracerProvider?: boolean;

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.

should this live in core? is this not a node-specific option?

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.

also, why experimental? this can just be a regular option, and as discussed I'd actually make it opt-out (or more specifically, make the default dynamic based on if any options are set that require the more fully features tracer, e.g. spanProcessors)

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.

Wouldn't this be a breaking change? I thought the opt-out would be rather for v11 and in v10 it's opt-in

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

No, it's not a breaking change. The only case where it would be breaking is if people added span processors, but in that case we default back to the otel sdk tracer provider.

@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from 06f6f64 to 6759aa8CompareJune 23, 2026 19:13
Outside of span streaming, an outgoing fetch (`http.client`) span with no local
parent is no longer recorded as a standalone transaction — the downstream
sampling decision is left to the server. This is enforced via `onlyIfParent`,
which still creates a non-recording span so trace propagation headers are
injected.
This rule already lives in `SentrySampler`, but that only runs when an
OpenTelemetry SDK tracer provider is set up. Enforcing it in the instrumentation
makes it hold for the `SentryTracerProvider` and for SDKs that don't use an
OpenTelemetry tracer provider at all. The sampler rule is kept for OpenTelemetry
SDK / custom OpenTelemetry setups.
The transaction is assembled synchronously from the live span tree when the root
span ends, dropping child spans whose instrumentation closes them after it - in the
same tick (diagnostics-channel `asyncEnd`) or on a later tick (e.g. prisma engine
spans). A per-client debounced timer (the one the OpenTelemetry span exporter uses)
delays the snapshot so those children land first, and drains on the client `flush`
hook so `Sentry.flush()` / `close()` stays safe.
Enabled on the NodeClient rather than the SentryTracerProvider so it applies with or
without a tracer provider; the browser keeps its synchronous capture.
Under the SentryTracerProvider, streamed spans carry `sentry.origin` as a
first-class attribute including the default `manual` value, whereas the
OpenTelemetry SDK path omits the `manual` default. The `mysql` (v1) db spans
and the `pg.connect` span set no explicit origin, so they surface as
`manual` here.
Assert it for now. When those instrumentations are reworked to set an
explicit `auto.db.otel.*` origin (e.g. #21568 for mysql), these expectations
will be updated to the real origin then.
These assert prisma's engine spans (replayed asynchronously by
`@prisma/instrumentation`), which the SentryTracerProvider drops because it
assembles transactions synchronously on root-span end with no SpanExporter
buffer to wait for late children. They pass on the OpenTelemetry SDK
(`BasicTracerProvider`) path. Skip them here until the general
"complete span-tree capture without a SpanExporter" follow-up lands; v7 is
left enabled as it currently captures the engine spans in time.
Re-enabled now that the streamlined fastify integration (#21706) names spans at
creation instead of renaming via updateName(), so the SentryTracerProvider no
longer stamps sentry.source: 'custom'. Verified locally via e2e (11/11 pass each).
Moves the _INTERNAL_setDeferSegmentSpanCapture call out of initOtel (which only runs
on Sentry.init and only wires the first client) into the NodeClient constructor, which
runs for every client — first, second, or manually constructed — so each defers correctly.
Sign up for freeto 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.

6 participants

@andreiborza@mydea@Lms24@JPeer264@chargome@nicohrubec
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(node): Wire up SentryTracerProvider - #21680

Merged
andreiborza merged 29 commits into
ab/sentry-trace-provider-sealfrom
ab/sentry-trace-provider-node
Jul 2, 2026
Merged

feat(node): Wire up SentryTracerProvider#21680
andreiborza merged 29 commits into
ab/sentry-trace-provider-sealfrom
ab/sentry-trace-provider-node

Conversation

@andreiborza

@andreiborzaandreiborza commented Jun 22, 2026

Copy link
Copy Markdown
Member

What

Makes Sentry's minimal SentryTracerProvider the default OpenTelemetry tracer provider for @sentry/node, replacing the full OTel SDK BasicTracerProvider. The SDK provider stays available via the new openTelemetryBasicTracerProvider option (and is used automatically when custom openTelemetrySpanProcessors are provided).

Why

The minimal provider creates native Sentry spans and skips the OTel SDK span pipeline. This PR turns it on for Node and re-homes the work that pipeline used to do (op/name/source/status inference, resource and response context, the orphan-fetch and status-code rules) onto client hooks and the instrumentation, so it holds without an OTel SDK provider present. BasicTracerProvider remains for setups that rely on OTel SDK features the minimal provider does not run.

@andreiborza
andreiborza requested a review from a team as a code ownerJune 22, 2026 09:35
@andreiborza
andreiborza requested review from JPeer264 and mydea and removed request for a teamJune 22, 2026 09:35
@nicohrubec
nicohrubec self-requested a review June 22, 2026 09:46
@github-actions

github-actionsBot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser27.59 kB--
@sentry/browser - with treeshaking flags26.03 kB--
@sentry/browser (incl. Tracing)46.24 kB+0.46%+209 B 🔺
@sentry/browser (incl. Tracing + Span Streaming)48 kB+0.47%+222 B 🔺
@sentry/browser (incl. Tracing, Profiling)51.01 kB+0.41%+205 B 🔺
@sentry/browser (incl. Tracing, Replay)85.49 kB+0.27%+224 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags75.1 kB+0.31%+225 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas)90.19 kB+0.26%+232 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback)102.84 kB+0.23%+226 B 🔺
@sentry/browser (incl. Feedback)44.76 kB--
@sentry/browser (incl. sendFeedback)32.38 kB--
@sentry/browser (incl. FeedbackAsync)37.51 kB--
@sentry/browser (incl. Metrics)28.67 kB--
@sentry/browser (incl. Logs)28.91 kB--
@sentry/browser (incl. Metrics & Logs)29.59 kB--
@sentry/react29.38 kB--
@sentry/react (incl. Tracing)48.51 kB+0.35%+169 B 🔺
@sentry/vue33.03 kB+0.64%+207 B 🔺
@sentry/vue (incl. Tracing)48.11 kB+0.43%+205 B 🔺
@sentry/svelte27.61 kB--
CDN Bundle30 kB--
CDN Bundle (incl. Tracing)48.22 kB+0.46%+219 B 🔺
CDN Bundle (incl. Logs, Metrics)31.57 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)49.53 kB+0.4%+194 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics)70.77 kB--
CDN Bundle (incl. Tracing, Replay)85.68 kB+0.24%+200 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)86.97 kB+0.24%+204 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback)91.5 kB+0.22%+200 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.77 kB+0.24%+222 B 🔺
CDN Bundle - uncompressed89.35 kB--
CDN Bundle (incl. Tracing) - uncompressed145.92 kB+0.45%+649 B 🔺
CDN Bundle (incl. Logs, Metrics) - uncompressed94.05 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed149.89 kB+0.44%+649 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed218.59 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed264.93 kB+0.25%+649 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed268.89 kB+0.25%+649 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed278.63 kB+0.24%+651 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed282.58 kB+0.24%+651 B 🔺
@sentry/nextjs (client)50.95 kB+0.43%+218 B 🔺
@sentry/sveltekit (client)46.65 kB+0.49%+226 B 🔺
@sentry/core/server78.28 kB+0.72%+552 B 🔺
@sentry/core/browser64.62 kB+0.88%+563 B 🔺
@sentry/node-core63.17 kB+1.28%+796 B 🔺
@sentry/node122.76 kB+1.27%+1.54 kB 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)69.95 kB--
@sentry/node/light50.7 kB+0.47%+233 B 🔺
@sentry/node - without tracing74.22 kB+2.11%+1.53 kB 🔺
@sentry/aws-serverless85.01 kB+1.73%+1.45 kB 🔺
@sentry/cloudflare (withSentry) - minified181.52 kB+0.5%+900 B 🔺
@sentry/cloudflare (withSentry)449.12 kB+0.52%+2.29 kB 🔺

View base workflow run

@andreiborzaandreiborza changed the title feat(node): Wire up _experiments.useSentryTracerProviderfeat(node): Wire up SentryTracerProviderJun 22, 2026
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from e05567e to dc3cd9dCompareJune 22, 2026 11:32
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from dc3cd9d to f3c0c65CompareJune 22, 2026 11:49
Comment threadpackages/node/src/sdk/initOtel.ts Outdated
Comment threadpackages/node/src/sdk/initOtel.ts
Comment threadpackages/node/src/sdk/initOtel.ts Outdated
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from e200c8f to 502dca9CompareJune 22, 2026 18:20
Comment threadpackages/node/src/sdk/initOtel.ts
Comment threadpackages/node/src/sdk/initOtel.ts
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from 502dca9 to 6ae8302CompareJune 22, 2026 18:30
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from 6ae8302 to e2f91c0CompareJune 22, 2026 18:46
Comment threadpackages/node/test/sdk/init.test.ts
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch 2 times, most recently from 4a3010c to d2384e8CompareJune 22, 2026 22:34
Comment threadpackages/node/src/sdk/initOtel.ts
Comment threadpackages/core/src/types/options.ts Outdated
* @default false
* @experimental
*/
useSentryTracerProvider?: boolean;

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.

should this live in core? is this not a node-specific option?

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.

also, why experimental? this can just be a regular option, and as discussed I'd actually make it opt-out (or more specifically, make the default dynamic based on if any options are set that require the more fully features tracer, e.g. spanProcessors)

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.

Wouldn't this be a breaking change? I thought the opt-out would be rather for v11 and in v10 it's opt-in

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

No, it's not a breaking change. The only case where it would be breaking is if people added span processors, but in that case we default back to the otel sdk tracer provider.

@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from 06f6f64 to 6759aa8CompareJune 23, 2026 19:13
Outside of span streaming, an outgoing fetch (`http.client`) span with no local
parent is no longer recorded as a standalone transaction — the downstream
sampling decision is left to the server. This is enforced via `onlyIfParent`,
which still creates a non-recording span so trace propagation headers are
injected.
This rule already lives in `SentrySampler`, but that only runs when an
OpenTelemetry SDK tracer provider is set up. Enforcing it in the instrumentation
makes it hold for the `SentryTracerProvider` and for SDKs that don't use an
OpenTelemetry tracer provider at all. The sampler rule is kept for OpenTelemetry
SDK / custom OpenTelemetry setups.
The transaction is assembled synchronously from the live span tree when the root
span ends, dropping child spans whose instrumentation closes them after it - in the
same tick (diagnostics-channel `asyncEnd`) or on a later tick (e.g. prisma engine
spans). A per-client debounced timer (the one the OpenTelemetry span exporter uses)
delays the snapshot so those children land first, and drains on the client `flush`
hook so `Sentry.flush()` / `close()` stays safe.
Enabled on the NodeClient rather than the SentryTracerProvider so it applies with or
without a tracer provider; the browser keeps its synchronous capture.
Under the SentryTracerProvider, streamed spans carry `sentry.origin` as a
first-class attribute including the default `manual` value, whereas the
OpenTelemetry SDK path omits the `manual` default. The `mysql` (v1) db spans
and the `pg.connect` span set no explicit origin, so they surface as
`manual` here.
Assert it for now. When those instrumentations are reworked to set an
explicit `auto.db.otel.*` origin (e.g. #21568 for mysql), these expectations
will be updated to the real origin then.
These assert prisma's engine spans (replayed asynchronously by
`@prisma/instrumentation`), which the SentryTracerProvider drops because it
assembles transactions synchronously on root-span end with no SpanExporter
buffer to wait for late children. They pass on the OpenTelemetry SDK
(`BasicTracerProvider`) path. Skip them here until the general
"complete span-tree capture without a SpanExporter" follow-up lands; v7 is
left enabled as it currently captures the engine spans in time.
Re-enabled now that the streamlined fastify integration (#21706) names spans at
creation instead of renaming via updateName(), so the SentryTracerProvider no
longer stamps sentry.source: 'custom'. Verified locally via e2e (11/11 pass each).
Moves the _INTERNAL_setDeferSegmentSpanCapture call out of initOtel (which only runs
on Sentry.init and only wires the first client) into the NodeClient constructor, which
runs for every client — first, second, or manually constructed — so each defers correctly.
Sign up for freeto 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.

6 participants

@andreiborza@mydea@Lms24@JPeer264@chargome@nicohrubec
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat(node): Wire up SentryTracerProvider - #21680

Merged
andreiborza merged 29 commits into
ab/sentry-trace-provider-sealfrom
ab/sentry-trace-provider-node
Jul 2, 2026
Merged

feat(node): Wire up SentryTracerProvider#21680
andreiborza merged 29 commits into
ab/sentry-trace-provider-sealfrom
ab/sentry-trace-provider-node

Conversation

@andreiborza

@andreiborzaandreiborza commented Jun 22, 2026

Copy link
Copy Markdown
Member

What

Makes Sentry's minimal SentryTracerProvider the default OpenTelemetry tracer provider for @sentry/node, replacing the full OTel SDK BasicTracerProvider. The SDK provider stays available via the new openTelemetryBasicTracerProvider option (and is used automatically when custom openTelemetrySpanProcessors are provided).

Why

The minimal provider creates native Sentry spans and skips the OTel SDK span pipeline. This PR turns it on for Node and re-homes the work that pipeline used to do (op/name/source/status inference, resource and response context, the orphan-fetch and status-code rules) onto client hooks and the instrumentation, so it holds without an OTel SDK provider present. BasicTracerProvider remains for setups that rely on OTel SDK features the minimal provider does not run.

@andreiborza
andreiborza requested a review from a team as a code ownerJune 22, 2026 09:35
@andreiborza
andreiborza requested review from JPeer264 and mydea and removed request for a teamJune 22, 2026 09:35
@nicohrubec
nicohrubec self-requested a review June 22, 2026 09:46
@github-actions

github-actionsBot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser27.59 kB--
@sentry/browser - with treeshaking flags26.03 kB--
@sentry/browser (incl. Tracing)46.24 kB+0.46%+209 B 🔺
@sentry/browser (incl. Tracing + Span Streaming)48 kB+0.47%+222 B 🔺
@sentry/browser (incl. Tracing, Profiling)51.01 kB+0.41%+205 B 🔺
@sentry/browser (incl. Tracing, Replay)85.49 kB+0.27%+224 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags75.1 kB+0.31%+225 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas)90.19 kB+0.26%+232 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback)102.84 kB+0.23%+226 B 🔺
@sentry/browser (incl. Feedback)44.76 kB--
@sentry/browser (incl. sendFeedback)32.38 kB--
@sentry/browser (incl. FeedbackAsync)37.51 kB--
@sentry/browser (incl. Metrics)28.67 kB--
@sentry/browser (incl. Logs)28.91 kB--
@sentry/browser (incl. Metrics & Logs)29.59 kB--
@sentry/react29.38 kB--
@sentry/react (incl. Tracing)48.51 kB+0.35%+169 B 🔺
@sentry/vue33.03 kB+0.64%+207 B 🔺
@sentry/vue (incl. Tracing)48.11 kB+0.43%+205 B 🔺
@sentry/svelte27.61 kB--
CDN Bundle30 kB--
CDN Bundle (incl. Tracing)48.22 kB+0.46%+219 B 🔺
CDN Bundle (incl. Logs, Metrics)31.57 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)49.53 kB+0.4%+194 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics)70.77 kB--
CDN Bundle (incl. Tracing, Replay)85.68 kB+0.24%+200 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)86.97 kB+0.24%+204 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback)91.5 kB+0.22%+200 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.77 kB+0.24%+222 B 🔺
CDN Bundle - uncompressed89.35 kB--
CDN Bundle (incl. Tracing) - uncompressed145.92 kB+0.45%+649 B 🔺
CDN Bundle (incl. Logs, Metrics) - uncompressed94.05 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed149.89 kB+0.44%+649 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed218.59 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed264.93 kB+0.25%+649 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed268.89 kB+0.25%+649 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed278.63 kB+0.24%+651 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed282.58 kB+0.24%+651 B 🔺
@sentry/nextjs (client)50.95 kB+0.43%+218 B 🔺
@sentry/sveltekit (client)46.65 kB+0.49%+226 B 🔺
@sentry/core/server78.28 kB+0.72%+552 B 🔺
@sentry/core/browser64.62 kB+0.88%+563 B 🔺
@sentry/node-core63.17 kB+1.28%+796 B 🔺
@sentry/node122.76 kB+1.27%+1.54 kB 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)69.95 kB--
@sentry/node/light50.7 kB+0.47%+233 B 🔺
@sentry/node - without tracing74.22 kB+2.11%+1.53 kB 🔺
@sentry/aws-serverless85.01 kB+1.73%+1.45 kB 🔺
@sentry/cloudflare (withSentry) - minified181.52 kB+0.5%+900 B 🔺
@sentry/cloudflare (withSentry)449.12 kB+0.52%+2.29 kB 🔺

View base workflow run

@andreiborzaandreiborza changed the title feat(node): Wire up _experiments.useSentryTracerProviderfeat(node): Wire up SentryTracerProviderJun 22, 2026
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from e05567e to dc3cd9dCompareJune 22, 2026 11:32
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from dc3cd9d to f3c0c65CompareJune 22, 2026 11:49
Comment threadpackages/node/src/sdk/initOtel.ts Outdated
Comment threadpackages/node/src/sdk/initOtel.ts
Comment threadpackages/node/src/sdk/initOtel.ts Outdated
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from e200c8f to 502dca9CompareJune 22, 2026 18:20
Comment threadpackages/node/src/sdk/initOtel.ts
Comment threadpackages/node/src/sdk/initOtel.ts
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from 502dca9 to 6ae8302CompareJune 22, 2026 18:30
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from 6ae8302 to e2f91c0CompareJune 22, 2026 18:46
Comment threadpackages/node/test/sdk/init.test.ts
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch 2 times, most recently from 4a3010c to d2384e8CompareJune 22, 2026 22:34
Comment threadpackages/node/src/sdk/initOtel.ts
Comment threadpackages/core/src/types/options.ts Outdated
* @default false
* @experimental
*/
useSentryTracerProvider?: boolean;

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.

should this live in core? is this not a node-specific option?

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.

also, why experimental? this can just be a regular option, and as discussed I'd actually make it opt-out (or more specifically, make the default dynamic based on if any options are set that require the more fully features tracer, e.g. spanProcessors)

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.

Wouldn't this be a breaking change? I thought the opt-out would be rather for v11 and in v10 it's opt-in

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

No, it's not a breaking change. The only case where it would be breaking is if people added span processors, but in that case we default back to the otel sdk tracer provider.

@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from 06f6f64 to 6759aa8CompareJune 23, 2026 19:13
Outside of span streaming, an outgoing fetch (`http.client`) span with no local
parent is no longer recorded as a standalone transaction — the downstream
sampling decision is left to the server. This is enforced via `onlyIfParent`,
which still creates a non-recording span so trace propagation headers are
injected.
This rule already lives in `SentrySampler`, but that only runs when an
OpenTelemetry SDK tracer provider is set up. Enforcing it in the instrumentation
makes it hold for the `SentryTracerProvider` and for SDKs that don't use an
OpenTelemetry tracer provider at all. The sampler rule is kept for OpenTelemetry
SDK / custom OpenTelemetry setups.
The transaction is assembled synchronously from the live span tree when the root
span ends, dropping child spans whose instrumentation closes them after it - in the
same tick (diagnostics-channel `asyncEnd`) or on a later tick (e.g. prisma engine
spans). A per-client debounced timer (the one the OpenTelemetry span exporter uses)
delays the snapshot so those children land first, and drains on the client `flush`
hook so `Sentry.flush()` / `close()` stays safe.
Enabled on the NodeClient rather than the SentryTracerProvider so it applies with or
without a tracer provider; the browser keeps its synchronous capture.
Under the SentryTracerProvider, streamed spans carry `sentry.origin` as a
first-class attribute including the default `manual` value, whereas the
OpenTelemetry SDK path omits the `manual` default. The `mysql` (v1) db spans
and the `pg.connect` span set no explicit origin, so they surface as
`manual` here.
Assert it for now. When those instrumentations are reworked to set an
explicit `auto.db.otel.*` origin (e.g. #21568 for mysql), these expectations
will be updated to the real origin then.
These assert prisma's engine spans (replayed asynchronously by
`@prisma/instrumentation`), which the SentryTracerProvider drops because it
assembles transactions synchronously on root-span end with no SpanExporter
buffer to wait for late children. They pass on the OpenTelemetry SDK
(`BasicTracerProvider`) path. Skip them here until the general
"complete span-tree capture without a SpanExporter" follow-up lands; v7 is
left enabled as it currently captures the engine spans in time.
Re-enabled now that the streamlined fastify integration (#21706) names spans at
creation instead of renaming via updateName(), so the SentryTracerProvider no
longer stamps sentry.source: 'custom'. Verified locally via e2e (11/11 pass each).
Moves the _INTERNAL_setDeferSegmentSpanCapture call out of initOtel (which only runs
on Sentry.init and only wires the first client) into the NodeClient constructor, which
runs for every client — first, second, or manually constructed — so each defers correctly.
Sign up for freeto 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.

6 participants

@andreiborza@mydea@Lms24@JPeer264@chargome@nicohrubec
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(node): Wire up SentryTracerProvider - #21680

Merged
andreiborza merged 29 commits into
ab/sentry-trace-provider-sealfrom
ab/sentry-trace-provider-node
Jul 2, 2026
Merged

feat(node): Wire up SentryTracerProvider#21680
andreiborza merged 29 commits into
ab/sentry-trace-provider-sealfrom
ab/sentry-trace-provider-node

Conversation

@andreiborza

@andreiborzaandreiborza commented Jun 22, 2026

Copy link
Copy Markdown
Member

What

Makes Sentry's minimal SentryTracerProvider the default OpenTelemetry tracer provider for @sentry/node, replacing the full OTel SDK BasicTracerProvider. The SDK provider stays available via the new openTelemetryBasicTracerProvider option (and is used automatically when custom openTelemetrySpanProcessors are provided).

Why

The minimal provider creates native Sentry spans and skips the OTel SDK span pipeline. This PR turns it on for Node and re-homes the work that pipeline used to do (op/name/source/status inference, resource and response context, the orphan-fetch and status-code rules) onto client hooks and the instrumentation, so it holds without an OTel SDK provider present. BasicTracerProvider remains for setups that rely on OTel SDK features the minimal provider does not run.

@andreiborza
andreiborza requested a review from a team as a code ownerJune 22, 2026 09:35
@andreiborza
andreiborza requested review from JPeer264 and mydea and removed request for a teamJune 22, 2026 09:35
@nicohrubec
nicohrubec self-requested a review June 22, 2026 09:46
@github-actions

github-actionsBot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser27.59 kB--
@sentry/browser - with treeshaking flags26.03 kB--
@sentry/browser (incl. Tracing)46.24 kB+0.46%+209 B 🔺
@sentry/browser (incl. Tracing + Span Streaming)48 kB+0.47%+222 B 🔺
@sentry/browser (incl. Tracing, Profiling)51.01 kB+0.41%+205 B 🔺
@sentry/browser (incl. Tracing, Replay)85.49 kB+0.27%+224 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags75.1 kB+0.31%+225 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas)90.19 kB+0.26%+232 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback)102.84 kB+0.23%+226 B 🔺
@sentry/browser (incl. Feedback)44.76 kB--
@sentry/browser (incl. sendFeedback)32.38 kB--
@sentry/browser (incl. FeedbackAsync)37.51 kB--
@sentry/browser (incl. Metrics)28.67 kB--
@sentry/browser (incl. Logs)28.91 kB--
@sentry/browser (incl. Metrics & Logs)29.59 kB--
@sentry/react29.38 kB--
@sentry/react (incl. Tracing)48.51 kB+0.35%+169 B 🔺
@sentry/vue33.03 kB+0.64%+207 B 🔺
@sentry/vue (incl. Tracing)48.11 kB+0.43%+205 B 🔺
@sentry/svelte27.61 kB--
CDN Bundle30 kB--
CDN Bundle (incl. Tracing)48.22 kB+0.46%+219 B 🔺
CDN Bundle (incl. Logs, Metrics)31.57 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)49.53 kB+0.4%+194 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics)70.77 kB--
CDN Bundle (incl. Tracing, Replay)85.68 kB+0.24%+200 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)86.97 kB+0.24%+204 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback)91.5 kB+0.22%+200 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.77 kB+0.24%+222 B 🔺
CDN Bundle - uncompressed89.35 kB--
CDN Bundle (incl. Tracing) - uncompressed145.92 kB+0.45%+649 B 🔺
CDN Bundle (incl. Logs, Metrics) - uncompressed94.05 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed149.89 kB+0.44%+649 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed218.59 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed264.93 kB+0.25%+649 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed268.89 kB+0.25%+649 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed278.63 kB+0.24%+651 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed282.58 kB+0.24%+651 B 🔺
@sentry/nextjs (client)50.95 kB+0.43%+218 B 🔺
@sentry/sveltekit (client)46.65 kB+0.49%+226 B 🔺
@sentry/core/server78.28 kB+0.72%+552 B 🔺
@sentry/core/browser64.62 kB+0.88%+563 B 🔺
@sentry/node-core63.17 kB+1.28%+796 B 🔺
@sentry/node122.76 kB+1.27%+1.54 kB 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)69.95 kB--
@sentry/node/light50.7 kB+0.47%+233 B 🔺
@sentry/node - without tracing74.22 kB+2.11%+1.53 kB 🔺
@sentry/aws-serverless85.01 kB+1.73%+1.45 kB 🔺
@sentry/cloudflare (withSentry) - minified181.52 kB+0.5%+900 B 🔺
@sentry/cloudflare (withSentry)449.12 kB+0.52%+2.29 kB 🔺

View base workflow run

@andreiborzaandreiborza changed the title feat(node): Wire up _experiments.useSentryTracerProviderfeat(node): Wire up SentryTracerProviderJun 22, 2026
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from e05567e to dc3cd9dCompareJune 22, 2026 11:32
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from dc3cd9d to f3c0c65CompareJune 22, 2026 11:49
Comment threadpackages/node/src/sdk/initOtel.ts Outdated
Comment threadpackages/node/src/sdk/initOtel.ts
Comment threadpackages/node/src/sdk/initOtel.ts Outdated
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from e200c8f to 502dca9CompareJune 22, 2026 18:20
Comment threadpackages/node/src/sdk/initOtel.ts
Comment threadpackages/node/src/sdk/initOtel.ts
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from 502dca9 to 6ae8302CompareJune 22, 2026 18:30
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from 6ae8302 to e2f91c0CompareJune 22, 2026 18:46
Comment threadpackages/node/test/sdk/init.test.ts
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch 2 times, most recently from 4a3010c to d2384e8CompareJune 22, 2026 22:34
Comment threadpackages/node/src/sdk/initOtel.ts
Comment threadpackages/core/src/types/options.ts Outdated
* @default false
* @experimental
*/
useSentryTracerProvider?: boolean;

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.

should this live in core? is this not a node-specific option?

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.

also, why experimental? this can just be a regular option, and as discussed I'd actually make it opt-out (or more specifically, make the default dynamic based on if any options are set that require the more fully features tracer, e.g. spanProcessors)

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.

Wouldn't this be a breaking change? I thought the opt-out would be rather for v11 and in v10 it's opt-in

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

No, it's not a breaking change. The only case where it would be breaking is if people added span processors, but in that case we default back to the otel sdk tracer provider.

@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from 06f6f64 to 6759aa8CompareJune 23, 2026 19:13
Outside of span streaming, an outgoing fetch (`http.client`) span with no local
parent is no longer recorded as a standalone transaction — the downstream
sampling decision is left to the server. This is enforced via `onlyIfParent`,
which still creates a non-recording span so trace propagation headers are
injected.
This rule already lives in `SentrySampler`, but that only runs when an
OpenTelemetry SDK tracer provider is set up. Enforcing it in the instrumentation
makes it hold for the `SentryTracerProvider` and for SDKs that don't use an
OpenTelemetry tracer provider at all. The sampler rule is kept for OpenTelemetry
SDK / custom OpenTelemetry setups.
The transaction is assembled synchronously from the live span tree when the root
span ends, dropping child spans whose instrumentation closes them after it - in the
same tick (diagnostics-channel `asyncEnd`) or on a later tick (e.g. prisma engine
spans). A per-client debounced timer (the one the OpenTelemetry span exporter uses)
delays the snapshot so those children land first, and drains on the client `flush`
hook so `Sentry.flush()` / `close()` stays safe.
Enabled on the NodeClient rather than the SentryTracerProvider so it applies with or
without a tracer provider; the browser keeps its synchronous capture.
Under the SentryTracerProvider, streamed spans carry `sentry.origin` as a
first-class attribute including the default `manual` value, whereas the
OpenTelemetry SDK path omits the `manual` default. The `mysql` (v1) db spans
and the `pg.connect` span set no explicit origin, so they surface as
`manual` here.
Assert it for now. When those instrumentations are reworked to set an
explicit `auto.db.otel.*` origin (e.g. #21568 for mysql), these expectations
will be updated to the real origin then.
These assert prisma's engine spans (replayed asynchronously by
`@prisma/instrumentation`), which the SentryTracerProvider drops because it
assembles transactions synchronously on root-span end with no SpanExporter
buffer to wait for late children. They pass on the OpenTelemetry SDK
(`BasicTracerProvider`) path. Skip them here until the general
"complete span-tree capture without a SpanExporter" follow-up lands; v7 is
left enabled as it currently captures the engine spans in time.
Re-enabled now that the streamlined fastify integration (#21706) names spans at
creation instead of renaming via updateName(), so the SentryTracerProvider no
longer stamps sentry.source: 'custom'. Verified locally via e2e (11/11 pass each).
Moves the _INTERNAL_setDeferSegmentSpanCapture call out of initOtel (which only runs
on Sentry.init and only wires the first client) into the NodeClient constructor, which
runs for every client — first, second, or manually constructed — so each defers correctly.
Sign up for freeto 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.

6 participants

@andreiborza@mydea@Lms24@JPeer264@chargome@nicohrubec
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(node): Wire up SentryTracerProvider - #21680

Merged
andreiborza merged 29 commits into
ab/sentry-trace-provider-sealfrom
ab/sentry-trace-provider-node
Jul 2, 2026
Merged

feat(node): Wire up SentryTracerProvider#21680
andreiborza merged 29 commits into
ab/sentry-trace-provider-sealfrom
ab/sentry-trace-provider-node

Conversation

@andreiborza

@andreiborzaandreiborza commented Jun 22, 2026

Copy link
Copy Markdown
Member

What

Makes Sentry's minimal SentryTracerProvider the default OpenTelemetry tracer provider for @sentry/node, replacing the full OTel SDK BasicTracerProvider. The SDK provider stays available via the new openTelemetryBasicTracerProvider option (and is used automatically when custom openTelemetrySpanProcessors are provided).

Why

The minimal provider creates native Sentry spans and skips the OTel SDK span pipeline. This PR turns it on for Node and re-homes the work that pipeline used to do (op/name/source/status inference, resource and response context, the orphan-fetch and status-code rules) onto client hooks and the instrumentation, so it holds without an OTel SDK provider present. BasicTracerProvider remains for setups that rely on OTel SDK features the minimal provider does not run.

@andreiborza
andreiborza requested a review from a team as a code ownerJune 22, 2026 09:35
@andreiborza
andreiborza requested review from JPeer264 and mydea and removed request for a teamJune 22, 2026 09:35
@nicohrubec
nicohrubec self-requested a review June 22, 2026 09:46
@github-actions

github-actionsBot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser27.59 kB--
@sentry/browser - with treeshaking flags26.03 kB--
@sentry/browser (incl. Tracing)46.24 kB+0.46%+209 B 🔺
@sentry/browser (incl. Tracing + Span Streaming)48 kB+0.47%+222 B 🔺
@sentry/browser (incl. Tracing, Profiling)51.01 kB+0.41%+205 B 🔺
@sentry/browser (incl. Tracing, Replay)85.49 kB+0.27%+224 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags75.1 kB+0.31%+225 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas)90.19 kB+0.26%+232 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback)102.84 kB+0.23%+226 B 🔺
@sentry/browser (incl. Feedback)44.76 kB--
@sentry/browser (incl. sendFeedback)32.38 kB--
@sentry/browser (incl. FeedbackAsync)37.51 kB--
@sentry/browser (incl. Metrics)28.67 kB--
@sentry/browser (incl. Logs)28.91 kB--
@sentry/browser (incl. Metrics & Logs)29.59 kB--
@sentry/react29.38 kB--
@sentry/react (incl. Tracing)48.51 kB+0.35%+169 B 🔺
@sentry/vue33.03 kB+0.64%+207 B 🔺
@sentry/vue (incl. Tracing)48.11 kB+0.43%+205 B 🔺
@sentry/svelte27.61 kB--
CDN Bundle30 kB--
CDN Bundle (incl. Tracing)48.22 kB+0.46%+219 B 🔺
CDN Bundle (incl. Logs, Metrics)31.57 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)49.53 kB+0.4%+194 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics)70.77 kB--
CDN Bundle (incl. Tracing, Replay)85.68 kB+0.24%+200 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)86.97 kB+0.24%+204 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback)91.5 kB+0.22%+200 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.77 kB+0.24%+222 B 🔺
CDN Bundle - uncompressed89.35 kB--
CDN Bundle (incl. Tracing) - uncompressed145.92 kB+0.45%+649 B 🔺
CDN Bundle (incl. Logs, Metrics) - uncompressed94.05 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed149.89 kB+0.44%+649 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed218.59 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed264.93 kB+0.25%+649 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed268.89 kB+0.25%+649 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed278.63 kB+0.24%+651 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed282.58 kB+0.24%+651 B 🔺
@sentry/nextjs (client)50.95 kB+0.43%+218 B 🔺
@sentry/sveltekit (client)46.65 kB+0.49%+226 B 🔺
@sentry/core/server78.28 kB+0.72%+552 B 🔺
@sentry/core/browser64.62 kB+0.88%+563 B 🔺
@sentry/node-core63.17 kB+1.28%+796 B 🔺
@sentry/node122.76 kB+1.27%+1.54 kB 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)69.95 kB--
@sentry/node/light50.7 kB+0.47%+233 B 🔺
@sentry/node - without tracing74.22 kB+2.11%+1.53 kB 🔺
@sentry/aws-serverless85.01 kB+1.73%+1.45 kB 🔺
@sentry/cloudflare (withSentry) - minified181.52 kB+0.5%+900 B 🔺
@sentry/cloudflare (withSentry)449.12 kB+0.52%+2.29 kB 🔺

View base workflow run

@andreiborzaandreiborza changed the title feat(node): Wire up _experiments.useSentryTracerProviderfeat(node): Wire up SentryTracerProviderJun 22, 2026
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from e05567e to dc3cd9dCompareJune 22, 2026 11:32
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from dc3cd9d to f3c0c65CompareJune 22, 2026 11:49
Comment threadpackages/node/src/sdk/initOtel.ts Outdated
Comment threadpackages/node/src/sdk/initOtel.ts
Comment threadpackages/node/src/sdk/initOtel.ts Outdated
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from e200c8f to 502dca9CompareJune 22, 2026 18:20
Comment threadpackages/node/src/sdk/initOtel.ts
Comment threadpackages/node/src/sdk/initOtel.ts
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from 502dca9 to 6ae8302CompareJune 22, 2026 18:30
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from 6ae8302 to e2f91c0CompareJune 22, 2026 18:46
Comment threadpackages/node/test/sdk/init.test.ts
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch 2 times, most recently from 4a3010c to d2384e8CompareJune 22, 2026 22:34
Comment threadpackages/node/src/sdk/initOtel.ts
Comment threadpackages/core/src/types/options.ts Outdated
* @default false
* @experimental
*/
useSentryTracerProvider?: boolean;

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.

should this live in core? is this not a node-specific option?

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.

also, why experimental? this can just be a regular option, and as discussed I'd actually make it opt-out (or more specifically, make the default dynamic based on if any options are set that require the more fully features tracer, e.g. spanProcessors)

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.

Wouldn't this be a breaking change? I thought the opt-out would be rather for v11 and in v10 it's opt-in

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

No, it's not a breaking change. The only case where it would be breaking is if people added span processors, but in that case we default back to the otel sdk tracer provider.

@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from 06f6f64 to 6759aa8CompareJune 23, 2026 19:13
Outside of span streaming, an outgoing fetch (`http.client`) span with no local
parent is no longer recorded as a standalone transaction — the downstream
sampling decision is left to the server. This is enforced via `onlyIfParent`,
which still creates a non-recording span so trace propagation headers are
injected.
This rule already lives in `SentrySampler`, but that only runs when an
OpenTelemetry SDK tracer provider is set up. Enforcing it in the instrumentation
makes it hold for the `SentryTracerProvider` and for SDKs that don't use an
OpenTelemetry tracer provider at all. The sampler rule is kept for OpenTelemetry
SDK / custom OpenTelemetry setups.
The transaction is assembled synchronously from the live span tree when the root
span ends, dropping child spans whose instrumentation closes them after it - in the
same tick (diagnostics-channel `asyncEnd`) or on a later tick (e.g. prisma engine
spans). A per-client debounced timer (the one the OpenTelemetry span exporter uses)
delays the snapshot so those children land first, and drains on the client `flush`
hook so `Sentry.flush()` / `close()` stays safe.
Enabled on the NodeClient rather than the SentryTracerProvider so it applies with or
without a tracer provider; the browser keeps its synchronous capture.
Under the SentryTracerProvider, streamed spans carry `sentry.origin` as a
first-class attribute including the default `manual` value, whereas the
OpenTelemetry SDK path omits the `manual` default. The `mysql` (v1) db spans
and the `pg.connect` span set no explicit origin, so they surface as
`manual` here.
Assert it for now. When those instrumentations are reworked to set an
explicit `auto.db.otel.*` origin (e.g. #21568 for mysql), these expectations
will be updated to the real origin then.
These assert prisma's engine spans (replayed asynchronously by
`@prisma/instrumentation`), which the SentryTracerProvider drops because it
assembles transactions synchronously on root-span end with no SpanExporter
buffer to wait for late children. They pass on the OpenTelemetry SDK
(`BasicTracerProvider`) path. Skip them here until the general
"complete span-tree capture without a SpanExporter" follow-up lands; v7 is
left enabled as it currently captures the engine spans in time.
Re-enabled now that the streamlined fastify integration (#21706) names spans at
creation instead of renaming via updateName(), so the SentryTracerProvider no
longer stamps sentry.source: 'custom'. Verified locally via e2e (11/11 pass each).
Moves the _INTERNAL_setDeferSegmentSpanCapture call out of initOtel (which only runs
on Sentry.init and only wires the first client) into the NodeClient constructor, which
runs for every client — first, second, or manually constructed — so each defers correctly.
Sign up for freeto 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.

6 participants

@andreiborza@mydea@Lms24@JPeer264@chargome@nicohrubec
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat(node): Wire up SentryTracerProvider - #21680

Merged
andreiborza merged 29 commits into
ab/sentry-trace-provider-sealfrom
ab/sentry-trace-provider-node
Jul 2, 2026
Merged

feat(node): Wire up SentryTracerProvider#21680
andreiborza merged 29 commits into
ab/sentry-trace-provider-sealfrom
ab/sentry-trace-provider-node

Conversation

@andreiborza

@andreiborzaandreiborza commented Jun 22, 2026

Copy link
Copy Markdown
Member

What

Makes Sentry's minimal SentryTracerProvider the default OpenTelemetry tracer provider for @sentry/node, replacing the full OTel SDK BasicTracerProvider. The SDK provider stays available via the new openTelemetryBasicTracerProvider option (and is used automatically when custom openTelemetrySpanProcessors are provided).

Why

The minimal provider creates native Sentry spans and skips the OTel SDK span pipeline. This PR turns it on for Node and re-homes the work that pipeline used to do (op/name/source/status inference, resource and response context, the orphan-fetch and status-code rules) onto client hooks and the instrumentation, so it holds without an OTel SDK provider present. BasicTracerProvider remains for setups that rely on OTel SDK features the minimal provider does not run.

@andreiborza
andreiborza requested a review from a team as a code ownerJune 22, 2026 09:35
@andreiborza
andreiborza requested review from JPeer264 and mydea and removed request for a teamJune 22, 2026 09:35
@nicohrubec
nicohrubec self-requested a review June 22, 2026 09:46
@github-actions

github-actionsBot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser27.59 kB--
@sentry/browser - with treeshaking flags26.03 kB--
@sentry/browser (incl. Tracing)46.24 kB+0.46%+209 B 🔺
@sentry/browser (incl. Tracing + Span Streaming)48 kB+0.47%+222 B 🔺
@sentry/browser (incl. Tracing, Profiling)51.01 kB+0.41%+205 B 🔺
@sentry/browser (incl. Tracing, Replay)85.49 kB+0.27%+224 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags75.1 kB+0.31%+225 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas)90.19 kB+0.26%+232 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback)102.84 kB+0.23%+226 B 🔺
@sentry/browser (incl. Feedback)44.76 kB--
@sentry/browser (incl. sendFeedback)32.38 kB--
@sentry/browser (incl. FeedbackAsync)37.51 kB--
@sentry/browser (incl. Metrics)28.67 kB--
@sentry/browser (incl. Logs)28.91 kB--
@sentry/browser (incl. Metrics & Logs)29.59 kB--
@sentry/react29.38 kB--
@sentry/react (incl. Tracing)48.51 kB+0.35%+169 B 🔺
@sentry/vue33.03 kB+0.64%+207 B 🔺
@sentry/vue (incl. Tracing)48.11 kB+0.43%+205 B 🔺
@sentry/svelte27.61 kB--
CDN Bundle30 kB--
CDN Bundle (incl. Tracing)48.22 kB+0.46%+219 B 🔺
CDN Bundle (incl. Logs, Metrics)31.57 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)49.53 kB+0.4%+194 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics)70.77 kB--
CDN Bundle (incl. Tracing, Replay)85.68 kB+0.24%+200 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)86.97 kB+0.24%+204 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback)91.5 kB+0.22%+200 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.77 kB+0.24%+222 B 🔺
CDN Bundle - uncompressed89.35 kB--
CDN Bundle (incl. Tracing) - uncompressed145.92 kB+0.45%+649 B 🔺
CDN Bundle (incl. Logs, Metrics) - uncompressed94.05 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed149.89 kB+0.44%+649 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed218.59 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed264.93 kB+0.25%+649 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed268.89 kB+0.25%+649 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed278.63 kB+0.24%+651 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed282.58 kB+0.24%+651 B 🔺
@sentry/nextjs (client)50.95 kB+0.43%+218 B 🔺
@sentry/sveltekit (client)46.65 kB+0.49%+226 B 🔺
@sentry/core/server78.28 kB+0.72%+552 B 🔺
@sentry/core/browser64.62 kB+0.88%+563 B 🔺
@sentry/node-core63.17 kB+1.28%+796 B 🔺
@sentry/node122.76 kB+1.27%+1.54 kB 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)69.95 kB--
@sentry/node/light50.7 kB+0.47%+233 B 🔺
@sentry/node - without tracing74.22 kB+2.11%+1.53 kB 🔺
@sentry/aws-serverless85.01 kB+1.73%+1.45 kB 🔺
@sentry/cloudflare (withSentry) - minified181.52 kB+0.5%+900 B 🔺
@sentry/cloudflare (withSentry)449.12 kB+0.52%+2.29 kB 🔺

View base workflow run

@andreiborzaandreiborza changed the title feat(node): Wire up _experiments.useSentryTracerProviderfeat(node): Wire up SentryTracerProviderJun 22, 2026
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from e05567e to dc3cd9dCompareJune 22, 2026 11:32
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from dc3cd9d to f3c0c65CompareJune 22, 2026 11:49
Comment threadpackages/node/src/sdk/initOtel.ts Outdated
Comment threadpackages/node/src/sdk/initOtel.ts
Comment threadpackages/node/src/sdk/initOtel.ts Outdated
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from e200c8f to 502dca9CompareJune 22, 2026 18:20
Comment threadpackages/node/src/sdk/initOtel.ts
Comment threadpackages/node/src/sdk/initOtel.ts
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from 502dca9 to 6ae8302CompareJune 22, 2026 18:30
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from 6ae8302 to e2f91c0CompareJune 22, 2026 18:46
Comment threadpackages/node/test/sdk/init.test.ts
@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch 2 times, most recently from 4a3010c to d2384e8CompareJune 22, 2026 22:34
Comment threadpackages/node/src/sdk/initOtel.ts
Comment threadpackages/core/src/types/options.ts Outdated
* @default false
* @experimental
*/
useSentryTracerProvider?: boolean;

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.

should this live in core? is this not a node-specific option?

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.

also, why experimental? this can just be a regular option, and as discussed I'd actually make it opt-out (or more specifically, make the default dynamic based on if any options are set that require the more fully features tracer, e.g. spanProcessors)

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.

Wouldn't this be a breaking change? I thought the opt-out would be rather for v11 and in v10 it's opt-in

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

No, it's not a breaking change. The only case where it would be breaking is if people added span processors, but in that case we default back to the otel sdk tracer provider.

@andreiborza
andreiborzaforce-pushed the ab/sentry-trace-provider-node branch from 06f6f64 to 6759aa8CompareJune 23, 2026 19:13
Outside of span streaming, an outgoing fetch (`http.client`) span with no local
parent is no longer recorded as a standalone transaction — the downstream
sampling decision is left to the server. This is enforced via `onlyIfParent`,
which still creates a non-recording span so trace propagation headers are
injected.
This rule already lives in `SentrySampler`, but that only runs when an
OpenTelemetry SDK tracer provider is set up. Enforcing it in the instrumentation
makes it hold for the `SentryTracerProvider` and for SDKs that don't use an
OpenTelemetry tracer provider at all. The sampler rule is kept for OpenTelemetry
SDK / custom OpenTelemetry setups.
The transaction is assembled synchronously from the live span tree when the root
span ends, dropping child spans whose instrumentation closes them after it - in the
same tick (diagnostics-channel `asyncEnd`) or on a later tick (e.g. prisma engine
spans). A per-client debounced timer (the one the OpenTelemetry span exporter uses)
delays the snapshot so those children land first, and drains on the client `flush`
hook so `Sentry.flush()` / `close()` stays safe.
Enabled on the NodeClient rather than the SentryTracerProvider so it applies with or
without a tracer provider; the browser keeps its synchronous capture.
Under the SentryTracerProvider, streamed spans carry `sentry.origin` as a
first-class attribute including the default `manual` value, whereas the
OpenTelemetry SDK path omits the `manual` default. The `mysql` (v1) db spans
and the `pg.connect` span set no explicit origin, so they surface as
`manual` here.
Assert it for now. When those instrumentations are reworked to set an
explicit `auto.db.otel.*` origin (e.g. #21568 for mysql), these expectations
will be updated to the real origin then.
These assert prisma's engine spans (replayed asynchronously by
`@prisma/instrumentation`), which the SentryTracerProvider drops because it
assembles transactions synchronously on root-span end with no SpanExporter
buffer to wait for late children. They pass on the OpenTelemetry SDK
(`BasicTracerProvider`) path. Skip them here until the general
"complete span-tree capture without a SpanExporter" follow-up lands; v7 is
left enabled as it currently captures the engine spans in time.
Re-enabled now that the streamlined fastify integration (#21706) names spans at
creation instead of renaming via updateName(), so the SentryTracerProvider no
longer stamps sentry.source: 'custom'. Verified locally via e2e (11/11 pass each).
Moves the _INTERNAL_setDeferSegmentSpanCapture call out of initOtel (which only runs
on Sentry.init and only wires the first client) into the NodeClient constructor, which
runs for every client — first, second, or manually constructed — so each defers correctly.
Sign up for freeto 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.

6 participants

@andreiborza@mydea@Lms24@JPeer264@chargome@nicohrubec