Skip to content

feat(browser): split web vitals integration#21210

Open
logaretm wants to merge 4 commits into
developfrom
awad/js-2628-split-web-vitals-into-their-own-integration
Open

feat(browser): split web vitals integration#21210
logaretm wants to merge 4 commits into
developfrom
awad/js-2628-split-web-vitals-into-their-own-integration

Conversation

@logaretm
Copy link
Copy Markdown
Member

@logaretm logaretm commented May 28, 2026

Splits out the web vitals recording logic to its own integration that is added by the browser tracing integration by default.

Closes #21209

@logaretm logaretm requested a review from a team as a code owner May 28, 2026 11:55
@logaretm logaretm requested review from Lms24 and mydea and removed request for a team May 28, 2026 11:56
@linear-code
Copy link
Copy Markdown

linear-code Bot commented May 28, 2026

JS-2628

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 28, 2026

size-limit report 📦

Path Size % Change Change
@sentry/browser 27.17 kB - -
@sentry/browser - with treeshaking flags 25.62 kB - -
@sentry/browser (incl. Tracing) 45.47 kB +0.5% +224 B 🔺
@sentry/browser (incl. Tracing + Span Streaming) 47.71 kB +0.5% +235 B 🔺
@sentry/browser (incl. Tracing, Profiling) 50.25 kB +0.07% +32 B 🔺
@sentry/browser (incl. Tracing, Replay) 84.65 kB -0.21% -178 B 🔽
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 74.28 kB -0.16% -118 B 🔽
@sentry/browser (incl. Tracing, Replay with Canvas) 89.35 kB -0.21% -183 B 🔽
@sentry/browser (incl. Tracing, Replay, Feedback) 102.03 kB -0.13% -129 B 🔽
@sentry/browser (incl. Feedback) 44.34 kB - -
@sentry/browser (incl. sendFeedback) 31.98 kB - -
@sentry/browser (incl. FeedbackAsync) 37.08 kB - -
@sentry/browser (incl. Metrics) 28.25 kB - -
@sentry/browser (incl. Logs) 28.48 kB - -
@sentry/browser (incl. Metrics & Logs) 29.19 kB - -
@sentry/react 28.99 kB - -
@sentry/react (incl. Tracing) 47.72 kB +0.44% +208 B 🔺
@sentry/vue 32.2 kB - -
@sentry/vue (incl. Tracing) 47.36 kB +0.43% +200 B 🔺
@sentry/svelte 27.19 kB - -
CDN Bundle 29.55 kB - -
CDN Bundle (incl. Tracing) 47.91 kB +0.22% +102 B 🔺
CDN Bundle (incl. Logs, Metrics) 31.05 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 49.16 kB +0.24% +115 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) 70.31 kB - -
CDN Bundle (incl. Tracing, Replay) 85.27 kB +0.1% +82 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 86.44 kB +0.11% +93 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) 91.09 kB +0.05% +41 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 92.28 kB +0.09% +75 B 🔺
CDN Bundle - uncompressed 87.59 kB - -
CDN Bundle (incl. Tracing) - uncompressed 144.8 kB +0.5% +707 B 🔺
CDN Bundle (incl. Logs, Metrics) - uncompressed 92.08 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 148.56 kB +0.48% +707 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 216.81 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 263.58 kB +0.28% +710 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 267.32 kB +0.27% +710 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 277.28 kB +0.26% +710 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 281.01 kB +0.26% +710 B 🔺
@sentry/nextjs (client) 50.24 kB +0.41% +203 B 🔺
@sentry/sveltekit (client) 45.87 kB +0.43% +195 B 🔺
@sentry/core/server 76.56 kB - -
@sentry/core/browser 63.09 kB - -
@sentry/node-core 61.69 kB +0.01% +1 B 🔺
@sentry/node 130.51 kB - -
@sentry/node - without tracing 74.1 kB +0.01% +1 B 🔺
@sentry/aws-serverless 86.29 kB - -
@sentry/cloudflare (withSentry) - minified 171.64 kB - -
@sentry/cloudflare (withSentry) 429.7 kB - -

View base workflow run

@logaretm logaretm force-pushed the awad/js-2628-split-web-vitals-into-their-own-integration branch from c7e4357 to 99e8359 Compare May 28, 2026 12:10
Co-Authored-By: Codex <codex@openai.com>
@logaretm logaretm force-pushed the awad/js-2628-split-web-vitals-into-their-own-integration branch from 99e8359 to 433f30a Compare May 28, 2026 12:10
Comment thread packages/browser/test/integrations/webVitals.test.ts
/**
* Web vitals to skip.
*/
disable?: WebVitalName[];
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I like this API much more than the enableInp flag we had in browserTracingIntegration. Just wondering if we should have a different name for this because we rarely use disable as an option. WDYT about ignore? Kinda goes with the same "denylist" pattern but is a bit more established in our options.

I was also thinking if instead of the "disable" mentality, we should have an enable: WebVitalName[] option that by default includes everything. But then I realized, if users set this to e.g. disable INP but have LCP and CLS, they'd never get a new vital we'd add here (soft nav vitals) unless they explicitly add it. So I think the denylist approach in this case is better suited.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

ignore works better here and similar to other stuff we have

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think one thing where browserTracing and webVitalsIntegration are still a bit interwoven is the addPerformanceEntries call where we write the web vitals as measurements onto the pageload span. We would still need this code if v11 ships with transactions and span streaming (most likely). Do you think it makes sense for us to also pull this out of browserTracing?

I guess one can make the argument that since this requires the pageload span to be present, it can also live in browserTracing. So in a way this is already a separation of concerns. No strong opinions!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think pageload is the transport only in this case, so ideally we should move it out. It is too tightly coupled to the other stuff collected like fp/fcp, do you think we can maybe have it its own performance observer loop so we can split them out?

logaretm and others added 2 commits May 29, 2026 20:41
Aligns the webVitalsIntegration denylist option with the more established
`ignore` naming used elsewhere in our options.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Previously `addPerformanceEntries` (driven by `browserTracingIntegration`)
both emitted pageload child spans (resource/navigation/measure) *and* wrote
the web vital values onto the pageload span. This interleaved web vital logic
with browserTracing's transport concerns.

This fully consolidates web vital logic into `webVitalsIntegration`:

- FP/FCP are now collected via their own `paint` PerformanceObserver in
  `startTrackingWebVitals`, instead of being scavenged from the shared
  `addPerformanceEntries` loop. All six vitals are now collected in one place.
- The "write vitals onto the pageload span" block is extracted into a new
  `addWebVitalsToSpan` helper, which the integration drives via
  `collectWebVitalsForClient(client, span)` at span end. The integration owns
  the decision of whether/how to record each vital.
- `connection.rtt` is decoupled from the web vital measurement flush and
  emitted directly by `_trackNavigator` (behavior-preserving: `setMeasurement`
  in v1, span attribute in span streaming).
- `addPerformanceEntries` slims to resource/navigation/measure spans +
  navigator; its `_performanceCursor` is no longer shared with web vitals.

`browserTracingIntegration` now only provides the pageload span and the
span-end trigger — it makes no web vital decisions.
@logaretm logaretm force-pushed the awad/js-2628-split-web-vitals-into-their-own-integration branch from 0eccc6c to fb0e9d0 Compare May 29, 2026 21:57
Comment thread packages/browser-utils/src/metrics/browserMetrics.ts
The web vital refactor moved `connection.rtt` out of the shared `_measurements`
object and emitted it directly via `setMeasurement` in `_trackNavigator`. Since
`_trackNavigator` runs from `addPerformanceEntries` for every idle span
(pageload and navigation), this regressed `connection.rtt` onto navigation
transactions too. Previously it was only flushed inside the `op === 'pageload'`
guard.

Restore the pageload-only scope for the non-streaming measurement (the
span-streaming attribute path was already emitted for all spans and is
unchanged) and add a navigation regression test.
Copy link
Copy Markdown

@cursor cursor Bot left a comment

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 98a76d7. Configure here.

const url = await getLocalTestUrl({ testDir: __dirname });

const pageloadRequest = await getFirstSentryEnvelopeRequest<Event>(page, url);
const navigationRequest = await getFirstSentryEnvelopeRequest<Event>(page, `${url}#foo`);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

E2E test uses unreliable getFirstSentryEnvelopeRequest helper

Medium Severity

The new E2E test uses getFirstSentryEnvelopeRequest which is flagged as unreliable by the project rules. The sequential pattern of awaiting the pageload request and then immediately navigating and awaiting the navigation request is prone to race conditions — stale or unrelated envelopes (e.g. session updates) could be captured instead of the expected transaction. Helpers like waitForTransaction with appropriate filtering would be more reliable.

Fix in Cursor Fix in Web

Triggered by project rule: PR Review Guidelines for Cursor Bot

Reviewed by Cursor Bugbot for commit 98a76d7. Configure here.

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.

Split Web Vitals into their own integration

2 participants