Skip to content

Commit 4081941

Browse files
authored
fix(rum-core): capture stack trace from syntax errors properly (#1239)
1 parent c83c586 commit 4081941

File tree

3 files changed

+70
-6
lines changed

3 files changed

+70
-6
lines changed

packages/rum-core/src/error-logging/error-logging.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { createStackTraces, filterInvalidFrames } from './stack-trace'
2727
import { generateRandomId, merge, extend } from '../common/utils'
2828
import { getPageContext } from '../common/context'
2929
import { truncateModel, ERROR_MODEL } from '../common/truncate'
30+
import stackParser from 'error-stack-parser'
3031

3132
/**
3233
* List of keys to be ignored from getting added to custom error properties
@@ -76,7 +77,7 @@ class ErrorLogging {
7677
* errorEvent = { message, filename, lineno, colno, error }
7778
*/
7879
createErrorDataModel(errorEvent) {
79-
const frames = createStackTraces(errorEvent)
80+
const frames = createStackTraces(stackParser, errorEvent)
8081
const filteredFrames = filterInvalidFrames(frames)
8182

8283
// If filename empty, assume inline script

packages/rum-core/src/error-logging/stack-trace.js

+20-4
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@
2323
*
2424
*/
2525

26-
import stackParser from 'error-stack-parser'
27-
2826
function filePathToFileName(fileUrl) {
2927
const origin =
3028
window.location.origin ||
@@ -91,7 +89,21 @@ function normalizeFunctionName(fnName) {
9189
return fnName
9290
}
9391

94-
export function createStackTraces(errorEvent) {
92+
function isValidStackTrace(stackTraces) {
93+
if (stackTraces.length === 0) {
94+
return false
95+
}
96+
97+
if (stackTraces.length === 1) {
98+
const stackTrace = stackTraces[0]
99+
100+
return 'lineNumber' in stackTrace
101+
}
102+
103+
return true
104+
}
105+
106+
export function createStackTraces(stackParser, errorEvent) {
95107
const { error, filename, lineno, colno } = errorEvent
96108

97109
let stackTraces = []
@@ -106,7 +118,11 @@ export function createStackTraces(errorEvent) {
106118
}
107119
}
108120

109-
if (stackTraces.length === 0) {
121+
// error-stack-parser library doesn't generate proper stack traces from Syntax Errors
122+
// thrown during the browser document parsing process. The outcome of the library depends on the browser:
123+
// 1. on non-chromium browsers it throws an exception.
124+
// 2. on chromium browsers it returns a malformed single stack trace element not containing the lineNumber property
125+
if (!isValidStackTrace(stackTraces)) {
110126
stackTraces = [
111127
{
112128
fileName: filename,

packages/rum-core/test/error-logging/stack-trace.spec.js

+48-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
*/
2525

2626
import { createStackTraces } from '../../src/error-logging/stack-trace'
27+
import stackParser from 'error-stack-parser'
2728

2829
describe('StackTraceService', function () {
2930
it('should produce correct number of frames', function (done) {
@@ -34,10 +35,56 @@ describe('StackTraceService', function () {
3435
try {
3536
generateError()
3637
} catch (error) {
37-
var stackTraces = createStackTraces({ error })
38+
var stackTraces = createStackTraces(stackParser, { error })
3839
expect(stackTraces.length).toBeGreaterThan(1)
3940
done()
4041
}
4142
}, 1)
4243
})
44+
45+
describe('integration error-stack-parser library', () => {
46+
it('should generate stack trace from errorEvent when stackParser throws an error parsing it', () => {
47+
const stackParserFake = {
48+
parse: () => {
49+
throw new Error()
50+
}
51+
}
52+
53+
const testErrorEvent = new Error()
54+
testErrorEvent.error = 'error event'
55+
testErrorEvent.lineno = 1
56+
testErrorEvent.colno = 30
57+
testErrorEvent.filename = '(inline script)'
58+
59+
const [stackTrace] = createStackTraces(stackParserFake, testErrorEvent)
60+
61+
expect(stackTrace.lineno).toBe(testErrorEvent.lineno)
62+
expect(stackTrace.colno).toBe(testErrorEvent.colno)
63+
expect(stackTrace.filename).toBe(testErrorEvent.filename)
64+
})
65+
66+
it('should generate stack trace from errorEvent when the one returned from stackParser does not contain lineNumber', () => {
67+
const stackParserFake = {
68+
parse: () => {
69+
return [
70+
{
71+
filename: 'malformed parsing filename'
72+
}
73+
]
74+
}
75+
}
76+
77+
const testErrorEvent = new Error()
78+
testErrorEvent.error = 'error event'
79+
testErrorEvent.lineno = 4
80+
testErrorEvent.colno = 23
81+
testErrorEvent.filename = '(inline script)'
82+
83+
const [stackTrace] = createStackTraces(stackParserFake, testErrorEvent)
84+
85+
expect(stackTrace.lineno).toBe(testErrorEvent.lineno)
86+
expect(stackTrace.colno).toBe(testErrorEvent.colno)
87+
expect(stackTrace.filename).toBe(testErrorEvent.filename)
88+
})
89+
})
4390
})

0 commit comments

Comments
 (0)