Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
(() => {
// I do nothing.
})();
Original file line numberDiff line numberDiff 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 numberDiff line numberDiff 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);
});
Original file line numberDiff line numberDiff line change
Expand Up@@ -300,6 +300,13 @@ interface AddPerformanceEntriesOptions {
* sent as a standalone span instead.
*/
recordClsOnPageloadSpan: boolean;

/**
* Resource spans with `op`s matching strings in the array will not be emitted.
*
* Default: []
*/
ignoreResourceSpans: Array<'resouce.script' | 'resource.css' | 'resource.img' | 'resource.other' | string>;
}

/** Add performance related spans to a transaction */
Expand DownExpand 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 DownExpand 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 DownExpand 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
Original file line numberDiff line numberDiff 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
Original file line numberDiff line numberDiff line change
Expand Up@@ -144,6 +144,13 @@ export interface BrowserTracingOptions {
*/
enableHTTPTimings: boolean;

/**
* Resource spans with `op`s 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 DownExpand 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 DownExpand Up@@ -268,6 +276,7 @@ export const browserTracingIntegration = ((_options: Partial<BrowserTracingOptio
trackFetchStreamPerformance,
shouldCreateSpanForRequest,
enableHTTPTimings,
ignoreResourceSpans,
instrumentPageLoad,
instrumentNavigation,
linkPreviousTrace,
Expand DownExpand 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
Loading