Skip to content

Commit 7d1efe7

Browse files
committed
Use existing stack trace logic
1 parent 3f4bdd3 commit 7d1efe7

2 files changed

Lines changed: 37 additions & 60 deletions

File tree

packages/debugger/src/domain/stacktrace.spec.ts

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ describe('stacktrace', () => {
55
it('should parse Chrome/V8 stack trace format with function names', () => {
66
const error = {
77
stack: `Error: test error
8-
at captureStackTrace (http://example.com/stacktrace.js:1:1)
98
at myFunction (http://example.com/app.js:42:10)
109
at anotherFunction (http://example.com/app.js:100:5)`,
1110
} as Error
@@ -31,7 +30,6 @@ describe('stacktrace', () => {
3130
it('should parse Chrome/V8 stack trace format without function names', () => {
3231
const error = {
3332
stack: `Error: test error
34-
at captureStackTrace (http://example.com/stacktrace.js:1:1)
3533
at http://example.com/app.js:42:10
3634
at http://example.com/app.js:100:5`,
3735
} as Error
@@ -57,7 +55,6 @@ describe('stacktrace', () => {
5755
it('should parse Firefox stack trace format', () => {
5856
const error = {
5957
stack: `test error
60-
captureStackTrace@http://example.com/stacktrace.js:1:1
6158
myFunction@http://example.com/app.js:42:10
6259
anotherFunction@http://example.com/app.js:100:5`,
6360
} as Error
@@ -83,7 +80,6 @@ anotherFunction@http://example.com/app.js:100:5`,
8380
it('should skip frames when skipFrames is specified', () => {
8481
const error = {
8582
stack: `Error: test error
86-
at captureStackTrace (http://example.com/stacktrace.js:1:1)
8783
at frameToSkip (http://example.com/app.js:10:5)
8884
at myFunction (http://example.com/app.js:42:10)
8985
at anotherFunction (http://example.com/app.js:100:5)`,
@@ -126,7 +122,6 @@ anotherFunction@http://example.com/app.js:100:5`,
126122
it('should skip malformed stack lines', () => {
127123
const error = {
128124
stack: `Error: test error
129-
at captureStackTrace (http://example.com/stacktrace.js:1:1)
130125
at myFunction (http://example.com/app.js:42:10)
131126
some malformed line without proper format
132127
at anotherFunction (http://example.com/app.js:100:5)`,
@@ -153,7 +148,6 @@ anotherFunction@http://example.com/app.js:100:5`,
153148
it('should handle file paths with spaces', () => {
154149
const error = {
155150
stack: `Error: test error
156-
at captureStackTrace (http://example.com/stacktrace.js:1:1)
157151
at myFunction (http://example.com/my app.js:42:10)`,
158152
} as Error
159153

@@ -168,25 +162,6 @@ anotherFunction@http://example.com/app.js:100:5`,
168162
},
169163
])
170164
})
171-
172-
it('should trim whitespace from function and file names', () => {
173-
const error = {
174-
stack: `Error: test error
175-
at captureStackTrace (http://example.com/stacktrace.js:1:1)
176-
at myFunction ( http://example.com/app.js :42:10)`,
177-
} as Error
178-
179-
const result = parseStackTrace(error)
180-
181-
expect(result).toEqual([
182-
{
183-
fileName: 'http://example.com/app.js',
184-
function: 'myFunction',
185-
lineNumber: 42,
186-
columnNumber: 10,
187-
},
188-
])
189-
})
190165
})
191166

192167
describe('captureStackTrace', () => {
@@ -205,16 +180,16 @@ anotherFunction@http://example.com/app.js:100:5`,
205180
})
206181

207182
it('should skip frames when specified', () => {
208-
function testFunction() {
209-
return captureStackTrace(0)
183+
function testFunction(skipFrames = 0) {
184+
return captureStackTrace(skipFrames)
210185
}
211186

212187
function wrapperFunction() {
213188
return testFunction()
214189
}
215190

216191
const resultWithoutSkip = wrapperFunction()
217-
const resultWithSkip = captureStackTrace(1)
192+
const resultWithSkip = testFunction(1)
218193

219194
// When skipping frames, we should have fewer frames
220195
expect(resultWithSkip.length).toBeLessThan(resultWithoutSkip.length)
Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import type { StackTrace } from '@datadog/browser-core'
2+
import { computeStackTrace } from '@datadog/browser-core'
3+
14
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions -- `type` is needed for implicit index signature compatibility with Context
25
export type StackFrame = {
36
fileName: string
@@ -14,48 +17,47 @@ export type StackFrame = {
1417
*/
1518
export function captureStackTrace(skipFrames = 0): StackFrame[] {
1619
const error = new Error()
17-
return parseStackTrace(error, skipFrames)
20+
const stackTrace = computeStackTrace(error)
21+
22+
// Skip this helper itself so callers get their own frame first.
23+
return mapStackFrames(stackTrace.stack, 1 + skipFrames)
1824
}
1925

2026
/**
2127
* Parse a stack trace from an Error object
2228
*
2329
* @param error - Error object with stack property
24-
* @param skipFrames - Number of frames to skip from the top of the stack (default: 0)
30+
* @param skipFrames - Number of frames to skip from the top of the parsed stack (default: 0)
2531
* @returns Array of stack frames
2632
*/
2733
export function parseStackTrace(error: Error, skipFrames = 0): StackFrame[] {
28-
const stack: StackFrame[] = []
29-
if (!error.stack) {
30-
return stack
34+
return mapStackFrames(computeStackTrace(error).stack, skipFrames)
35+
}
36+
37+
function mapStackFrame(frame: StackTrace['stack'][number]): StackFrame | undefined {
38+
if (!frame.url || frame.line === undefined || frame.column === undefined) {
39+
return
3140
}
32-
const stackLines = error.stack.split('\n')
33-
34-
// Skip the first line (error message), the captureStackTrace frame, and any additional frames to skip
35-
for (let i = 2 + skipFrames; i < stackLines.length; i++) {
36-
const line = stackLines[i].trim()
37-
38-
// Match various stack frame formats:
39-
// Chrome/V8: "at functionName (file:line:column)" or "at file:line:column"
40-
// Firefox: "functionName@file:line:column"
41-
const chromeMatch = line.match(/at\s+(?:(.+?)\s+\()?(.+?):(\d+):(\d+)\)?/)
42-
const firefoxMatch = line.match(/(.+?)@(.+?):(\d+):(\d+)/)
43-
44-
const match = chromeMatch || firefoxMatch
45-
if (match) {
46-
const functionName = match[1] || ''
47-
const fileName = match[2]
48-
const lineNumber = parseInt(match[3], 10)
49-
const columnNumber = parseInt(match[4], 10)
50-
51-
stack.push({
52-
fileName: fileName.trim(),
53-
function: functionName.trim(),
54-
lineNumber,
55-
columnNumber,
56-
})
57-
}
41+
42+
return {
43+
fileName: frame.url.trim(),
44+
function: frame.func === '?' || !frame.func ? '' : frame.func.trim(),
45+
lineNumber: frame.line,
46+
columnNumber: frame.column,
5847
}
48+
}
49+
50+
function mapStackFrames(stack: StackTrace['stack'], skipFrames = 0): StackFrame[] {
51+
return stack.reduce<StackFrame[]>((result, frame, index) => {
52+
if (index < skipFrames) {
53+
return result
54+
}
55+
56+
const mappedFrame = mapStackFrame(frame)
57+
if (mappedFrame) {
58+
result.push(mappedFrame)
59+
}
5960

60-
return stack
61+
return result
62+
}, [])
6163
}

0 commit comments

Comments
 (0)