-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
feat(browser): Emit web vitals as streamed spans #19827
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 19 commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
56b5617
feat(browser-utils): Add FCP instrumentation handler and export INP_E…
logaretm d7ae263
feat(browser): Emit web vitals as streamed spans when span streaming …
logaretm 13670f7
test(browser): Add integration tests for streamed web vital spans
logaretm f093a6d
fix(browser): Only emit LCP, CLS, INP as streamed spans; disable stan…
logaretm 9eed4c9
fix(browser): Add MAX_PLAUSIBLE_INP_DURATION check to streamed INP sp…
logaretm 6cba655
fix(browser): Prevent duplicate INP spans when span streaming is enabled
logaretm accc960
fix(browser-utils): Remove dead FCP instrumentation code
logaretm e92046f
fix(browser-utils): Add fallback for browserPerformanceTimeOrigin in …
logaretm 86aadbb
fix(browser-utils): Cache browserPerformanceTimeOrigin call in _sendL…
logaretm e973a84
fix(browser): Skip INP interaction listeners when span streaming is e…
logaretm 4f8aaf5
fix(browser): Skip CLS/LCP measurements on pageload span when streaming
logaretm dde4ad7
refactor(browser-utils): Share MAX_PLAUSIBLE_INP_DURATION between INP…
logaretm 5dec7ce
fix(browser): Fix ReferenceError for spanStreamingEnabled in afterAll…
logaretm 382f99b
fix(browser): Skip redundant CLS/LCP handlers when span streaming is …
logaretm 936f7f0
send wv spans as child spans of pageload/inp root span
Lms24 02ac8fd
fix ttfb, fb, fco, measurements, connection attributes
Lms24 43037a5
fix integration tests after attribute name changes
Lms24 b307949
guard DEBUG_BUILD && fix size limit
Lms24 a0bfb46
cleanup
Lms24 70a2ca4
fix size limit again + unit tests
Lms24 1aa3b6e
cleanup
Lms24 5bba109
another cleanup lol
Lms24 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
...es/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-streamed-spans/init.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import * as Sentry from '@sentry/browser'; | ||
|
|
||
| window.Sentry = Sentry; | ||
| window._testBaseTimestamp = performance.timeOrigin / 1000; | ||
|
|
||
| Sentry.init({ | ||
| dsn: 'https://public@dsn.ingest.sentry.io/1337', | ||
| integrations: [Sentry.browserTracingIntegration(), Sentry.spanStreamingIntegration()], | ||
| traceLifecycle: 'stream', | ||
| tracesSampleRate: 1, | ||
| }); |
17 changes: 17 additions & 0 deletions
17
...browser-integration-tests/suites/tracing/metrics/web-vitals-cls-streamed-spans/subject.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| import { simulateCLS } from '../../../../utils/web-vitals/cls.ts'; | ||
|
|
||
| // Simulate Layout shift right at the beginning of the page load, depending on the URL hash | ||
| // don't run if expected CLS is NaN | ||
| const expectedCLS = Number(location.hash.slice(1)); | ||
| if (expectedCLS && expectedCLS >= 0) { | ||
| simulateCLS(expectedCLS).then(() => window.dispatchEvent(new Event('cls-done'))); | ||
| } | ||
|
|
||
| // Simulate layout shift whenever the trigger-cls event is dispatched | ||
| // Cannot trigger via a button click because expected layout shift after | ||
| // an interaction doesn't contribute to CLS. | ||
| window.addEventListener('trigger-cls', () => { | ||
| simulateCLS(0.1).then(() => { | ||
| window.dispatchEvent(new Event('cls-done')); | ||
| }); | ||
| }); |
10 changes: 10 additions & 0 deletions
10
...wser-integration-tests/suites/tracing/metrics/web-vitals-cls-streamed-spans/template.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| <!doctype html> | ||
| <html> | ||
| <head> | ||
| <meta charset="utf-8" /> | ||
| </head> | ||
| <body> | ||
| <div id="content"></div> | ||
| <p>Some content</p> | ||
| </body> | ||
| </html> |
76 changes: 76 additions & 0 deletions
76
...es/browser-integration-tests/suites/tracing/metrics/web-vitals-cls-streamed-spans/test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| import type { Page } from '@playwright/test'; | ||
| import { expect } from '@playwright/test'; | ||
| import { sentryTest } from '../../../../utils/fixtures'; | ||
| import { hidePage, shouldSkipTracingTest, testingCdnBundle } from '../../../../utils/helpers'; | ||
| import { getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils'; | ||
|
|
||
| sentryTest.beforeEach(async ({ browserName, page }) => { | ||
| if (shouldSkipTracingTest() || testingCdnBundle() || browserName !== 'chromium') { | ||
| sentryTest.skip(); | ||
| } | ||
|
|
||
| await page.setViewportSize({ width: 800, height: 1200 }); | ||
| }); | ||
|
|
||
| function waitForLayoutShift(page: Page): Promise<void> { | ||
| return page.evaluate(() => { | ||
| return new Promise(resolve => { | ||
| window.addEventListener('cls-done', () => resolve()); | ||
| }); | ||
| }); | ||
| } | ||
|
|
||
| sentryTest('captures CLS as a streamed span with source attributes', async ({ getLocalTestUrl, page }) => { | ||
| const url = await getLocalTestUrl({ testDir: __dirname }); | ||
|
|
||
| const clsSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'ui.webvital.cls'); | ||
|
Lms24 marked this conversation as resolved.
|
||
| const pageloadSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'pageload'); | ||
|
|
||
| await page.goto(`${url}#0.15`); | ||
| await waitForLayoutShift(page); | ||
| await hidePage(page); | ||
|
|
||
| const clsSpan = await clsSpanPromise; | ||
| const pageloadSpan = await pageloadSpanPromise; | ||
|
|
||
| expect(clsSpan.attributes?.['sentry.op']).toEqual({ type: 'string', value: 'ui.webvital.cls' }); | ||
| expect(clsSpan.attributes?.['sentry.origin']).toEqual({ type: 'string', value: 'auto.http.browser.cls' }); | ||
| expect(clsSpan.attributes?.['sentry.exclusive_time']).toEqual({ type: 'integer', value: 0 }); | ||
| expect(clsSpan.attributes?.['user_agent.original']?.value).toEqual(expect.stringContaining('Chrome')); | ||
|
|
||
| // Check browser.web_vital.cls.source attributes | ||
| expect(clsSpan.attributes?.['browser.web_vital.cls.source.1']?.value).toEqual( | ||
| expect.stringContaining('body > div#content > p'), | ||
| ); | ||
|
|
||
| // Check pageload span id is present | ||
| expect(clsSpan.attributes?.['sentry.pageload.span_id']?.value).toBe(pageloadSpan.span_id); | ||
|
|
||
| // CLS is a point-in-time metric | ||
| expect(clsSpan.start_timestamp).toEqual(clsSpan.end_timestamp); | ||
|
|
||
| expect(clsSpan.span_id).toMatch(/^[\da-f]{16}$/); | ||
| expect(clsSpan.trace_id).toMatch(/^[\da-f]{32}$/); | ||
|
|
||
| expect(clsSpan.parent_span_id).toBe(pageloadSpan.span_id); | ||
| expect(clsSpan.trace_id).toBe(pageloadSpan.trace_id); | ||
| }); | ||
|
|
||
| sentryTest('CLS streamed span has web vital value attribute', async ({ getLocalTestUrl, page }) => { | ||
| const url = await getLocalTestUrl({ testDir: __dirname }); | ||
|
|
||
| const clsSpanPromise = waitForStreamedSpan(page, span => getSpanOp(span) === 'ui.webvital.cls'); | ||
|
|
||
| await page.goto(`${url}#0.1`); | ||
| await waitForLayoutShift(page); | ||
| await hidePage(page); | ||
|
|
||
| const clsSpan = await clsSpanPromise; | ||
|
|
||
| // The CLS value should be set as a browser.web_vital.cls.value attribute | ||
| expect(clsSpan.attributes?.['browser.web_vital.cls.value']?.type).toBe('double'); | ||
| // Flakey value dependent on timings -> we check for a range | ||
| const clsValue = clsSpan.attributes?.['browser.web_vital.cls.value']?.value as number; | ||
| expect(clsValue).toBeGreaterThan(0.05); | ||
| expect(clsValue).toBeLessThan(0.15); | ||
| }); | ||
|
cursor[bot] marked this conversation as resolved.
|
||
10 changes: 10 additions & 0 deletions
10
...es/browser-integration-tests/suites/tracing/metrics/web-vitals-inp-streamed-spans/init.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import * as Sentry from '@sentry/browser'; | ||
|
|
||
| window.Sentry = Sentry; | ||
| window._testBaseTimestamp = performance.timeOrigin / 1000; | ||
|
|
||
| Sentry.init({ | ||
| dsn: 'https://public@dsn.ingest.sentry.io/1337', | ||
| integrations: [Sentry.browserTracingIntegration({ idleTimeout: 4000 }), Sentry.spanStreamingIntegration()], | ||
| tracesSampleRate: 1, | ||
| }); |
19 changes: 19 additions & 0 deletions
19
...browser-integration-tests/suites/tracing/metrics/web-vitals-inp-streamed-spans/subject.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| const blockUI = | ||
| (delay = 70) => | ||
| e => { | ||
| const startTime = Date.now(); | ||
|
|
||
| function getElapsed() { | ||
| const time = Date.now(); | ||
| return time - startTime; | ||
| } | ||
|
|
||
| while (getElapsed() < delay) { | ||
| // | ||
| } | ||
|
|
||
| e.target.classList.add('clicked'); | ||
| }; | ||
|
|
||
| document.querySelector('[data-test-id=slow-button]').addEventListener('click', blockUI(450)); | ||
| document.querySelector('[data-test-id=normal-button]').addEventListener('click', blockUI()); |
10 changes: 10 additions & 0 deletions
10
...wser-integration-tests/suites/tracing/metrics/web-vitals-inp-streamed-spans/template.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| <!doctype html> | ||
| <html> | ||
| <head> | ||
| <meta charset="utf-8" /> | ||
| </head> | ||
| <body> | ||
| <button data-test-id="slow-button" data-sentry-element="SlowButton">Slow</button> | ||
| <button data-test-id="normal-button" data-sentry-element="NormalButton">Click Me</button> | ||
| </body> | ||
| </html> |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.