Skip to content

Commit 5248f19

Browse files
authored
Merge pull request #661 from appsignal/fix-match-backtrace-path-spaces
Match on the entire backtrace path line
2 parents e84cfe9 + 51783cb commit 5248f19

File tree

3 files changed

+79
-9
lines changed

3 files changed

+79
-9
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
bump: patch
3+
type: fix
4+
---
5+
6+
Fix matching on backtrace paths containing spaces.
7+
8+
When using `matchBacktracePaths`, when a backtrace line path contains a space, it will now match correctly against the whole path.

packages/javascript/src/__tests__/span.test.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,35 @@ describe("Span", () => {
8989
})
9090
})
9191

92+
it("cleans Chrome-style backtraces with spaces in the path", () => {
93+
const error = new Error("test error")
94+
error.stack = [
95+
"Error: test error",
96+
" at Foo (http://localhost:8080/assets with space/app/bundle.js:13:10)",
97+
" at Bar (http://localhost:8080/assets with space/app/bundle.js:17:10)",
98+
" at track (http://thirdparty.app/script.js:1:530)",
99+
" at http://localhost:8080/assets with space/app/bundle.js:21:10"
100+
].join("\n")
101+
102+
span.setError(error)
103+
span.cleanBacktracePath(
104+
[new RegExp("/assets with space/(app/.*)$")].map(toBacktraceMatcher)
105+
)
106+
107+
const backtrace = span.serialize().error.backtrace
108+
expect(backtrace).toEqual([
109+
"Error: test error",
110+
" at Foo (app/bundle.js:13:10)",
111+
" at Bar (app/bundle.js:17:10)",
112+
" at track (http://thirdparty.app/script.js:1:530)",
113+
" at app/bundle.js:21:10"
114+
])
115+
116+
expect(span.serialize().environment).toMatchObject({
117+
backtrace_paths_matched: "3"
118+
})
119+
})
120+
92121
it("cleans Safari/FF-style backtraces", () => {
93122
const error = new Error("test error")
94123
error.stack = [
@@ -116,6 +145,33 @@ describe("Span", () => {
116145
})
117146
})
118147

148+
it("cleans Safari/FF-style backtraces with spaces in the path", () => {
149+
const error = new Error("test error")
150+
error.stack = [
151+
"Foo@http://localhost:8080/assets with space/app/bundle.js:13:10",
152+
"Bar@http://localhost:8080/assets with space/app/bundle.js:17:10",
153+
"track@http://thirdparty.app/script.js:1:530",
154+
"@http://localhost:8080/assets with space/app/bundle.js:21:10"
155+
].join("\n")
156+
157+
span.setError(error)
158+
span.cleanBacktracePath(
159+
[new RegExp("/assets with space/(app/.*)$")].map(toBacktraceMatcher)
160+
)
161+
162+
const backtrace = span.serialize().error.backtrace
163+
expect(backtrace).toEqual([
164+
"Foo@app/bundle.js:13:10",
165+
"Bar@app/bundle.js:17:10",
166+
"track@http://thirdparty.app/script.js:1:530",
167+
"@app/bundle.js:21:10"
168+
])
169+
170+
expect(span.serialize().environment).toMatchObject({
171+
backtrace_paths_matched: "3"
172+
})
173+
})
174+
119175
it("concatenates all match groups", () => {
120176
const error = new Error("test error")
121177
error.stack = [

packages/javascript/src/span.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -186,15 +186,22 @@ function extractPath(backtraceLine: string): string | undefined {
186186
// A Chrome backtrace line always contains `at` near the beginning,
187187
// preceded by space characters and followed by one space.
188188
const IS_CHROME = /^\s*at\s/
189+
189190
// In a Chrome backtrace line, the path (if it is available)
190-
// is located, usually within parentheses, after the "@" towards the
191-
// end of the line, along with the line number and column number,
192-
// separated by colons. We check for those to reject clear non-paths.
193-
const CHROME_PATH = /at(?:\s.*)?\s\(?(.*):\d*:\d*\)?$/i
191+
// is located after the "at" towards the end of the line, along with
192+
// the line number and column number, separated by colons.
193+
// When the function name is available, the path is enclosed in
194+
// parentheses. When there is no function name, the path comes
195+
// immediately after the "at".
196+
// We check for the line and column numbers to reject clear non-paths.
197+
const CHROME_PATH_WITH_FUNCTION_NAME = /^\s*at(?:\s[^\(]+)?\s\((.*):\d+:\d+\)$/
198+
const CHROME_PATH_WITHOUT_FUNCTION_NAME = /^\s*at\s(.*):\d+:\d+$/
194199

195200
if (backtraceLine.match(IS_CHROME)) {
196-
const match = backtraceLine.match(CHROME_PATH)
197-
return match ? match[1] : undefined
201+
return (
202+
backtraceLine.match(CHROME_PATH_WITH_FUNCTION_NAME)?.[1] ??
203+
backtraceLine.match(CHROME_PATH_WITHOUT_FUNCTION_NAME)?.[1]
204+
)
198205
}
199206

200207
// A Safari or Firefox backtrace line always contains `@` after the first
@@ -205,10 +212,9 @@ function extractPath(backtraceLine: string): string | undefined {
205212
// is located after the "@" at the towards end of the line, followed by
206213
// the line number and column number, separated by colons. We check for
207214
// those to reject clear non-paths.
208-
const SAFARI_FF_PATH = /@\s?(.*):\d*:\d*$/i
215+
const SAFARI_FF_PATH = /^.*@\s?(.*):\d+:\d+$/
209216

210217
if (backtraceLine.match(IS_SAFARI_FF)) {
211-
const match = backtraceLine.match(SAFARI_FF_PATH)
212-
return match ? match[1] : undefined
218+
return backtraceLine.match(SAFARI_FF_PATH)?.[1]
213219
}
214220
}

0 commit comments

Comments
 (0)