Skip to content

Commit 60426d1

Browse files
authored
fix(nextjs): Stop sending transactions for requests that 404 (#4095)
1 parent 9f1374d commit 60426d1

File tree

4 files changed

+52
-4
lines changed

4 files changed

+52
-4
lines changed

packages/nextjs/src/index.client.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export function init(options: NextjsOptions): void {
3030
});
3131
configureScope(scope => {
3232
scope.setTag('runtime', 'browser');
33+
scope.addEventProcessor(event => (event.type === 'transaction' && event.transaction === '/404' ? null : event));
3334
});
3435
}
3536

packages/nextjs/src/index.server.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Carrier, getHubFromCarrier, getMainCarrier } from '@sentry/hub';
22
import { RewriteFrames } from '@sentry/integrations';
33
import { configureScope, getCurrentHub, init as nodeInit, Integrations } from '@sentry/node';
4+
import { Event } from '@sentry/types';
45
import { escapeStringForRegex, logger } from '@sentry/utils';
56
import * as domainModule from 'domain';
67
import * as path from 'path';
@@ -56,6 +57,8 @@ export function init(options: NextjsOptions): void {
5657
if (process.env.VERCEL) {
5758
scope.setTag('vercel', true);
5859
}
60+
61+
scope.addEventProcessor(filterTransactions);
5962
});
6063

6164
if (activeDomain) {
@@ -65,6 +68,8 @@ export function init(options: NextjsOptions): void {
6568
// apply the changes made by `nodeInit` to the domain's hub also
6669
domainHub.bindClient(globalHub.getClient());
6770
domainHub.getScope()?.update(globalHub.getScope());
71+
// `scope.update()` doesn’t copy over event processors, so we have to add it manually
72+
domainHub.getScope()?.addEventProcessor(filterTransactions);
6873

6974
// restore the domain hub as the current one
7075
domain.active = activeDomain;
@@ -107,6 +112,10 @@ function addServerIntegrations(options: NextjsOptions): void {
107112
}
108113
}
109114

115+
function filterTransactions(event: Event): Event | null {
116+
return event.type === 'transaction' && event.transaction === '/404' ? null : event;
117+
}
118+
110119
export { withSentryConfig } from './config';
111120
export { withSentry } from './utils/withSentry';
112121

packages/nextjs/test/index.client.test.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import { BaseClient } from '@sentry/core';
12
import { getCurrentHub } from '@sentry/hub';
23
import * as SentryReact from '@sentry/react';
34
import { Integrations as TracingIntegrations } from '@sentry/tracing';
45
import { Integration } from '@sentry/types';
5-
import { getGlobalObject } from '@sentry/utils';
6+
import { getGlobalObject, logger, SentryError } from '@sentry/utils';
67

78
import { init, Integrations, nextRouterInstrumentation } from '../src/index.client';
89
import { NextjsOptions } from '../src/utils/nextjsOptions';
@@ -12,10 +13,12 @@ const { BrowserTracing } = TracingIntegrations;
1213
const global = getGlobalObject();
1314

1415
const reactInit = jest.spyOn(SentryReact, 'init');
16+
const captureEvent = jest.spyOn(BaseClient.prototype, 'captureEvent');
17+
const logError = jest.spyOn(logger, 'error');
1518

1619
describe('Client init()', () => {
1720
afterEach(() => {
18-
reactInit.mockClear();
21+
jest.clearAllMocks();
1922
global.__SENTRY__.hub = undefined;
2023
});
2124

@@ -50,6 +53,22 @@ describe('Client init()', () => {
5053
expect(currentScope._tags).toEqual({ runtime: 'browser' });
5154
});
5255

56+
it('adds 404 transaction filter', () => {
57+
init({
58+
dsn: 'https://[email protected]/12312012',
59+
tracesSampleRate: 1.0,
60+
});
61+
const hub = getCurrentHub();
62+
const sendEvent = jest.spyOn(hub.getClient()!.getTransport!(), 'sendEvent');
63+
64+
const transaction = hub.startTransaction({ name: '/404' });
65+
transaction.finish();
66+
67+
expect(sendEvent).not.toHaveBeenCalled();
68+
expect(captureEvent.mock.results[0].value).toBeUndefined();
69+
expect(logError).toHaveBeenCalledWith(new SentryError('An event processor returned null, will not send event.'));
70+
});
71+
5372
describe('integrations', () => {
5473
it('does not add BrowserTracing integration by default if tracesSampleRate is not set', () => {
5574
init({});

packages/nextjs/test/index.server.test.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import { BaseClient } from '@sentry/core';
12
import { RewriteFrames } from '@sentry/integrations';
23
import * as SentryNode from '@sentry/node';
34
import { getCurrentHub, NodeClient } from '@sentry/node';
45
import { Integration } from '@sentry/types';
5-
import { getGlobalObject } from '@sentry/utils';
6+
import { getGlobalObject, logger, SentryError } from '@sentry/utils';
67
import * as domain from 'domain';
78

89
import { init } from '../src/index.server';
@@ -16,10 +17,12 @@ const global = getGlobalObject();
1617
(global as typeof global & { __rewriteFramesDistDir__: string }).__rewriteFramesDistDir__ = '.next';
1718

1819
const nodeInit = jest.spyOn(SentryNode, 'init');
20+
const captureEvent = jest.spyOn(BaseClient.prototype, 'captureEvent');
21+
const logError = jest.spyOn(logger, 'error');
1922

2023
describe('Server init()', () => {
2124
afterEach(() => {
22-
nodeInit.mockClear();
25+
jest.clearAllMocks();
2326
global.__SENTRY__.hub = undefined;
2427
});
2528

@@ -87,6 +90,22 @@ describe('Server init()', () => {
8790
expect(currentScope._tags.vercel).toBeUndefined();
8891
});
8992

93+
it('adds 404 transaction filter', () => {
94+
init({
95+
dsn: 'https://[email protected]/12312012',
96+
tracesSampleRate: 1.0,
97+
});
98+
const hub = getCurrentHub();
99+
const sendEvent = jest.spyOn(hub.getClient()!.getTransport!(), 'sendEvent');
100+
101+
const transaction = hub.startTransaction({ name: '/404' });
102+
transaction.finish();
103+
104+
expect(sendEvent).not.toHaveBeenCalled();
105+
expect(captureEvent.mock.results[0].value).toBeUndefined();
106+
expect(logError).toHaveBeenCalledWith(new SentryError('An event processor returned null, will not send event.'));
107+
});
108+
90109
it("initializes both global hub and domain hub when there's an active domain", () => {
91110
const globalHub = getCurrentHub();
92111
const local = domain.create();

0 commit comments

Comments
 (0)