Skip to content

ref(node): Fold breadcrumb & trace propagation into node fetch instrumentation - #21872

Merged
mydea merged 1 commit into
developfrom
fn/node-fetch-enhance-undici
Jul 1, 2026
Merged

ref(node): Fold breadcrumb & trace propagation into node fetch instrumentation#21872
mydea merged 1 commit into
developfrom
fn/node-fetch-enhance-undici

Conversation

@mydea

@mydeamydea commented Jul 1, 2026

Copy link
Copy Markdown
Member

Enhances the node undici-instrumentation into a single instrumentation that emits http.client spans, records breadcrumbs, and propagates traces for outgoing fetch/undici requests. Previously this was split across two subscribers on the same diagnostics channels: instrumentUndici (spans) and SentryNodeFetchInstrumentation (breadcrumbs + trace propagation).

What changes:

  • Span creation is gated by a spans option (default true). When spans are disabled, breadcrumbs are still recorded and trace propagation headers are still injected (gated by tracePropagation).
  • Trace propagation now goes through the dedup-aware addTracePropagationHeadersToFetchRequest, which gains an optional span argument so outgoing headers reference the http.client span while still de-duplicating against trace headers the user set manually (e.g. via getTraceData()). This preserves the double-baggage prevention that the two-instrumentation setup previously provided.
  • @sentry/node-core now exports addFetchRequestBreadcrumb and addTracePropagationHeadersToFetchRequest so the node instrumentation can reuse them.

Root cause: folding the two subscribers into one dropped the de-dup pass that ran after span-based header injection; routing propagation through addTracePropagationHeadersToFetchRequest (now span-aware) restores it.

This PR is intentionally scoped to the node package (plus the minimal node-core support above) to keep the diff reviewable. A stacked follow-up (#21873) moves the instrumentation into node-core.

🤖 Generated with Claude Code

…instrumentation
Enhance the node `undici-instrumentation` so it is a single instrumentation that emits
`http.client` spans (gated by a `spans` option, default `true`), records breadcrumbs, and
propagates traces for outgoing fetch/undici requests. This replaces the previous setup where
span creation (`instrumentUndici`) and breadcrumb/trace-propagation (`SentryNodeFetchInstrumentation`)
ran as two separate subscribers on the same diagnostics channels.
Trace propagation now goes through the dedup-aware `addTracePropagationHeadersToFetchRequest`, which
gained an optional `span` argument so the outgoing headers reference the `http.client` span while
still de-duplicating against any trace headers the user set manually (e.g. via `getTraceData()`).
This keeps the change contained to the `node` package to ease review; a follow-up moves the
instrumentation into `node-core`.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mydea
mydea requested a review from a team as a code ownerJuly 1, 2026 08:23
@mydea
mydea requested review from JPeer264 and andreiborza and removed request for a teamJuly 1, 2026 08:23

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

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

Reviewed by Cursor Bugbot for commit d43ca81. Configure here.

// Ignore span creation if:
// - the outgoing request is ignored via config
// - method is 'CONNECT'
if (request.method === 'CONNECT' || ignoredByCallback) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Skipped requests lose trace headers

Medium Severity

With spans enabled, onRequestCreated returns before calling addTracePropagationHeadersToFetchRequest for CONNECT requests and when new URL fails, so outgoing headers are never injected. The removed SentryNodeFetchInstrumentation still ran on those requests and propagated trace data from the active scope.

Additional Locations (1)
Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit d43ca81. Configure here.

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.

I think this is actually correct/expected.

@github-actions

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser27.62 kB--
@sentry/browser - with treeshaking flags26.05 kB--
@sentry/browser (incl. Tracing)46.07 kB--
@sentry/browser (incl. Tracing + Span Streaming)47.82 kB--
@sentry/browser (incl. Tracing, Profiling)50.84 kB--
@sentry/browser (incl. Tracing, Replay)85.31 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags74.91 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)89.99 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)102.67 kB--
@sentry/browser (incl. Feedback)44.8 kB--
@sentry/browser (incl. sendFeedback)32.42 kB--
@sentry/browser (incl. FeedbackAsync)37.55 kB--
@sentry/browser (incl. Metrics)28.68 kB--
@sentry/browser (incl. Logs)28.93 kB--
@sentry/browser (incl. Metrics & Logs)29.61 kB--
@sentry/react29.41 kB--
@sentry/react (incl. Tracing)48.38 kB--
@sentry/vue32.85 kB--
@sentry/vue (incl. Tracing)47.93 kB--
@sentry/svelte27.64 kB--
CDN Bundle30.02 kB--
CDN Bundle (incl. Tracing)48.02 kB--
CDN Bundle (incl. Logs, Metrics)31.58 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)49.35 kB--
CDN Bundle (incl. Replay, Logs, Metrics)70.79 kB--
CDN Bundle (incl. Tracing, Replay)85.51 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)86.79 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)91.32 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)92.56 kB--
CDN Bundle - uncompressed89.42 kB--
CDN Bundle (incl. Tracing) - uncompressed145.35 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed94.12 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed149.32 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed218.66 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed264.36 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed268.32 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed278.06 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed282.01 kB--
@sentry/nextjs (client)50.76 kB--
@sentry/sveltekit (client)46.46 kB--
@sentry/core/server77.75 kB--
@sentry/core/browser64.06 kB--
@sentry/node-core61.48 kB+0.03%+13 B 🔺
@sentry/node121.99 kB-0.07%-78 B 🔽
@sentry/node/import (ESM hook with diagnostics-channel injection)69.95 kB--
@sentry/node/light50.46 kB+0.03%+11 B 🔺
@sentry/node - without tracing73.16 kB-0.07%-47 B 🔽
@sentry/aws-serverless84.02 kB-0.09%-70 B 🔽
@sentry/cloudflare (withSentry) - minified180.62 kB--
@sentry/cloudflare (withSentry)446.93 kB--

View base workflow run

SentryNodeFetchInstrumentation,
type SentryNodeFetchInstrumentationOptions,
} from './integrations/node-fetch/SentryNodeFetchInstrumentation';
export { addFetchRequestBreadcrumb, addTracePropagationHeadersToFetchRequest } from './utils/outgoingFetchRequest';

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.

super-l: Is this meant for users to be used as well? If not, I think prefixing it with _INTERNAL_ would be nice - not sure if we have a best practice for that, at least I always did it. And/or mark them as @internal in the JSDoc

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.

Ah. It seems it will be dropped again in the next PR :D

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.

yeah, this is a bit tricky 😅 should be cleaned up when I merge the stack together!

@mydea
mydea merged commit a693fb5 into developJul 1, 2026
207 checks passed
@mydea
mydea deleted the fn/node-fetch-enhance-undici branch July 1, 2026 11:08
mydea added a commit that referenced this pull request Jul 1, 2026
Stacked on #21872.
Moves the `undici-instrumentation` (spans + breadcrumbs + trace
propagation) and the vendored `node-fetch` types from the `node` package
into `node-core`, and exposes `instrumentUndici` / `NodeFetchOptions`
from `@sentry/node-core`.
What changes:
- `node-core`'s `nativeNodeFetchIntegration` now instruments via
`instrumentUndici` directly (spans default to `true`).
- The `node` package keeps a thin `nativeNodeFetchIntegration` wrapper
(`node-fetch.ts`) that derives the `spans` default from the client
options (`skipOpenTelemetrySetup` / `hasSpansEnabled`) before delegating
to `instrumentUndici`. In v11 this will become the only implementation.
- `SentryNodeFetchInstrumentation` is now unused internally and is
marked `@deprecated`. It stays exported for backwards compatibility and
will be removed in a future major version.
Review the first PR in the stack (#21872) first — this PR is purely the
relocation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

2 participants

@mydea@JPeer264