Skip to content

Commit c52b1dd

Browse files
fix(v8/utils): Stack parser skip frames (not lines of stack string) (#10560)
1 parent 1cdab67 commit c52b1dd

File tree

5 files changed

+38
-31
lines changed

5 files changed

+38
-31
lines changed

MIGRATION.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,11 @@ const replay = Sentry.getIntegration(Replay);
309309
const replay = getClient().getIntegrationByName('Replay');
310310
```
311311

312+
#### `framesToPop` applies to parsed frames
313+
314+
Error with `framesToPop` property will have the specified number of frames removed from the top of the stack. This
315+
changes compared to the v7 where the property `framesToPop` was used to remove top n lines from the stack string.
316+
312317
#### `tracingOrigins` has been replaced by `tracePropagationTargets`
313318

314319
`tracingOrigins` is now removed in favor of the `tracePropagationTargets` option. The `tracePropagationTargets` option

packages/browser/src/eventbuilder.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,11 @@ export function parseStackFrames(
106106
// reliably in other circumstances.
107107
const stacktrace = ex.stacktrace || ex.stack || '';
108108

109-
const popSize = getPopSize(ex);
109+
const skipLines = getSkipFirstStackStringLines(ex);
110+
const framesToPop = getPopFirstTopFrames(ex);
110111

111112
try {
112-
return stackParser(stacktrace, popSize);
113+
return stackParser(stacktrace, skipLines, framesToPop);
113114
} catch (e) {
114115
// no-empty
115116
}
@@ -120,15 +121,30 @@ export function parseStackFrames(
120121
// Based on our own mapping pattern - https://github.com/getsentry/sentry/blob/9f08305e09866c8bd6d0c24f5b0aabdd7dd6c59c/src/sentry/lang/javascript/errormapping.py#L83-L108
121122
const reactMinifiedRegexp = /Minified React error #\d+;/i;
122123

123-
function getPopSize(ex: Error & { framesToPop?: number }): number {
124-
if (ex) {
125-
if (typeof ex.framesToPop === 'number') {
126-
return ex.framesToPop;
127-
}
124+
/**
125+
* Certain known React errors contain links that would be falsely
126+
* parsed as frames. This function check for these errors and
127+
* returns number of the stack string lines to skip.
128+
*/
129+
function getSkipFirstStackStringLines(ex: Error): number {
130+
if (ex && reactMinifiedRegexp.test(ex.message)) {
131+
return 1;
132+
}
128133

129-
if (reactMinifiedRegexp.test(ex.message)) {
130-
return 1;
131-
}
134+
return 0;
135+
}
136+
137+
/**
138+
* If error has `framesToPop` property, it means that the
139+
* creator tells us the first x frames will be useless
140+
* and should be discarded. Typically error from wrapper function
141+
* which don't point to the actual location in the developer's code.
142+
*
143+
* Example: https://github.com/zertosh/invariant/blob/master/invariant.js#L46
144+
*/
145+
function getPopFirstTopFrames(ex: Error & { framesToPop?: unknown }): number {
146+
if (typeof ex.framesToPop === 'number') {
147+
return ex.framesToPop;
132148
}
133149

134150
return 0;

packages/browser/test/unit/tracekit/react.test.ts

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { exceptionFromError } from '../../../src/eventbuilder';
22
import { defaultStackParser as parser } from '../../../src/stack-parsers';
33

44
describe('Tracekit - React Tests', () => {
5-
it('should correctly parse Invariant Violation errors and use framesToPop to drop info message', () => {
5+
it('should correctly parse Invariant Violation errors and use framesToPop to drop the invariant frame', () => {
66
const REACT_INVARIANT_VIOLATION_EXCEPTION = {
77
framesToPop: 1,
88
message:
@@ -38,13 +38,6 @@ describe('Tracekit - React Tests', () => {
3838
colno: 21841,
3939
in_app: true,
4040
},
41-
{
42-
filename: 'http://localhost:5000/static/js/foo.chunk.js',
43-
function: '?',
44-
lineno: 1,
45-
colno: 21738,
46-
in_app: true,
47-
},
4841
],
4942
},
5043
});
@@ -97,7 +90,7 @@ describe('Tracekit - React Tests', () => {
9790
});
9891
});
9992

100-
it('should not drop additional frame for production errors if framesToPop is still there', () => {
93+
it('should drop invariant frame for production errors if framesToPop is present', () => {
10194
const REACT_PRODUCTION_ERROR = {
10295
framesToPop: 1,
10396
message:
@@ -133,13 +126,6 @@ describe('Tracekit - React Tests', () => {
133126
colno: 21841,
134127
in_app: true,
135128
},
136-
{
137-
filename: 'http://localhost:5000/static/js/foo.chunk.js',
138-
function: '?',
139-
lineno: 1,
140-
colno: 21738,
141-
in_app: true,
142-
},
143129
],
144130
},
145131
});

packages/types/src/stacktrace.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ export interface Stacktrace {
66
frames_omitted?: [number, number];
77
}
88

9-
export type StackParser = (stack: string, skipFirst?: number) => StackFrame[];
9+
export type StackParser = (stack: string, skipFirstLines?: number, framesToPop?: number) => StackFrame[];
1010
export type StackLineParserFn = (line: string) => StackFrame | undefined;
1111
export type StackLineParser = [number, StackLineParserFn];

packages/utils/src/stacktrace.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ const STRIP_FRAME_REGEXP = /captureMessage|captureException/;
1616
export function createStackParser(...parsers: StackLineParser[]): StackParser {
1717
const sortedParsers = parsers.sort((a, b) => a[0] - b[0]).map(p => p[1]);
1818

19-
return (stack: string, skipFirst: number = 0): StackFrame[] => {
19+
return (stack: string, skipFirstLines: number = 0, framesToPop: number = 0): StackFrame[] => {
2020
const frames: StackFrame[] = [];
2121
const lines = stack.split('\n');
2222

23-
for (let i = skipFirst; i < lines.length; i++) {
23+
for (let i = skipFirstLines; i < lines.length; i++) {
2424
const line = lines[i];
2525
// Ignore lines over 1kb as they are unlikely to be stack frames.
2626
// Many of the regular expressions use backtracking which results in run time that increases exponentially with
@@ -49,12 +49,12 @@ export function createStackParser(...parsers: StackLineParser[]): StackParser {
4949
}
5050
}
5151

52-
if (frames.length >= STACKTRACE_FRAME_LIMIT) {
52+
if (frames.length >= STACKTRACE_FRAME_LIMIT + framesToPop) {
5353
break;
5454
}
5555
}
5656

57-
return stripSentryFramesAndReverse(frames);
57+
return stripSentryFramesAndReverse(frames.slice(framesToPop));
5858
};
5959
}
6060

0 commit comments

Comments
 (0)