Uh oh!
There was an error while loading. Please reload this page.
feat(node): Wire up SentryTracerProvider - #21680
Conversation
size-limit report 📦
|
e05567e to
dc3cd9dComparedc3cd9d to
f3c0c65CompareUh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
e200c8f to
502dca9CompareUh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
502dca9 to
6ae8302Compare6ae8302 to
e2f91c0CompareUh oh!
There was an error while loading. Please reload this page.
4a3010c to
d2384e8CompareUh oh!
There was an error while loading. Please reload this page.
| * @default false | ||
| * @experimental | ||
| */ | ||
| useSentryTracerProvider?: boolean; |
There was a problem hiding this comment.
should this live in core? is this not a node-specific option?
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
Wouldn't this be a breaking change? I thought the opt-out would be rather for v11 and in v10 it's opt-in
There was a problem hiding this comment.
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.
06f6f64 to
6759aa8CompareOutside 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.
… to drain the deferred transaction capture
What
Makes Sentry's minimal
SentryTracerProviderthe default OpenTelemetry tracer provider for@sentry/node, replacing the full OTel SDKBasicTracerProvider. The SDK provider stays available via the newopenTelemetryBasicTracerProvideroption (and is used automatically when customopenTelemetrySpanProcessorsare 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.
BasicTracerProviderremains for setups that rely on OTel SDK features the minimal provider does not run.