Skip to content

Commit

Permalink
feat(nuxt): Add filter for not found source maps (devtools) (#14437)
Browse files Browse the repository at this point in the history
Dev tools sometimes try to access source maps. This filter makes sure
this error is not reported in Sentry as it adds noise to the data.
  • Loading branch information
s1gr1d authored Nov 22, 2024
1 parent 6fbab43 commit 05479b8
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 22 deletions.
64 changes: 43 additions & 21 deletions packages/nuxt/src/server/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,30 +26,52 @@ export function init(options: SentryNuxtServerOptions): Client | undefined {

const client = initNode(sentryOptions);

getGlobalScope().addEventProcessor(
Object.assign(
(event => {
if (event.type === 'transaction') {
// Filter out transactions for Nuxt build assets
// This regex matches the default path to the nuxt-generated build assets (`_nuxt`).
// todo: the buildAssetDir could be changed in the nuxt config - change this to a more generic solution
if (event.transaction?.match(/^GET \/_nuxt\//)) {
options.debug &&
DEBUG_BUILD &&
logger.log('NuxtLowQualityTransactionsFilter filtered transaction: ', event.transaction);
return null;
}
getGlobalScope().addEventProcessor(lowQualityTransactionsFilter(options));
getGlobalScope().addEventProcessor(clientSourceMapErrorFilter(options));

return event;
} else {
return event;
}
}) satisfies EventProcessor,
{ id: 'NuxtLowQualityTransactionsFilter' },
),
return client;
}

/**
* Filter out transactions for Nuxt build assets
* This regex matches the default path to the nuxt-generated build assets (`_nuxt`).
*
* Only exported for testing
*/
export function lowQualityTransactionsFilter(options: SentryNuxtServerOptions): EventProcessor {
return Object.assign(
(event => {
if (event.type === 'transaction' && event.transaction?.match(/^GET \/_nuxt\//)) {
// todo: the buildAssetDir could be changed in the nuxt config - change this to a more generic solution
options.debug &&
DEBUG_BUILD &&
logger.log('NuxtLowQualityTransactionsFilter filtered transaction: ', event.transaction);
return null;
} else {
return event;
}
}) satisfies EventProcessor,
{ id: 'NuxtLowQualityTransactionsFilter' },
);
}

return client;
/**
* The browser devtools try to get the source maps, but as client source maps may not be available there is going to be an error (no problem for the application though).
*
* Only exported for testing
*/
export function clientSourceMapErrorFilter(options: SentryNuxtServerOptions): EventProcessor {
return Object.assign(
(event => {
const errorMsg = event.exception?.values?.[0]?.value;
if (errorMsg?.match(/^ENOENT: no such file or directory, open '.*\/_nuxt\/.*\.js\.map'/)) {
options.debug && DEBUG_BUILD && logger.log('NuxtClientSourceMapErrorFilter filtered error: ', errorMsg);
return null;
}
return event;
}) satisfies EventProcessor,
{ id: 'NuxtClientSourceMapErrorFilter' },
);
}

function getNuxtDefaultIntegrations(options: NodeOptions): Integration[] {
Expand Down
87 changes: 86 additions & 1 deletion packages/nuxt/test/server/sdk.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import * as SentryNode from '@sentry/node';
import type { NodeClient } from '@sentry/node';
import { Scope } from '@sentry/node';
import { getGlobalScope } from '@sentry/node';
import { SDK_VERSION } from '@sentry/node';
import type { EventProcessor } from '@sentry/types';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import type { SentryNuxtServerOptions } from '../../src/common/types';
import { init } from '../../src/server';
import { mergeRegisterEsmLoaderHooks } from '../../src/server/sdk';
import { clientSourceMapErrorFilter, mergeRegisterEsmLoaderHooks } from '../../src/server/sdk';

const nodeInit = vi.spyOn(SentryNode, 'init');

Expand Down Expand Up @@ -83,6 +86,88 @@ describe('Nuxt Server SDK', () => {
expect.any(Object),
);
});

it('registers an event processor', async () => {
let passedEventProcessors: EventProcessor[] = [];
const addEventProcessor = vi
.spyOn(getGlobalScope(), 'addEventProcessor')
.mockImplementation((eventProcessor: EventProcessor) => {
passedEventProcessors = [...passedEventProcessors, eventProcessor];
return new Scope();
});

init({
dsn: 'https://[email protected]/1337',
});

expect(addEventProcessor).toHaveBeenCalledTimes(2);
expect(passedEventProcessors[0]?.id).toEqual('NuxtLowQualityTransactionsFilter');
expect(passedEventProcessors[1]?.id).toEqual('NuxtClientSourceMapErrorFilter');
});
});

describe('clientSourceMapErrorFilter', () => {
const options = { debug: false };
const filter = clientSourceMapErrorFilter(options);

describe('filters out errors', () => {
it.each([
[
'source map errors with leading /',
{
exception: { values: [{ value: "ENOENT: no such file or directory, open '/path/to/_nuxt/file.js.map'" }] },
},
],
[
'source map errors without leading /',
{ exception: { values: [{ value: "ENOENT: no such file or directory, open 'path/to/_nuxt/file.js.map'" }] } },
],
[
'source map errors with long path',
{
exception: {
values: [
{
value:
"ENOENT: no such file or directory, open 'path/to/public/_nuxt/public/long/long/path/file.js.map'",
},
],
},
},
],
])('filters out %s', (_, event) => {
// @ts-expect-error Event type is not correct in tests
expect(filter(event)).toBeNull();
});
});

describe('does not filter out errors', () => {
it.each([
['other errors', { exception: { values: [{ value: 'Some other error' }] } }],
['events with no exceptions', {}],
[
'events without _nuxt in path',
{
exception: { values: [{ value: "ENOENT: no such file or directory, open '/path/to/other/file.js.map'" }] },
},
],
[
'source map errors with different casing',
{
exception: { values: [{ value: "ENOENT: No Such file or directory, open '/path/to/_nuxt/file.js.map'" }] },
},
],
[
'non-source-map file',
{ exception: { values: [{ value: "ENOENT: no such file or directory, open '/path/to/_nuxt/file.js'" }] } },
],
['events with no exception values', { exception: { values: [] } }],
['events with null exception value', { exception: { values: [null] } }],
])('does not filter out %s', (_, event) => {
// @ts-expect-error Event type is not correct in tests
expect(filter(event)).toEqual(event);
});
});
});

describe('mergeRegisterEsmLoaderHooks', () => {
Expand Down

0 comments on commit 05479b8

Please sign in to comment.