-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(tracing): add long animation frame tracing (#12646)
Adds an option to trace long animation frames as per #11719. This tracing feature is disabled by default. Currently, this feature https://developer.mozilla.org/en-US/docs/Web/API/Performance_API/Long_animation_frame_timing is only supported by Chromium browsers. Usage is opt-in: ```js Sentry.init({ dsn: '__PUBLIC_DSN__', integrations: [ Sentry.browserTracingIntegration({ enableLongAnimationFrame: true, }), ], tracesSampleRate: 1, }); ```
- Loading branch information
Showing
25 changed files
with
607 additions
and
2 deletions.
There are no files selected for viewing
This file contains 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
12 changes: 12 additions & 0 deletions
12
...s/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/assets/script.js
This file contains 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,12 @@ | ||
(() => { | ||
const startTime = Date.now(); | ||
|
||
function getElasped() { | ||
const time = Date.now(); | ||
return time - startTime; | ||
} | ||
|
||
while (getElasped() < 101) { | ||
// | ||
} | ||
})(); |
11 changes: 11 additions & 0 deletions
11
...tion-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/init.js
This file contains 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; | ||
|
||
Sentry.init({ | ||
dsn: 'https://public@dsn.ingest.sentry.io/1337', | ||
integrations: [ | ||
Sentry.browserTracingIntegration({ enableLongTask: false, enableLongAnimationFrame: false, idleTimeout: 9000 }), | ||
], | ||
tracesSampleRate: 1, | ||
}); |
10 changes: 10 additions & 0 deletions
10
...ests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/template.html
This file contains 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>Rendered Before Long Animation Frame</div> | ||
<script src="https://example.com/path/to/script.js"></script> | ||
</body> | ||
</html> |
27 changes: 27 additions & 0 deletions
27
...tion-tests/suites/tracing/browserTracingIntegration/long-animation-frame-disabled/test.ts
This file contains 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,27 @@ | ||
import type { Route } from '@playwright/test'; | ||
import { expect } from '@playwright/test'; | ||
import type { Event } from '@sentry/types'; | ||
|
||
import { sentryTest } from '../../../../utils/fixtures'; | ||
import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; | ||
|
||
sentryTest( | ||
'should not capture long animation frame when flag is disabled.', | ||
async ({ browserName, getLocalTestPath, page }) => { | ||
// Long animation frames only work on chrome | ||
if (shouldSkipTracingTest() || browserName !== 'chromium') { | ||
sentryTest.skip(); | ||
} | ||
|
||
await page.route('**/path/to/script.js', (route: Route) => | ||
route.fulfill({ path: `${__dirname}/assets/script.js` }), | ||
); | ||
|
||
const url = await getLocalTestPath({ testDir: __dirname }); | ||
|
||
const eventData = await getFirstSentryEnvelopeRequest<Event>(page, url); | ||
const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui')); | ||
|
||
expect(uiSpans?.length).toBe(0); | ||
}, | ||
); |
25 changes: 25 additions & 0 deletions
25
...ts/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/assets/script.js
This file contains 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,25 @@ | ||
function getElapsed(startTime) { | ||
const time = Date.now(); | ||
return time - startTime; | ||
} | ||
|
||
function handleClick() { | ||
const startTime = Date.now(); | ||
while (getElapsed(startTime) < 105) { | ||
// | ||
} | ||
} | ||
|
||
function start() { | ||
const startTime = Date.now(); | ||
while (getElapsed(startTime) < 105) { | ||
// | ||
} | ||
} | ||
|
||
// trigger 2 long-animation-frame events | ||
// one from the top-level and the other from an event-listener | ||
start(); | ||
|
||
const button = document.getElementById('clickme'); | ||
button.addEventListener('click', handleClick); |
15 changes: 15 additions & 0 deletions
15
...ation-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/init.js
This file contains 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,15 @@ | ||
import * as Sentry from '@sentry/browser'; | ||
|
||
window.Sentry = Sentry; | ||
|
||
Sentry.init({ | ||
dsn: 'https://public@dsn.ingest.sentry.io/1337', | ||
integrations: [ | ||
Sentry.browserTracingIntegration({ | ||
idleTimeout: 9000, | ||
enableLongTask: false, | ||
enableLongAnimationFrame: true, | ||
}), | ||
], | ||
tracesSampleRate: 1, | ||
}); |
13 changes: 13 additions & 0 deletions
13
...tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/template.html
This file contains 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,13 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<meta charset="utf-8" /> | ||
</head> | ||
<body> | ||
<div>Rendered Before Long Animation Frame</div> | ||
<button id="clickme"> | ||
click me to start the long animation! | ||
</button> | ||
<script src="https://example.com/path/to/script.js"></script> | ||
</body> | ||
</html> |
112 changes: 112 additions & 0 deletions
112
...ation-tests/suites/tracing/browserTracingIntegration/long-animation-frame-enabled/test.ts
This file contains 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,112 @@ | ||
import type { Route } from '@playwright/test'; | ||
import { expect } from '@playwright/test'; | ||
import type { Event } from '@sentry/types'; | ||
|
||
import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/browser'; | ||
import { sentryTest } from '../../../../utils/fixtures'; | ||
import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; | ||
|
||
sentryTest( | ||
'should capture long animation frame for top-level script.', | ||
async ({ browserName, getLocalTestPath, page }) => { | ||
// Long animation frames only work on chrome | ||
if (shouldSkipTracingTest() || browserName !== 'chromium') { | ||
sentryTest.skip(); | ||
} | ||
|
||
await page.route('**/path/to/script.js', (route: Route) => | ||
route.fulfill({ path: `${__dirname}/assets/script.js` }), | ||
); | ||
|
||
const url = await getLocalTestPath({ testDir: __dirname }); | ||
|
||
const promise = getFirstSentryEnvelopeRequest<Event>(page); | ||
|
||
await page.goto(url); | ||
|
||
await new Promise(resolve => setTimeout(resolve, 1000)); | ||
|
||
const eventData = await promise; | ||
|
||
const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui.long-animation-frame')); | ||
|
||
expect(uiSpans?.length).toEqual(1); | ||
|
||
const [topLevelUISpan] = uiSpans || []; | ||
expect(topLevelUISpan).toEqual( | ||
expect.objectContaining({ | ||
op: 'ui.long-animation-frame', | ||
description: 'Main UI thread blocked', | ||
parent_span_id: eventData.contexts?.trace?.span_id, | ||
data: { | ||
'code.filepath': 'https://example.com/path/to/script.js', | ||
'browser.script.source_char_position': 0, | ||
'browser.script.invoker': 'https://example.com/path/to/script.js', | ||
'browser.script.invoker_type': 'classic-script', | ||
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.long-animation-frame', | ||
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics', | ||
}, | ||
}), | ||
); | ||
const start = topLevelUISpan.start_timestamp ?? 0; | ||
const end = topLevelUISpan.timestamp ?? 0; | ||
const duration = end - start; | ||
|
||
expect(duration).toBeGreaterThanOrEqual(0.1); | ||
expect(duration).toBeLessThanOrEqual(0.15); | ||
}, | ||
); | ||
|
||
sentryTest( | ||
'should capture long animation frame for event listener.', | ||
async ({ browserName, getLocalTestPath, page }) => { | ||
// Long animation frames only work on chrome | ||
if (shouldSkipTracingTest() || browserName !== 'chromium') { | ||
sentryTest.skip(); | ||
} | ||
|
||
await page.route('**/path/to/script.js', (route: Route) => | ||
route.fulfill({ path: `${__dirname}/assets/script.js` }), | ||
); | ||
|
||
const url = await getLocalTestPath({ testDir: __dirname }); | ||
|
||
const promise = getFirstSentryEnvelopeRequest<Event>(page); | ||
|
||
await page.goto(url); | ||
|
||
// trigger long animation frame function | ||
await page.getByRole('button').click(); | ||
|
||
await new Promise(resolve => setTimeout(resolve, 1000)); | ||
|
||
const eventData = await promise; | ||
|
||
const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui.long-animation-frame')); | ||
|
||
expect(uiSpans?.length).toEqual(2); | ||
|
||
// ignore the first ui span (top-level long animation frame) | ||
const [, eventListenerUISpan] = uiSpans || []; | ||
|
||
expect(eventListenerUISpan).toEqual( | ||
expect.objectContaining({ | ||
op: 'ui.long-animation-frame', | ||
description: 'Main UI thread blocked', | ||
parent_span_id: eventData.contexts?.trace?.span_id, | ||
data: { | ||
'browser.script.invoker': 'BUTTON#clickme.onclick', | ||
'browser.script.invoker_type': 'event-listener', | ||
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'ui.long-animation-frame', | ||
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.browser.metrics', | ||
}, | ||
}), | ||
); | ||
const start = eventListenerUISpan.start_timestamp ?? 0; | ||
const end = eventListenerUISpan.timestamp ?? 0; | ||
const duration = end - start; | ||
|
||
expect(duration).toBeGreaterThanOrEqual(0.1); | ||
expect(duration).toBeLessThanOrEqual(0.15); | ||
}, | ||
); |
12 changes: 12 additions & 0 deletions
12
...ites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/assets/script.js
This file contains 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,12 @@ | ||
(() => { | ||
const startTime = Date.now(); | ||
|
||
function getElasped() { | ||
const time = Date.now(); | ||
return time - startTime; | ||
} | ||
|
||
while (getElasped() < 101) { | ||
// | ||
} | ||
})(); |
11 changes: 11 additions & 0 deletions
11
...-tests/suites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/init.js
This file contains 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; | ||
|
||
Sentry.init({ | ||
dsn: 'https://public@dsn.ingest.sentry.io/1337', | ||
integrations: [ | ||
Sentry.browserTracingIntegration({ enableLongTask: true, enableLongAnimationFrame: true, idleTimeout: 9000 }), | ||
], | ||
tracesSampleRate: 1, | ||
}); |
10 changes: 10 additions & 0 deletions
10
.../suites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/template.html
This file contains 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>Rendered Before Long Task</div> | ||
<script src="https://example.com/path/to/script.js"></script> | ||
</body> | ||
</html> |
27 changes: 27 additions & 0 deletions
27
...-tests/suites/tracing/browserTracingIntegration/long-animation-frame-non-chromium/test.ts
This file contains 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,27 @@ | ||
import type { Route } from '@playwright/test'; | ||
import { expect } from '@playwright/test'; | ||
import type { Event } from '@sentry/types'; | ||
|
||
import { sentryTest } from '../../../../utils/fixtures'; | ||
import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; | ||
|
||
sentryTest( | ||
'should not capture long animation frame or long task when browser is non-chromium', | ||
async ({ browserName, getLocalTestPath, page }) => { | ||
// Only test non-chromium browsers | ||
if (shouldSkipTracingTest() || browserName === 'chromium') { | ||
sentryTest.skip(); | ||
} | ||
|
||
await page.route('**/path/to/script.js', (route: Route) => | ||
route.fulfill({ path: `${__dirname}/assets/script.js` }), | ||
); | ||
|
||
const url = await getLocalTestPath({ testDir: __dirname }); | ||
|
||
const eventData = await getFirstSentryEnvelopeRequest<Event>(page, url); | ||
const uiSpans = eventData.spans?.filter(({ op }) => op?.startsWith('ui')); | ||
|
||
expect(uiSpans?.length).toBe(0); | ||
}, | ||
); |
25 changes: 25 additions & 0 deletions
25
...tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/assets/script.js
This file contains 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,25 @@ | ||
function getElapsed(startTime) { | ||
const time = Date.now(); | ||
return time - startTime; | ||
} | ||
|
||
function handleClick() { | ||
const startTime = Date.now(); | ||
while (getElapsed(startTime) < 105) { | ||
// | ||
} | ||
} | ||
|
||
function start() { | ||
const startTime = Date.now(); | ||
while (getElapsed(startTime) < 105) { | ||
// | ||
} | ||
} | ||
|
||
// trigger 2 long-animation-frame events | ||
// one from the top-level and the other from an event-listener | ||
start(); | ||
|
||
const button = document.getElementById('clickme'); | ||
button.addEventListener('click', handleClick); |
15 changes: 15 additions & 0 deletions
15
...s/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/init.js
This file contains 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,15 @@ | ||
import * as Sentry from '@sentry/browser'; | ||
|
||
window.Sentry = Sentry; | ||
|
||
Sentry.init({ | ||
dsn: 'https://public@dsn.ingest.sentry.io/1337', | ||
integrations: [ | ||
Sentry.browserTracingIntegration({ | ||
idleTimeout: 9000, | ||
enableLongTask: true, | ||
enableLongAnimationFrame: true, | ||
}), | ||
], | ||
tracesSampleRate: 1, | ||
}); |
13 changes: 13 additions & 0 deletions
13
...es/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/template.html
This file contains 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,13 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<meta charset="utf-8" /> | ||
</head> | ||
<body> | ||
<div>Rendered Before Long Animation Frame</div> | ||
<button id="clickme"> | ||
click me to start the long animation! | ||
</button> | ||
<script src="https://example.com/path/to/script.js"></script> | ||
</body> | ||
</html> |
Oops, something went wrong.