Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/browser-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export {
export { elementTimingIntegration, startTrackingElementTiming } from './performance/elementTiming';

export { interactionsIntegration } from './performance/interactions';
export { longTasksIntegration } from './performance/longTasks';

export { isBotUserAgent } from './isBotUserAgent';

Expand Down
45 changes: 45 additions & 0 deletions packages/browser-utils/src/performance/longTasks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type { IntegrationFn } from '@sentry/core';
import { defineIntegration, GLOBAL_OBJ } from '@sentry/core';
import { startTrackingLongAnimationFrames, startTrackingLongTasks } from './entries';

interface LongTasksOptions {
/**
* If true, long tasks are captured as spans when long animation frames are disabled or not supported.
*
* Default: true
*/
enableLongTask?: boolean;

/**
* If true, long animation frames are captured as spans in browsers that support them.
*
* Default: true
*/
enableLongAnimationFrame?: boolean;
}

const INTEGRATION_NAME = 'LongTasks';

const _longTasksIntegration = ((options: LongTasksOptions = {}) => {
const { enableLongTask = true, enableLongAnimationFrame = true } = options;

return {
name: INTEGRATION_NAME,
setup() {
if (
enableLongAnimationFrame &&
GLOBAL_OBJ.PerformanceObserver &&
PerformanceObserver.supportedEntryTypes?.includes('long-animation-frame')
) {
startTrackingLongAnimationFrames();
} else if (enableLongTask) {
startTrackingLongTasks();
}
},
};
}) satisfies IntegrationFn;

/**
* Captures long animation frames, or long tasks where those are not supported, as spans on the active span.
*/
export const longTasksIntegration = defineIntegration(_longTasksIntegration);
1 change: 1 addition & 0 deletions packages/browser/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export {
} from '@sentry/browser-utils';
export type { CachedRouteProvider, RouteProvider } from '@sentry/browser-utils';
export { interactionsIntegration } from './integrations/interactions';
export { longTasksIntegration } from './integrations/longTasks';

export type { RequestInstrumentationOptions } from './tracing/request';
export {
Expand Down
1 change: 1 addition & 0 deletions packages/browser/src/integrations/longTasks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { longTasksIntegration } from '@sentry/browser-utils';
29 changes: 10 additions & 19 deletions packages/browser/src/tracing/browserTracingIntegration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import {
getClient,
getCurrentScope,
getDynamicSamplingContextFromSpan,
GLOBAL_OBJ,
hasSpansEnabled,
hasSpanStreamingEnabled,
NAVIGATION_SPAN_NAME_FALLBACK,
Expand All @@ -36,11 +35,10 @@ import {
import { _INTERNAL_ensureBrowserSpanStreaming, startIdleSpan, startInactiveSpan } from '@sentry/core/browser';
import {
addHistoryInstrumentationHandler,
longTasksIntegration,
addPerformanceEntries,
getLocationHref,
isBotUserAgent,
startTrackingLongAnimationFrames,
startTrackingLongTasks,
} from '@sentry/browser-utils';
import { BROWSER_NAVIGATION_TYPE } from '@sentry/conventions/attributes';
import { DEBUG_BUILD } from '../debug-build';
Expand Down Expand Up @@ -286,16 +284,17 @@ export interface BrowserTracingOptions {
* Options for `browserTracingCoreIntegration`: the options of `browserTracingIntegration`, minus the ones
* for features that `browserTracingIntegration` registers as standalone integrations.
*/
export type BrowserTracingCoreOptions = Omit<BrowserTracingOptions, 'enableInp' | 'webVitals' | 'enableElementTiming'>;
export type BrowserTracingCoreOptions = Omit<
BrowserTracingOptions,
'enableInp' | 'webVitals' | 'enableElementTiming' | 'enableLongTask' | 'enableLongAnimationFrame'
>;

const DEFAULT_BROWSER_TRACING_OPTIONS: BrowserTracingCoreOptions = {
...TRACING_DEFAULTS,
instrumentNavigation: true,
instrumentBfcacheRestore: true,
instrumentPageLoad: true,
markBackgroundSpan: true,
enableLongTask: true,
enableLongAnimationFrame: true,
ignoreResourceSpans: [],
detectRedirects: true,
linkPreviousTrace: 'in-memory',
Expand Down Expand Up @@ -324,7 +323,7 @@ export const browserTracingIntegration = ((options: Partial<BrowserTracingOption
}

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

return _browserTracingIntegration(options, client => {
// Custom clients and test doubles may not implement it.
Expand All @@ -339,6 +338,10 @@ export const browserTracingIntegration = ((options: Partial<BrowserTracingOption
ignore: enableInp || ignore.includes('inp') ? ignore : [...ignore, 'inp'],
}),
);

if (enableLongTask || enableLongAnimationFrame) {
client.addIntegration(longTasksIntegration({ enableLongTask, enableLongAnimationFrame }));
}
});
}) satisfies IntegrationFn;

Expand All @@ -363,8 +366,6 @@ function _browserTracingIntegration(
const optionalWindowDocument = WINDOW.document as (typeof WINDOW)['document'] | undefined;

const {
enableLongTask,
enableLongAnimationFrame,
beforeStartSpan,
idleTimeout,
finalTimeout,
Expand Down Expand Up @@ -475,16 +476,6 @@ function _browserTracingIntegration(

registerSpanErrorInstrumentation();

if (
enableLongAnimationFrame &&
GLOBAL_OBJ.PerformanceObserver &&
PerformanceObserver.supportedEntryTypes?.includes('long-animation-frame')
) {
startTrackingLongAnimationFrames();
} else if (enableLongTask) {
startTrackingLongTasks();
}

if (detectRedirects && optionalWindowDocument) {
const interactionHandler = (): void => {
lastInteractionTimestamp = timestampInSeconds();
Expand Down
21 changes: 21 additions & 0 deletions packages/browser/test/tracing/browserTracingIntegration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,27 @@ describe('browserTracingIntegration', () => {
expect(client.getIntegrationByName('WebVitals')).toBeUndefined();
});

it.each([
['registers longTasksIntegration by default', {}, true],
['registers longTasksIntegration with only long tasks enabled', { enableLongAnimationFrame: false }, true],
[
'does not register longTasksIntegration when both options are off',
{ enableLongTask: false, enableLongAnimationFrame: false },
false,
],
])('%s', (_name, options, registered) => {
const client = new BrowserClient(
getDefaultBrowserClientOptions({
tracesSampleRate: 1,
integrations: [browserTracingIntegration(options)],
}),
);
setCurrentClient(client);
client.init();

expect(!!client.getIntegrationByName('LongTasks')).toBe(registered);
});

it('keeps the webVitalsIntegration the user supplies', () => {
const userWebVitals = webVitalsModule.webVitalsIntegration({ softNavigations: false });

Expand Down
Loading