Skip to content

Commit a5ea680

Browse files
author
Luca Forstner
authored
fix: Apply stack frame metadata before event processors (#12799)
1 parent 580e6a4 commit a5ea680

File tree

14 files changed

+232
-36
lines changed

14 files changed

+232
-36
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
Sentry.init({
4+
dsn: 'https://[email protected]/1337',
5+
integrations: [Sentry.moduleMetadataIntegration()],
6+
beforeSend(event) {
7+
const moduleMetadataEntries = [];
8+
9+
if (event.type === undefined) {
10+
try {
11+
event.exception.values.forEach(value => {
12+
value.stacktrace.frames.forEach(frame => {
13+
moduleMetadataEntries.push(frame.module_metadata);
14+
});
15+
});
16+
} catch (e) {
17+
// noop
18+
}
19+
}
20+
21+
event.extra = {
22+
...event.extra,
23+
module_metadata_entries: moduleMetadataEntries,
24+
};
25+
26+
return event;
27+
},
28+
});
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
var _sentryModuleMetadataGlobal =
2+
typeof window !== 'undefined'
3+
? window
4+
: typeof global !== 'undefined'
5+
? global
6+
: typeof self !== 'undefined'
7+
? self
8+
: {};
9+
10+
_sentryModuleMetadataGlobal._sentryModuleMetadata = _sentryModuleMetadataGlobal._sentryModuleMetadata || {};
11+
12+
_sentryModuleMetadataGlobal._sentryModuleMetadata[new Error().stack] = Object.assign(
13+
{},
14+
_sentryModuleMetadataGlobal._sentryModuleMetadata[new Error().stack],
15+
{
16+
foo: 'bar',
17+
},
18+
);
19+
20+
setTimeout(() => {
21+
throw new Error('I am a module metadata Error');
22+
}, 0);
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { expect } from '@playwright/test';
2+
import type { Event } from '@sentry/types';
3+
4+
import { sentryTest } from '../../../../utils/fixtures';
5+
import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers';
6+
7+
sentryTest('should provide module_metadata on stack frames in beforeSend', async ({ getLocalTestPath, page }) => {
8+
// moduleMetadataIntegration is not included in any CDN bundles
9+
if (process.env.PW_BUNDLE?.startsWith('bundle')) {
10+
sentryTest.skip();
11+
}
12+
13+
const url = await getLocalTestPath({ testDir: __dirname });
14+
15+
const errorEvent = await getFirstSentryEnvelopeRequest<Event>(page, url);
16+
expect(errorEvent.extra?.['module_metadata_entries']).toEqual([{ foo: 'bar' }]);
17+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import * as Sentry from '@sentry/browser';
2+
3+
Sentry.init({
4+
dsn: 'https://[email protected]/1337',
5+
integrations: [
6+
Sentry.moduleMetadataIntegration(),
7+
Sentry.rewriteFramesIntegration({
8+
iteratee: frame => {
9+
return {
10+
...frame,
11+
filename: 'bloop', // something that should completely mess with module metadata association
12+
};
13+
},
14+
}),
15+
],
16+
beforeSend(event) {
17+
const moduleMetadataEntries = [];
18+
19+
if (event.type === undefined) {
20+
try {
21+
event.exception.values.forEach(value => {
22+
value.stacktrace.frames.forEach(frame => {
23+
moduleMetadataEntries.push(frame.module_metadata);
24+
});
25+
});
26+
} catch (e) {
27+
// noop
28+
}
29+
}
30+
31+
event.extra = {
32+
...event.extra,
33+
module_metadata_entries: moduleMetadataEntries,
34+
};
35+
36+
return event;
37+
},
38+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
var _sentryModuleMetadataGlobal =
2+
typeof window !== 'undefined'
3+
? window
4+
: typeof global !== 'undefined'
5+
? global
6+
: typeof self !== 'undefined'
7+
? self
8+
: {};
9+
10+
_sentryModuleMetadataGlobal._sentryModuleMetadata = _sentryModuleMetadataGlobal._sentryModuleMetadata || {};
11+
12+
_sentryModuleMetadataGlobal._sentryModuleMetadata[new Error().stack] = Object.assign(
13+
{},
14+
_sentryModuleMetadataGlobal._sentryModuleMetadata[new Error().stack],
15+
{
16+
foo: 'baz',
17+
},
18+
);
19+
20+
setTimeout(() => {
21+
throw new Error('I am a module metadata Error');
22+
}, 0);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { expect } from '@playwright/test';
2+
import type { Event } from '@sentry/types';
3+
4+
import { sentryTest } from '../../../../utils/fixtures';
5+
import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers';
6+
7+
sentryTest(
8+
'should provide module_metadata on stack frames in beforeSend even though an event processor (rewriteFramesIntegration) modified the filename',
9+
async ({ getLocalTestPath, page }) => {
10+
// moduleMetadataIntegration is not included in any CDN bundles
11+
if (process.env.PW_BUNDLE?.startsWith('bundle')) {
12+
sentryTest.skip();
13+
}
14+
15+
const url = await getLocalTestPath({ testDir: __dirname });
16+
17+
const errorEvent = await getFirstSentryEnvelopeRequest<Event>(page, url);
18+
expect(errorEvent?.extra?.['module_metadata_entries']).toEqual([{ foo: 'baz' }]);
19+
},
20+
);

packages/core/src/baseclient.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,8 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
455455

456456
public on(hook: 'close', callback: () => void): () => void;
457457

458+
public on(hook: 'applyFrameMetadata', callback: (event: Event) => void): () => void;
459+
458460
/** @inheritdoc */
459461
public on(hook: string, callback: unknown): () => void {
460462
// Note that the code below, with nullish coalescing assignment,
@@ -541,6 +543,9 @@ export abstract class BaseClient<O extends ClientOptions> implements Client<O> {
541543
/** @inheritdoc */
542544
public emit(hook: 'close'): void;
543545

546+
/** @inheritdoc */
547+
public emit(hook: 'applyFrameMetadata', event: Event): void;
548+
544549
/** @inheritdoc */
545550
public emit(hook: string, ...rest: unknown[]): void {
546551
const callbacks = this._hooks[hook];
Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
1-
import type { EventItem, IntegrationFn } from '@sentry/types';
1+
import type { EventItem } from '@sentry/types';
22
import { forEachEnvelopeItem } from '@sentry/utils';
33
import { defineIntegration } from '../integration';
44

55
import { addMetadataToStackFrames, stripMetadataFromStackFrames } from '../metadata';
66

7-
const INTEGRATION_NAME = 'ModuleMetadata';
8-
9-
const _moduleMetadataIntegration = (() => {
7+
/**
8+
* Adds module metadata to stack frames.
9+
*
10+
* Metadata can be injected by the Sentry bundler plugins using the `moduleMetadata` config option.
11+
*
12+
* When this integration is added, the metadata passed to the bundler plugin is added to the stack frames of all events
13+
* under the `module_metadata` property. This can be used to help in tagging or routing of events from different teams
14+
* our sources
15+
*/
16+
export const moduleMetadataIntegration = defineIntegration(() => {
1017
return {
11-
name: INTEGRATION_NAME,
18+
name: 'ModuleMetadata',
1219
setup(client) {
1320
// We need to strip metadata from stack frames before sending them to Sentry since these are client side only.
1421
client.on('beforeEnvelope', envelope => {
@@ -23,23 +30,16 @@ const _moduleMetadataIntegration = (() => {
2330
}
2431
});
2532
});
26-
},
2733

28-
processEvent(event, _hint, client) {
29-
const stackParser = client.getOptions().stackParser;
30-
addMetadataToStackFrames(stackParser, event);
31-
return event;
34+
client.on('applyFrameMetadata', event => {
35+
// Only apply stack frame metadata to error events
36+
if (event.type !== undefined) {
37+
return;
38+
}
39+
40+
const stackParser = client.getOptions().stackParser;
41+
addMetadataToStackFrames(stackParser, event);
42+
});
3243
},
3344
};
34-
}) satisfies IntegrationFn;
35-
36-
/**
37-
* Adds module metadata to stack frames.
38-
*
39-
* Metadata can be injected by the Sentry bundler plugins using the `_experiments.moduleMetadata` config option.
40-
*
41-
* When this integration is added, the metadata passed to the bundler plugin is added to the stack frames of all events
42-
* under the `module_metadata` property. This can be used to help in tagging or routing of events from different teams
43-
* our sources
44-
*/
45-
export const moduleMetadataIntegration = defineIntegration(_moduleMetadataIntegration);
45+
});

packages/core/src/integrations/third-party-errors-filter.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,19 @@ export const thirdPartyErrorFilterIntegration = defineIntegration((options: Opti
5353
}
5454
});
5555
});
56+
57+
client.on('applyFrameMetadata', event => {
58+
// Only apply stack frame metadata to error events
59+
if (event.type !== undefined) {
60+
return;
61+
}
62+
63+
const stackParser = client.getOptions().stackParser;
64+
addMetadataToStackFrames(stackParser, event);
65+
});
5666
},
57-
processEvent(event, _hint, client) {
58-
const stackParser = client.getOptions().stackParser;
59-
addMetadataToStackFrames(stackParser, event);
6067

68+
processEvent(event) {
6169
const frameKeys = getBundleKeysForAllFramesWithFilenames(event);
6270

6371
if (frameKeys) {

packages/core/src/utils/prepareEvent.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ export function prepareEvent(
6060
applyClientOptions(prepared, options);
6161
applyIntegrationsMetadata(prepared, integrations);
6262

63+
if (client) {
64+
client.emit('applyFrameMetadata', event);
65+
}
66+
6367
// Only put debug IDs onto frames for error events.
6468
if (event.type === undefined) {
6569
applyDebugIds(prepared, options.stackParser);

0 commit comments

Comments
 (0)