Skip to content

feat: Emit low-cardinality http.client span names for fetch and XHR - #23682

Open
chargome wants to merge 6 commits into
developfrom
charlygomez/js-3412-http-client-fetch-xhr
Open

feat: Emit low-cardinality http.client span names for fetch and XHR#23682
chargome wants to merge 6 commits into
developfrom
charlygomez/js-3412-http-client-fetch-xhr

Conversation

@chargome

Copy link
Copy Markdown
Member

With span streaming, http.client spans are named {method} {url.domain} (GET api.example.com)
instead of {method} {sanitized-url}, falling back to the method alone when there is no domain
(relative and data URLs). traceLifecycle: 'static' is unchanged.

Covers instrumentFetchRequest in @sentry/core (browser, bun, cloudflare, vercel-edge), browser
XHR, and http.client.stream. node:http and undici follow separately.

Spans also get a url.domain attribute in both lifecycles, so the value in the name stays
filterable — same as the resource.* port.

Ref #23527

With span streaming, `http.client` spans are named `{method} {url.domain}` instead of
`{method} {sanitized-url}`, falling back to the method alone when there is no domain.
Covers `instrumentFetchRequest` in `@sentry/core`, browser XHR, and `http.client.stream`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@chargome chargome self-assigned this Aug 27, 2026
@linear-code

linear-code Bot commented Aug 27, 2026

Copy link
Copy Markdown

JS-3412

@chargome

Copy link
Copy Markdown
Member Author

bugbot run

Comment thread packages/browser/src/tracing/request.ts
Comment thread packages/browser/src/tracing/request.ts Outdated
Comment thread packages/browser/src/integrations/fetchStreamPerformance.ts Outdated
@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 28.56 kB - -
@sentry/browser - with treeshaking flags 26.92 kB - -
@sentry/browser - with treeshaking flags tracing without tracing 26.82 kB - -
@sentry/browser (incl. Tracing) 48.86 kB +0.22% +104 B 🔺
@sentry/browser (incl. Tracing + Span Streaming) 48.87 kB +0.21% +101 B 🔺
@sentry/browser (incl. Tracing, Profiling) 51.79 kB +0.22% +111 B 🔺
@sentry/browser (incl. Tracing, Replay) 88.35 kB +0.14% +122 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 77.75 kB +0.16% +122 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas) 93.04 kB +0.12% +107 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback) 106 kB +0.15% +150 B 🔺
@sentry/browser (incl. Feedback) 46.05 kB - -
@sentry/browser (incl. sendFeedback) 33.62 kB - -
@sentry/browser (incl. FeedbackAsync) 38.73 kB - -
@sentry/browser (incl. Metrics) 29.51 kB - -
@sentry/browser (incl. Logs) 29.8 kB - -
@sentry/browser (incl. Metrics & Logs) 30.43 kB - -
@sentry/react 30.3 kB - -
@sentry/react (incl. Tracing) 51.06 kB +0.23% +116 B 🔺
@sentry/vue 35.73 kB - -
@sentry/vue (incl. Tracing) 51.13 kB +0.23% +113 B 🔺
@sentry/svelte 28.59 kB - -
CDN Bundle 30.35 kB - -
CDN Bundle (incl. Tracing) 49.5 kB +0.25% +123 B 🔺
CDN Bundle (incl. Logs, Metrics) 32.58 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 51.41 kB +0.32% +159 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) 73.17 kB - -
CDN Bundle (incl. Tracing, Replay) 87 kB +0.16% +138 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 88.86 kB +0.16% +134 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) 92.93 kB +0.15% +130 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 94.83 kB +0.2% +183 B 🔺
CDN Bundle - uncompressed 89.95 kB - -
CDN Bundle (incl. Tracing) - uncompressed 147.56 kB +0.25% +356 B 🔺
CDN Bundle (incl. Logs, Metrics) - uncompressed 96.24 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 153.25 kB +0.24% +356 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 225.41 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 267.05 kB +0.14% +356 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 272.73 kB +0.14% +356 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 280.75 kB +0.13% +356 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 286.42 kB +0.13% +356 B 🔺
@sentry/nextjs (client) 53.68 kB +0.24% +124 B 🔺
@sentry/sveltekit (client) 49.29 kB +0.21% +101 B 🔺
@sentry/core/server 65.74 kB +0.12% +76 B 🔺
@sentry/core/browser 51.94 kB +0.15% +73 B 🔺
@sentry/node 123.77 kB +0.02% +18 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 85.53 kB - -
@sentry/node - without tracing 88.03 kB +0.02% +14 B 🔺
@sentry/node - without channel injection 103.18 kB +0.03% +23 B 🔺
@sentry/aws-serverless 96.42 kB +0.03% +26 B 🔺
@sentry/cloudflare (withSentry) - minified 201.21 kB +0.09% +168 B 🔺
@sentry/cloudflare (withSentry) 500.7 kB +0.09% +439 B 🔺

View base workflow run

`parseUrl` returns the raw authority, so `user:pass@host:port` could reach the XHR span name
and `server.address`. Strip it, and set `url.domain` on browser fetch and XHR spans so the
value in the streamed name is filterable. `fetchStreamPerformance` now uses the client it
receives in `setup` instead of `getClient()`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@chargome

Copy link
Copy Markdown
Member Author

bugbot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit bdcfbb5. Configure here.

@chargome
chargome marked this pull request as ready for review August 27, 2026 16:13
@chargome
chargome requested a review from a team as a code owner August 27, 2026 16:13
@chargome
chargome requested review from logaretm and msonnb and removed request for a team August 27, 2026 16:13
Comment thread packages/browser/src/integrations/graphqlClient.ts
const streamedName = domain ? `${method} ${domain}` : method;
const streamSpan = startInactiveSpan({
name: `${method} ${sanitizedUrl}`,
name: hasSpanStreamingEnabled(client) ? streamedName : `${method} ${sanitizedUrl}`,

@JPeer264 JPeer264 Aug 27, 2026

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.

q: The sanitizedUrl also takes care of "data: URLs", should the streamedName solely be domains?

Edit: seems like the PR description covers that part

Comment thread packages/browser/src/tracing/request.ts Outdated
// name or attribute.
const host = parsedUrl?.host?.replace(/^.*@/, '');
// Unlike `server.address`, `url.domain` excludes the port.
const domain = host?.replace(/:\d+$/, '');

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.

q: This works differently than in the fetchStreamPerformance integration, is that intended?

The other code I mean:

const domain = parsedUrl && !isURLObjectRelative(parsedUrl) ? parsedUrl.hostname : undefined;

@chargome
chargome requested a review from Lms24 August 31, 2026 08:05

@Lms24 Lms24 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

One request: Can we add a few more tests for relative URLs?

For browser, we could add a streaming version of of /dev-packages/browser-integration-tests/suites/tracing/request/fetch-relative-url/test.ts.

For node, I think having one or two integration tests would also be a good thing. Maybe one relative, one absolute? sorry, this is covered in the other PR, I should have checked that first.

Besides test, my only other concern was loosing information on relative URLs (see comment)

Comment thread packages/browser/src/integrations/fetchStreamPerformance.ts Outdated
Relative URLs previously left `http.client` and `http.client.stream` spans named after the
method alone, even in browsers where the page origin is a perfectly good domain to resolve
against. `instrumentFetchRequest` now takes a `urlBase` that the browser fills with the page
origin, so a relative fetch is named `GET app.example.com` instead of `GET`. XHR and stream
spans resolve the same way.

Consolidate the three different domain derivations (two regexes over `parseUrl().host`, and
an `isURLObjectRelative` check over a URL object) into one `getUrlDomain` helper. `URL.hostname`
drops userinfo and the port structurally, so the credential stripping no longer needs a regex.

Keep the GraphQL operation on outgoing request spans as `graphql.operation.name` and
`graphql.operation.type`. It only ever lived in the span name, so the low-cardinality rename
would otherwise have dropped it from the span entirely.

Refs #23527

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XvHhCTmsaPtA8vMDuHXSwd
Comment thread packages/browser/src/tracing/request.ts
…82-feed

# Conflicts:
#	MIGRATION.md
#	packages/browser/src/integrations/fetchStreamPerformance.ts
#	packages/browser/src/tracing/request.ts
#	packages/core/src/fetch.ts
Comment thread packages/browser/src/tracing/request.ts
`waitForStreamedSpans` resolves on the first envelope matching its predicate, and the
predicate only waited for the three `http.client` spans. The pageload span often shares
that envelope but does not have to, so asserting `parent_span_id` against it failed
whenever it landed in a later one — the flakiness check caught this on repeat 10.

Parenting is already covered by the `fetch-streamed` and `xhr-streamed` suites, so drop
it here and assert only the span name and URL attributes these tests exist for.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XvHhCTmsaPtA8vMDuHXSwd
@chargome
chargome requested a review from Lms24 August 31, 2026 16:02

@Lms24 Lms24 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

thanks for adding the tests!


Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
integrations: [Sentry.browserTracingIntegration(), Sentry.spanStreamingIntegration()],

@Lms24 Lms24 Aug 31, 2026

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: shouldn't be necessary anymore in v11

Suggested change
integrations: [Sentry.browserTracingIntegration(), Sentry.spanStreamingIntegration()],
integrations: [Sentry.browserTracingIntegration()],


Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
integrations: [Sentry.browserTracingIntegration(), Sentry.spanStreamingIntegration()],

@Lms24 Lms24 Aug 31, 2026

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: shouldn't be necessary anymore in v11

Suggested change
integrations: [Sentry.browserTracingIntegration(), Sentry.spanStreamingIntegration()],
integrations: [Sentry.browserTracingIntegration()]

*/
export function getUrlDomain(url: string, base?: string): string | undefined {
try {
return new URL(url, base).hostname || undefined;

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: can we reuse our parseUrl function(s) for this? not sure if anything speaks against this

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 the base URL... this is probably fine to leave as-is although I could have sworn we have a helper somewhere that does this already. Anyway, feel free to ignore

Starting a browser span adds the integration on its own, so listing it in `Sentry.init` is
no longer needed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XvHhCTmsaPtA8vMDuHXSwd
@chargome
chargome enabled auto-merge (squash) August 31, 2026 16:11
@chargome
chargome force-pushed the charlygomez/js-3412-http-client-fetch-xhr branch from 4886648 to 5385238 Compare August 31, 2026 16:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants