Skip to content

Commit 17db8e2

Browse files
authored
fix(node): LocalVariables, Improve frame matching for ESM (#7049)
1 parent 9a6960b commit 17db8e2

File tree

3 files changed

+71
-1
lines changed

3 files changed

+71
-1
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/* eslint-disable no-unused-vars */
2+
import * as Sentry from '@sentry/node';
3+
4+
Sentry.init({
5+
dsn: 'https://[email protected]/1337',
6+
includeLocalVariables: true,
7+
integrations: [new Sentry.Integrations.LocalVariables({ captureAllExceptions: true })],
8+
beforeSend: event => {
9+
// eslint-disable-next-line no-console
10+
console.log(JSON.stringify(event));
11+
},
12+
});
13+
14+
class Some {
15+
two(name) {
16+
throw new Error('Enough!');
17+
}
18+
}
19+
20+
function one(name) {
21+
const arr = [1, '2', null];
22+
const obj = {
23+
name,
24+
num: 5,
25+
};
26+
27+
const ty = new Some();
28+
29+
ty.two(name);
30+
}
31+
32+
try {
33+
one('some name');
34+
} catch (e) {
35+
Sentry.captureException(e);
36+
}

packages/node-integration-tests/suites/public-api/LocalVariables/test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import type { Event } from '@sentry/node';
2+
import { parseSemver } from '@sentry/utils';
23
import * as childProcess from 'child_process';
34
import * as path from 'path';
45

6+
const nodeMajor = parseSemver(process.version.slice(1)).major || 1;
7+
8+
const testIf = (condition: boolean, t: jest.It) => (condition ? t : t.skip);
9+
510
describe('LocalVariables integration', () => {
611
test('Should not include local variables by default', done => {
712
expect.assertions(2);
@@ -52,6 +57,34 @@ describe('LocalVariables integration', () => {
5257
});
5358
});
5459

60+
testIf(nodeMajor > 10, test)('Should include local variables with ESM', done => {
61+
expect.assertions(4);
62+
63+
const testScriptPath = path.resolve(__dirname, 'local-variables-caught.mjs');
64+
65+
childProcess.exec(`node ${testScriptPath}`, { encoding: 'utf8' }, (_, stdout) => {
66+
const event = JSON.parse(stdout) as Event;
67+
68+
const frames = event.exception?.values?.[0].stacktrace?.frames || [];
69+
const lastFrame = frames[frames.length - 1];
70+
71+
expect(lastFrame.function).toBe('Some.two');
72+
expect(lastFrame.vars).toEqual({ name: 'some name' });
73+
74+
const penultimateFrame = frames[frames.length - 2];
75+
76+
expect(penultimateFrame.function).toBe('one');
77+
expect(penultimateFrame.vars).toEqual({
78+
name: 'some name',
79+
arr: [1, '2', null],
80+
obj: { name: 'some name', num: 5 },
81+
ty: '<Some>',
82+
});
83+
84+
done();
85+
});
86+
});
87+
5588
test('Includes local variables for caught exceptions when enabled', done => {
5689
expect.assertions(4);
5790

packages/node/src/integrations/localvariables.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,8 @@ export class LocalVariables implements Integration {
237237
const framePromises = callFrames.map(async ({ scopeChain, functionName, this: obj }) => {
238238
const localScope = scopeChain.find(scope => scope.type === 'local');
239239

240-
const fn = obj.className === 'global' ? functionName : `${obj.className}.${functionName}`;
240+
// obj.className is undefined in ESM modules
241+
const fn = obj.className === 'global' || !obj.className ? functionName : `${obj.className}.${functionName}`;
241242

242243
if (localScope?.object.objectId === undefined) {
243244
return { function: fn };

0 commit comments

Comments
 (0)