Skip to content

feat(browser): option to ignore certain resource types #16389

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 8 commits into from
May 26, 2025
Merged
Show file tree
Hide file tree
Changes from 4 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(() => {
// I do nothing.
})();
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as Sentry from '@sentry/browser';

window.Sentry = Sentry;

Sentry.init({
dsn: 'https://[email protected]/1337',
integrations: [
Sentry.browserTracingIntegration({
ignoreResourceSpans: ['resource.script'],
idleTimeout: 9000,
}),
],
tracesSampleRate: 1,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { Route } from '@playwright/test';
import { expect } from '@playwright/test';
import type { Event } from '@sentry/core';
import { sentryTest } from '../../../../utils/fixtures';
import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers';

sentryTest('should allow specific types of resource spans to be ignored.', async ({ getLocalTestUrl, page }) => {
if (shouldSkipTracingTest()) {
sentryTest.skip();
}

await page.route('**/path/to/script.js', (route: Route) => route.fulfill({ path: `${__dirname}/assets/script.js` }));

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

const eventData = await getFirstSentryEnvelopeRequest<Event>(page, url);
const allSpans = eventData.spans?.filter(({ op }) => op?.startsWith('resource.script'));

expect(allSpans?.length).toBe(0);
});
25 changes: 23 additions & 2 deletions packages/browser-utils/src/metrics/browserMetrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,13 @@ interface AddPerformanceEntriesOptions {
* sent as a standalone span instead.
*/
recordClsOnPageloadSpan: boolean;

/**
* Resource spans matching strings in the array will not be emitted.
*
* Default: []
*/
ignoreResourceSpans: Array<string>;
}

/** Add performance related spans to a transaction */
Expand Down Expand Up @@ -355,7 +362,15 @@ export function addPerformanceEntries(span: Span, options: AddPerformanceEntries
break;
}
case 'resource': {
_addResourceSpans(span, entry as PerformanceResourceTiming, entry.name, startTime, duration, timeOrigin);
_addResourceSpans(
span,
entry as PerformanceResourceTiming,
entry.name,
startTime,
duration,
timeOrigin,
options.ignoreResourceSpans,
);
break;
}
// Ignore other entry types.
Expand Down Expand Up @@ -568,13 +583,19 @@ export function _addResourceSpans(
startTime: number,
duration: number,
timeOrigin: number,
ignoreResourceSpans?: Array<string>,
): void {
// we already instrument based on fetch and xhr, so we don't need to
// duplicate spans here.
if (entry.initiatorType === 'xmlhttprequest' || entry.initiatorType === 'fetch') {
return;
}

const op = entry.initiatorType ? `resource.${entry.initiatorType}` : 'resource.other';
if (ignoreResourceSpans?.includes(op)) {
return;
}

const parsedUrl = parseUrl(resourceUrl);

const attributes: SpanAttributes = {
Expand Down Expand Up @@ -616,7 +637,7 @@ export function _addResourceSpans(

startAndEndSpan(span, startTimestamp, endTimestamp, {
name: resourceUrl.replace(WINDOW.location.origin, ''),
op: entry.initiatorType ? `resource.${entry.initiatorType}` : 'resource.other',
op,
attributes,
});
}
Expand Down
47 changes: 47 additions & 0 deletions packages/browser-utils/test/browser/browserMetrics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,53 @@ describe('_addResourceSpans', () => {
}
});

it('allows resource spans to be ignored via ignoreResourceSpans', () => {
const spans: Span[] = [];
const ignoredResourceSpans = ['resource.other', 'resource.script'];

getClient()?.on('spanEnd', span => {
spans.push(span);
});

const table = [
{
initiatorType: undefined,
op: 'resource.other',
},
{
initiatorType: 'css',
op: 'resource.css',
},
{
initiatorType: 'css',
op: 'resource.css',
},
{
initiatorType: 'image',
op: 'resource.image',
},
{
initiatorType: 'script',
op: 'resource.script',
},
];
for (const row of table) {
const { initiatorType } = row;
const entry = mockPerformanceResourceTiming({
initiatorType,
nextHopProtocol: 'http/1.1',
});
_addResourceSpans(span, entry, 'https://example.com/assets/to/me', 123, 234, 465, ignoredResourceSpans);
}
expect(spans).toHaveLength(table.length - ignoredResourceSpans.length);
const spanOps = new Set(
spans.map(s => {
return spanToJSON(s).op;
}),
);
expect(spanOps).toEqual(new Set(['resource.css', 'resource.image']));
});

it('allows for enter size of 0', () => {
const spans: Span[] = [];

Expand Down
11 changes: 10 additions & 1 deletion packages/browser/src/tracing/browserTracingIntegration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,13 @@ export interface BrowserTracingOptions {
*/
enableHTTPTimings: boolean;

/**
* Resource spans matching strings in the array will not be emitted.
*
* Default: []
*/
ignoreResourceSpans: Array<string>;

/**
* Link the currently started trace to a previous trace (e.g. a prior pageload, navigation or
* manually started span). When enabled, this option will allow you to navigate between traces
Expand Down Expand Up @@ -226,6 +233,7 @@ const DEFAULT_BROWSER_TRACING_OPTIONS: BrowserTracingOptions = {
enableLongTask: true,
enableLongAnimationFrame: true,
enableInp: true,
ignoreResourceSpans: [],
linkPreviousTrace: 'in-memory',
consistentTraceSampling: false,
_experiments: {},
Expand Down Expand Up @@ -268,6 +276,7 @@ export const browserTracingIntegration = ((_options: Partial<BrowserTracingOptio
trackFetchStreamPerformance,
shouldCreateSpanForRequest,
enableHTTPTimings,
ignoreResourceSpans,
instrumentPageLoad,
instrumentNavigation,
linkPreviousTrace,
Expand Down Expand Up @@ -310,7 +319,7 @@ export const browserTracingIntegration = ((_options: Partial<BrowserTracingOptio
// This will generally always be defined here, because it is set in `setup()` of the integration
// but technically, it is optional, so we guard here to be extra safe
_collectWebVitals?.();
addPerformanceEntries(span, { recordClsOnPageloadSpan: !enableStandaloneClsSpans });
addPerformanceEntries(span, { recordClsOnPageloadSpan: !enableStandaloneClsSpans, ignoreResourceSpans });
setActiveIdleSpan(client, undefined);

// A trace should stay consistent over the entire timespan of one route - even after the pageload/navigation ended.
Expand Down