Skip to content
Merged
3 changes: 2 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ jobs:
with:
node-version-file: 'package.json'
- name: Set up Deno
uses: denoland/setup-deno@v2.0.2
uses: denoland/setup-deno@v2.0.3
with:
deno-version: v2.1.5
- name: Restore caches
Expand Down Expand Up @@ -886,6 +886,7 @@ jobs:
- uses: pnpm/action-setup@v4
with:
version: 9.4.0
# TODO: Remove this once the repo is bumped to 20.19.2 or higher
- name: Set up Node for Angular 20
if: matrix.test-application == 'angular-20'
uses: actions/setup-node@v4
Expand Down
11 changes: 10 additions & 1 deletion .github/workflows/canary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ jobs:
fail-fast: false
matrix:
include:
- test-application: 'angular-20'
build-command: 'test:build-canary'
label: 'angular-20 (next)'
- test-application: 'create-react-app'
build-command: 'test:build-canary'
label: 'create-react-app (canary)'
Expand Down Expand Up @@ -123,8 +126,14 @@ jobs:
- uses: pnpm/action-setup@v4
with:
version: 9.4.0

# TODO: Remove this once the repo is bumped to 20.19.2 or higher
- name: Set up Node for Angular 20
if: matrix.test-application == 'angular-20'
uses: actions/setup-node@v4
with:
node-version: '20.19.2'
- name: Set up Node
if: matrix.test-application != 'angular-20'
uses: actions/setup-node@v4
with:
node-version-file: 'dev-packages/e2e-tests/package.json'
Expand Down
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,31 @@

- "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott

## 9.25.0

### Important Changes

- **feat(browser): Add option to ignore `mark` and `measure` spans ([#16443](https://github.com/getsentry/sentry-javascript/pull/16443))**

This release adds an option to `browserTracingIntegration` that lets you ignore
`mark` and `measure` spans created from the `performance.mark(...)` and `performance.measure(...)` browser APIs:

```js
Sentry.init({
integrations: [
Sentry.browserTracingIntegration({
ignorePerformanceApiSpans: ['measure-to-ignore', /mark-to-ignore/],
}),
],
});
```

### Other Changes

- feat(browser): Export getTraceData from the browser sdks ([#16433](https://github.com/getsentry/sentry-javascript/pull/16433))
- feat(node): Add `includeServerName` option ([#16442](https://github.com/getsentry/sentry-javascript/pull/16442))
- fix(nuxt): Remove setting `@sentry/nuxt` external ([#16444](https://github.com/getsentry/sentry-javascript/pull/16444))

## 9.24.0

### Important Changes
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as Sentry from '@sentry/browser';

window.Sentry = Sentry;

Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
integrations: [
Sentry.browserTracingIntegration({
ignorePerformanceApiSpans: ['measure-ignore', /mark-i/],
idleTimeout: 9000,
}),
],
tracesSampleRate: 1,
});

performance.mark('mark-pass');
performance.mark('mark-ignore');
performance.measure('measure-pass');
performance.measure('measure-ignore');
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import type { Route } from '@playwright/test';
import { expect } from '@playwright/test';
import { sentryTest } from '../../../../utils/fixtures';
import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../utils/helpers';

sentryTest(
'should ignore mark and measure spans that match `ignorePerformanceApiSpans`',
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 transactionRequestPromise = waitForTransactionRequest(
page,
evt => evt.type === 'transaction' && evt.contexts?.trace?.op === 'pageload',
);

await page.goto(url);

const transactionEvent = envelopeRequestParser(await transactionRequestPromise);
const markAndMeasureSpans = transactionEvent.spans?.filter(({ op }) => op && ['mark', 'measure'].includes(op));

expect(markAndMeasureSpans?.length).toBe(3);
expect(markAndMeasureSpans).toEqual(
expect.arrayContaining([
expect.objectContaining({
description: 'mark-pass',
op: 'mark',
}),
expect.objectContaining({
description: 'measure-pass',
op: 'measure',
}),
expect.objectContaining({
description: 'sentry-tracing-init',
op: 'mark',
}),
]),
);
},
);
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"watch": "ng build --watch --configuration development",
"test": "playwright test",
"test:build": "pnpm install && pnpm build",
"test:build-canary": "pnpm install && pnpm add @angular/animations@next @angular/common@next @angular/compiler@next @angular/core@next @angular/forms@next @angular/platform-browser@next @angular/platform-browser-dynamic@next @angular/router@next && pnpm add -D @angular-devkit/build-angular@next @angular/cli@next @angular/compiler-cli@next && pnpm build",
"test:assert": "playwright test",
"clean": "npx rimraf .angular node_modules pnpm-lock.yaml dist"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"@sentry/core": "latest || *",
"@sentry/opentelemetry": "latest || *",
"@types/node": "^18.19.1",
"fastify": "4.29.0",
"fastify": "4.29.1",
"typescript": "5.6.3",
"ts-node": "10.9.2"
},
Expand Down
20 changes: 19 additions & 1 deletion packages/browser-utils/src/metrics/browserMetrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
setMeasurement,
spanToJSON,
stringMatchesSomePattern,
} from '@sentry/core';
import { WINDOW } from '../types';
import { trackClsAsStandaloneSpan } from './cls';
Expand Down Expand Up @@ -307,6 +308,15 @@ interface AddPerformanceEntriesOptions {
* Default: []
*/
ignoreResourceSpans: Array<'resouce.script' | 'resource.css' | 'resource.img' | 'resource.other' | string>;

/**
* Performance spans created from browser Performance APIs,
* `performance.mark(...)` nand `performance.measure(...)`
* with `name`s matching strings in the array will not be emitted.
*
* Default: []
*/
ignorePerformanceApiSpans: Array<string | RegExp>;
}

/** Add performance related spans to a transaction */
Expand Down Expand Up @@ -346,7 +356,7 @@ export function addPerformanceEntries(span: Span, options: AddPerformanceEntries
case 'mark':
case 'paint':
case 'measure': {
_addMeasureSpans(span, entry, startTime, duration, timeOrigin);
_addMeasureSpans(span, entry, startTime, duration, timeOrigin, options.ignorePerformanceApiSpans);

// capture web vitals
const firstHidden = getVisibilityWatcher();
Expand Down Expand Up @@ -440,7 +450,15 @@ export function _addMeasureSpans(
startTime: number,
duration: number,
timeOrigin: number,
ignorePerformanceApiSpans: AddPerformanceEntriesOptions['ignorePerformanceApiSpans'],
): void {
if (
['mark', 'measure'].includes(entry.entryType) &&
stringMatchesSomePattern(entry.name, ignorePerformanceApiSpans)
) {
return;
}

const navEntry = getNavigationEntry(false);
const requestTime = msToSec(navEntry ? navEntry.requestStart : 0);
// Because performance.measure accepts arbitrary timestamps it can produce
Expand Down
69 changes: 67 additions & 2 deletions packages/browser-utils/test/browser/browserMetrics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ describe('_addMeasureSpans', () => {
const startTime = 23;
const duration = 356;

_addMeasureSpans(span, entry, startTime, duration, timeOrigin);
_addMeasureSpans(span, entry, startTime, duration, timeOrigin, []);

expect(spans).toHaveLength(1);
expect(spanToJSON(spans[0]!)).toEqual(
Expand Down Expand Up @@ -112,10 +112,75 @@ describe('_addMeasureSpans', () => {
const startTime = 23;
const duration = -50;

_addMeasureSpans(span, entry, startTime, duration, timeOrigin);
_addMeasureSpans(span, entry, startTime, duration, timeOrigin, []);

expect(spans).toHaveLength(0);
});

it('ignores performance spans that match ignorePerformanceApiSpans', () => {
const pageloadSpan = new SentrySpan({ op: 'pageload', name: '/', sampled: true });
const spans: Span[] = [];

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

const entries: PerformanceEntry[] = [
{
entryType: 'measure',
name: 'measure-pass',
duration: 10,
startTime: 12,
toJSON: () => ({}),
},
{
entryType: 'measure',
name: 'measure-ignore',
duration: 10,
startTime: 12,
toJSON: () => ({}),
},
{
entryType: 'mark',
name: 'mark-pass',
duration: 0,
startTime: 12,
toJSON: () => ({}),
},
{
entryType: 'mark',
name: 'mark-ignore',
duration: 0,
startTime: 12,
toJSON: () => ({}),
},
{
entryType: 'paint',
name: 'mark-ignore',
duration: 0,
startTime: 12,
toJSON: () => ({}),
},
];

const timeOrigin = 100;
const startTime = 23;
const duration = 356;

entries.forEach(e => {
_addMeasureSpans(pageloadSpan, e, startTime, duration, timeOrigin, ['measure-i', /mark-ign/]);
});

expect(spans).toHaveLength(3);
expect(spans.map(spanToJSON)).toEqual(
expect.arrayContaining([
expect.objectContaining({ description: 'measure-pass', op: 'measure' }),
expect.objectContaining({ description: 'mark-pass', op: 'mark' }),
// name matches but type is not (mark|measure) => should not be ignored
expect.objectContaining({ description: 'mark-ignore', op: 'paint' }),
]),
);
});
});

describe('_addResourceSpans', () => {
Expand Down
1 change: 1 addition & 0 deletions packages/browser/src/exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export {
setCurrentClient,
Scope,
continueTrace,
getTraceData,
suppressTracing,
SDK_VERSION,
setContext,
Expand Down
45 changes: 43 additions & 2 deletions packages/browser/src/tracing/browserTracingIntegration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,42 @@ export interface BrowserTracingOptions {
*
* Default: []
*/
ignoreResourceSpans: Array<string>;
ignoreResourceSpans: Array<'resouce.script' | 'resource.css' | 'resource.img' | 'resource.other' | string>;

/**
* Spans created from the following browser Performance APIs,
*
* - [`performance.mark(...)`](https://developer.mozilla.org/en-US/docs/Web/API/Performance/mark)
* - [`performance.measure(...)`](https://developer.mozilla.org/en-US/docs/Web/API/Performance/measure)
*
* will not be emitted if their names match strings in this array.
*
* This is useful, if you come across `mark` or `measure` spans in your Sentry traces
* that you want to ignore. For example, sometimes, browser extensions or libraries
* emit these entries on their own, which might not be relevant to your application.
*
* * @example
* ```ts
* Sentry.init({
* integrations: [
* Sentry.browserTracingIntegration({
* ignorePerformanceApiSpans: ['myMeasurement', /myMark/],
* }),
* ],
* });
*
* // no spans will be created for these:
* performance.mark('myMark');
* performance.measure('myMeasurement');
*
* // spans will be created for these:
* performance.mark('authenticated');
* performance.measure('input-duration', ...);
* ```
*
* Default: [] - By default, all `mark` and `measure` entries are sent as spans.
*/
ignorePerformanceApiSpans: Array<string | RegExp>;

/**
* Link the currently started trace to a previous trace (e.g. a prior pageload, navigation or
Expand Down Expand Up @@ -234,6 +269,7 @@ const DEFAULT_BROWSER_TRACING_OPTIONS: BrowserTracingOptions = {
enableLongAnimationFrame: true,
enableInp: true,
ignoreResourceSpans: [],
ignorePerformanceApiSpans: [],
linkPreviousTrace: 'in-memory',
consistentTraceSampling: false,
_experiments: {},
Expand Down Expand Up @@ -277,6 +313,7 @@ export const browserTracingIntegration = ((_options: Partial<BrowserTracingOptio
shouldCreateSpanForRequest,
enableHTTPTimings,
ignoreResourceSpans,
ignorePerformanceApiSpans,
instrumentPageLoad,
instrumentNavigation,
linkPreviousTrace,
Expand Down Expand Up @@ -319,7 +356,11 @@ 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, ignoreResourceSpans });
addPerformanceEntries(span, {
recordClsOnPageloadSpan: !enableStandaloneClsSpans,
ignoreResourceSpans,
ignorePerformanceApiSpans,
});
setActiveIdleSpan(client, undefined);

// A trace should stay consistent over the entire timespan of one route - even after the pageload/navigation ended.
Expand Down
6 changes: 5 additions & 1 deletion packages/node/src/sdk/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ export class NodeClient extends ServerRuntimeClient<NodeClientOptions> {
private _logOnExitFlushListener: (() => void) | undefined;

public constructor(options: NodeClientOptions) {
const serverName = options.serverName || global.process.env.SENTRY_NAME || os.hostname();
const serverName =
options.includeServerName === false
? undefined
: options.serverName || global.process.env.SENTRY_NAME || os.hostname();

const clientOptions: ServerRuntimeClientOptions = {
...options,
platform: 'node',
Expand Down
Loading
Loading