Skip to content

Commit 4e7d649

Browse files
committed
feat(browser): Extract long task and LoAF tracking into longTasksIntegration
`browserTracingIntegration` registers it by default, so nothing changes for its users. `browserTracingCoreIntegration` users only bundle it when they add it.
1 parent 90e1c2e commit 4e7d649

6 files changed

Lines changed: 79 additions & 19 deletions

File tree

‎packages/browser-utils/src/index.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export {
2424
export { elementTimingIntegration, startTrackingElementTiming } from './performance/elementTiming';
2525

2626
export { interactionsIntegration } from './performance/interactions';
27+
export { longTasksIntegration } from './performance/longTasks';
2728

2829
export { isBotUserAgent } from './isBotUserAgent';
2930

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import type { IntegrationFn } from '@sentry/core';
2+
import { defineIntegration, GLOBAL_OBJ } from '@sentry/core';
3+
import { startTrackingLongAnimationFrames, startTrackingLongTasks } from './entries';
4+
5+
interface LongTasksOptions {
6+
/**
7+
* If true, long tasks are captured as spans when long animation frames are disabled or not supported.
8+
*
9+
* Default: true
10+
*/
11+
enableLongTask?: boolean;
12+
13+
/**
14+
* If true, long animation frames are captured as spans in browsers that support them.
15+
*
16+
* Default: true
17+
*/
18+
enableLongAnimationFrame?: boolean;
19+
}
20+
21+
const INTEGRATION_NAME = 'LongTasks';
22+
23+
const _longTasksIntegration = ((options: LongTasksOptions = {}) => {
24+
const { enableLongTask = true, enableLongAnimationFrame = true } = options;
25+
26+
return {
27+
name: INTEGRATION_NAME,
28+
setup() {
29+
if (
30+
enableLongAnimationFrame &&
31+
GLOBAL_OBJ.PerformanceObserver &&
32+
PerformanceObserver.supportedEntryTypes?.includes('long-animation-frame')
33+
) {
34+
startTrackingLongAnimationFrames();
35+
} else if (enableLongTask) {
36+
startTrackingLongTasks();
37+
}
38+
},
39+
};
40+
}) satisfies IntegrationFn;
41+
42+
/**
43+
* Captures long animation frames, or long tasks where those are not supported, as spans on the active span.
44+
*/
45+
export const longTasksIntegration = defineIntegration(_longTasksIntegration);

‎packages/browser/src/index.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export {
5151
} from '@sentry/browser-utils';
5252
export type { CachedRouteProvider, RouteProvider } from '@sentry/browser-utils';
5353
export { interactionsIntegration } from './integrations/interactions';
54+
export { longTasksIntegration } from './integrations/longTasks';
5455

5556
export type { RequestInstrumentationOptions } from './tracing/request';
5657
export {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { longTasksIntegration } from '@sentry/browser-utils';

‎packages/browser/src/tracing/browserTracingIntegration.ts‎

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import {
1818
getClient,
1919
getCurrentScope,
2020
getDynamicSamplingContextFromSpan,
21-
GLOBAL_OBJ,
2221
hasSpansEnabled,
2322
hasSpanStreamingEnabled,
2423
NAVIGATION_SPAN_NAME_FALLBACK,
@@ -36,11 +35,10 @@ import {
3635
import { _INTERNAL_ensureBrowserSpanStreaming, startIdleSpan, startInactiveSpan } from '@sentry/core/browser';
3736
import {
3837
addHistoryInstrumentationHandler,
38+
longTasksIntegration,
3939
addPerformanceEntries,
4040
getLocationHref,
4141
isBotUserAgent,
42-
startTrackingLongAnimationFrames,
43-
startTrackingLongTasks,
4442
} from '@sentry/browser-utils';
4543
import { BROWSER_NAVIGATION_TYPE } from '@sentry/conventions/attributes';
4644
import { DEBUG_BUILD } from '../debug-build';
@@ -286,16 +284,17 @@ export interface BrowserTracingOptions {
286284
* Options for `browserTracingCoreIntegration`: the options of `browserTracingIntegration`, minus the ones
287285
* for features that `browserTracingIntegration` registers as standalone integrations.
288286
*/
289-
export type BrowserTracingCoreOptions = Omit<BrowserTracingOptions, 'enableInp' | 'webVitals' | 'enableElementTiming'>;
287+
export type BrowserTracingCoreOptions = Omit<
288+
BrowserTracingOptions,
289+
'enableInp' | 'webVitals' | 'enableElementTiming' | 'enableLongTask' | 'enableLongAnimationFrame'
290+
>;
290291

291292
const DEFAULT_BROWSER_TRACING_OPTIONS: BrowserTracingCoreOptions = {
292293
...TRACING_DEFAULTS,
293294
instrumentNavigation: true,
294295
instrumentBfcacheRestore: true,
295296
instrumentPageLoad: true,
296297
markBackgroundSpan: true,
297-
enableLongTask: true,
298-
enableLongAnimationFrame: true,
299298
ignoreResourceSpans: [],
300299
detectRedirects: true,
301300
linkPreviousTrace: 'in-memory',
@@ -324,7 +323,7 @@ export const browserTracingIntegration = ((options: Partial<BrowserTracingOption
324323
}
325324

326325
// oxlint-disable-next-line typescript/no-deprecated -- still honoured until it is removed
327-
const { enableInp = true, webVitals } = options;
326+
const { enableInp = true, webVitals, enableLongTask = true, enableLongAnimationFrame = true } = options;
328327

329328
return _browserTracingIntegration(options, client => {
330329
const ignore = webVitals?.ignore ?? [];
@@ -334,6 +333,10 @@ export const browserTracingIntegration = ((options: Partial<BrowserTracingOption
334333
ignore: enableInp || ignore.includes('inp') ? ignore : [...ignore, 'inp'],
335334
}),
336335
);
336+
337+
if (enableLongTask || enableLongAnimationFrame) {
338+
client.addIntegration(longTasksIntegration({ enableLongTask, enableLongAnimationFrame }));
339+
}
337340
});
338341
}) satisfies IntegrationFn;
339342

@@ -358,8 +361,6 @@ function _browserTracingIntegration(
358361
const optionalWindowDocument = WINDOW.document as (typeof WINDOW)['document'] | undefined;
359362

360363
const {
361-
enableLongTask,
362-
enableLongAnimationFrame,
363364
beforeStartSpan,
364365
idleTimeout,
365366
finalTimeout,
@@ -470,16 +471,6 @@ function _browserTracingIntegration(
470471

471472
registerSpanErrorInstrumentation();
472473

473-
if (
474-
enableLongAnimationFrame &&
475-
GLOBAL_OBJ.PerformanceObserver &&
476-
PerformanceObserver.supportedEntryTypes?.includes('long-animation-frame')
477-
) {
478-
startTrackingLongAnimationFrames();
479-
} else if (enableLongTask) {
480-
startTrackingLongTasks();
481-
}
482-
483474
if (detectRedirects && optionalWindowDocument) {
484475
const interactionHandler = (): void => {
485476
lastInteractionTimestamp = timestampInSeconds();

‎packages/browser/test/tracing/browserTracingIntegration.test.ts‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,27 @@ describe('browserTracingIntegration', () => {
225225
expect(client.getIntegrationByName('WebVitals')).toBeUndefined();
226226
});
227227

228+
it.each([
229+
['registers longTasksIntegration by default', {}, true],
230+
['registers longTasksIntegration with only long tasks enabled', { enableLongAnimationFrame: false }, true],
231+
[
232+
'does not register longTasksIntegration when both options are off',
233+
{ enableLongTask: false, enableLongAnimationFrame: false },
234+
false,
235+
],
236+
])('%s', (_name, options, registered) => {
237+
const client = new BrowserClient(
238+
getDefaultBrowserClientOptions({
239+
tracesSampleRate: 1,
240+
integrations: [browserTracingIntegration(options)],
241+
}),
242+
);
243+
setCurrentClient(client);
244+
client.init();
245+
246+
expect(!!client.getIntegrationByName('LongTasks')).toBe(registered);
247+
});
248+
228249
it('keeps the webVitalsIntegration the user supplies', () => {
229250
const userWebVitals = webVitalsModule.webVitalsIntegration({ softNavigations: false });
230251

0 commit comments

Comments
 (0)