Skip to content

Commit 201608b

Browse files
committed
Add backtrace_paths_matched to environment
Count how many lines in a given backtrace were modified by the `matchBacktracePaths` configuration option, and report that count as the `backtrace_paths_matched` entry in the environment sample data. This is useful for customer support purposes, allowing us to easily tell whether some lines have been modified by backtrace path matchers.
1 parent 3651dcf commit 201608b

File tree

3 files changed

+122
-2
lines changed

3 files changed

+122
-2
lines changed

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

+2
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ describe("Appsignal", () => {
101101
"Error: test error",
102102
" at Foo (/istheapp.js:13:10)"
103103
])
104+
105+
expect(payload.environment.backtrace_paths_matched).toEqual("1")
104106
})
105107
})
106108

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

+111-2
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,46 @@ describe("Span", () => {
1616
})
1717

1818
it("returns the span when no error is given", () => {
19-
span.setError((undefined as unknown) as Error)
19+
const result = span.setError((undefined as unknown) as Error)
2020
expect(span.serialize().error.message).toEqual("No error has been set")
21+
expect(result).toBe(span)
2122
})
2223

2324
it("returns the span when the passed object is not an error", () => {
2425
const event = new CloseEvent("event")
25-
span.setError((event as unknown) as Error)
26+
const result = span.setError((event as unknown) as Error)
2627
expect(span.serialize().error.message).toEqual("No error has been set")
28+
expect(result).toBe(span)
29+
})
30+
})
31+
32+
describe("setEnvironment", () => {
33+
it("updates the spans environment", () => {
34+
span.setEnvironment({ key: "value" })
35+
expect(span.serialize().environment).toEqual({ key: "value" })
36+
})
37+
38+
it("merges the environment", () => {
39+
span.setEnvironment({ key: "value" })
40+
span.setEnvironment({ key2: "value2" })
41+
expect(span.serialize().environment).toEqual({
42+
key: "value",
43+
key2: "value2"
44+
})
45+
})
46+
47+
it("returns the span when no environment is given", () => {
48+
const result = span.setEnvironment(
49+
(undefined as unknown) as { key: string }
50+
)
51+
expect(span.serialize().environment).toEqual({})
52+
expect(result).toBe(span)
53+
})
54+
55+
it("returns the span when the passed object is not an object", () => {
56+
const result = span.setEnvironment((123 as unknown) as { key: string })
57+
expect(span.serialize().environment).toEqual({})
58+
expect(result).toBe(span)
2759
})
2860
})
2961

@@ -49,6 +81,10 @@ describe("Span", () => {
4981
" at track (http://thirdparty.app/script.js:1:530)",
5082
" at app/bundle.js:21:10"
5183
])
84+
85+
expect(span.serialize().environment).toMatchObject({
86+
backtrace_paths_matched: "3"
87+
})
5288
})
5389

5490
it("cleans Safari/FF-style backtraces", () => {
@@ -70,6 +106,10 @@ describe("Span", () => {
70106
"track@http://thirdparty.app/script.js:1:530",
71107
"@app/bundle.js:21:10"
72108
])
109+
110+
expect(span.serialize().environment).toMatchObject({
111+
backtrace_paths_matched: "3"
112+
})
73113
})
74114

75115
it("concatenates all match groups", () => {
@@ -95,6 +135,10 @@ describe("Span", () => {
95135
" at track (http://thirdparty.app/script.js:1:530)",
96136
" at assets/app/bundle.js:21:10"
97137
])
138+
139+
expect(span.serialize().environment).toMatchObject({
140+
backtrace_paths_matched: "3"
141+
})
98142
})
99143

100144
it("tries matchers in order", () => {
@@ -119,6 +163,71 @@ describe("Span", () => {
119163
"Foo@app/bundle.js:13:10",
120164
"Bar@app/bundle.js:17:10"
121165
])
166+
167+
expect(span.serialize().environment).toMatchObject({
168+
backtrace_paths_matched: "2"
169+
})
170+
})
171+
172+
it("does not match any paths when the matcher has no match groups", () => {
173+
const error = new Error("test error")
174+
error.stack = [
175+
"Foo@http://localhost:8080/assets/app/bundle.js:13:10"
176+
].join("\n")
177+
178+
span.setError(error)
179+
// This regex always matches the line, but contains no match groups,
180+
// meaning the replacement that the matcher would do would be to an
181+
// empty string.
182+
//
183+
// This should result in the line not being modified.
184+
span.cleanBacktracePath(new RegExp(".*"))
185+
186+
const backtrace = span.serialize().error.backtrace
187+
expect(backtrace).toEqual([
188+
"Foo@http://localhost:8080/assets/app/bundle.js:13:10"
189+
])
190+
191+
expect(span.serialize().environment).toBeUndefined()
192+
})
193+
194+
it("does not replace the path when the match is empty", () => {
195+
const error = new Error("test error")
196+
error.stack = [
197+
"Foo@http://localhost:8080/assets/app/bundle.js:13:10"
198+
].join("\n")
199+
200+
span.setError(error)
201+
// This regex always matches the line with an empty match group,
202+
// meaning the replacement that the matcher would do would be to an
203+
// empty string.
204+
//
205+
// This should result in the line not being modified.
206+
span.cleanBacktracePath(new RegExp(".*(z*)$"))
207+
208+
const backtrace = span.serialize().error.backtrace
209+
expect(backtrace).toEqual([
210+
"Foo@http://localhost:8080/assets/app/bundle.js:13:10"
211+
])
212+
213+
expect(span.serialize().environment).toBeUndefined()
214+
})
215+
216+
it("does not report `backtrace_paths_matched` when no paths are matched", () => {
217+
const error = new Error("test error")
218+
error.stack = [
219+
"Foo@http://localhost:8080/assets/app/bundle.js:13:10"
220+
].join("\n")
221+
222+
span.setError(error)
223+
span.cleanBacktracePath(new RegExp("^pancakes/(.*)$"))
224+
225+
const backtrace = span.serialize().error.backtrace
226+
expect(backtrace).toEqual([
227+
"Foo@http://localhost:8080/assets/app/bundle.js:13:10"
228+
])
229+
230+
expect(span.serialize().environment).toBeUndefined()
122231
})
123232
})
124233
})

packages/javascript/src/span.ts

+9
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ export class Span extends Serializable<JSSpanData> {
9191
return this
9292
}
9393

94+
let linesMatched = 0
95+
9496
this._data.error.backtrace = this._data.error.backtrace.map(line => {
9597
const path = extractPath(line)
9698
if (!path) {
@@ -109,13 +111,20 @@ export class Span extends Serializable<JSSpanData> {
109111

110112
const relevantPath = match.slice(1).join("")
111113
if (relevantPath) {
114+
linesMatched++
112115
return line.replace(path, relevantPath)
113116
}
114117
}
115118

116119
return line
117120
})
118121

122+
if (linesMatched > 0) {
123+
this.setEnvironment({
124+
backtrace_paths_matched: linesMatched.toString()
125+
})
126+
}
127+
119128
return this
120129
}
121130
}

0 commit comments

Comments
 (0)