Skip to content

Commit 22c8706

Browse files
committed
feat: Make node transactions a pluggable integration with tests
1 parent af7ece0 commit 22c8706

File tree

3 files changed

+186
-16
lines changed

3 files changed

+186
-16
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { Scope } from '@sentry/hub';
2+
import { Integration, SentryEvent, StackFrame } from '@sentry/types';
3+
import { NodeOptions } from '../../backend';
4+
import { getCurrentHub } from '../../hub';
5+
6+
/** Add node transaction to the event */
7+
export class Transaction implements Integration {
8+
/**
9+
* @inheritDoc
10+
*/
11+
public name: string = 'Transaction';
12+
13+
/**
14+
* @inheritDoc
15+
*/
16+
public install(_: NodeOptions = {}): void {
17+
getCurrentHub().configureScope((scope: Scope) => {
18+
scope.addEventProcessor(async event => this.process(event));
19+
});
20+
}
21+
22+
/**
23+
* @inheritDoc
24+
*/
25+
public async process(event: SentryEvent): Promise<SentryEvent> {
26+
const frames = this.getFramesFromEvent(event);
27+
28+
// use for loop so we don't have to reverse whole frames array
29+
for (let i = frames.length - 1; i >= 0; i--) {
30+
const frame = frames[i];
31+
32+
if (frame.in_app === true) {
33+
event.transaction = this.getTransaction(frame);
34+
break;
35+
}
36+
}
37+
38+
return event;
39+
}
40+
41+
/** JSDoc */
42+
private getFramesFromEvent(event: SentryEvent): StackFrame[] {
43+
const exception = event.exception && event.exception.values && event.exception.values[0];
44+
return (exception && exception.stacktrace && exception.stacktrace.frames) || [];
45+
}
46+
47+
/** JSDoc */
48+
private getTransaction(frame: StackFrame): string {
49+
return frame.module || frame.function ? `${frame.module || '?'}/${frame.function || '?'}` : '<unknown>';
50+
}
51+
}

packages/node/src/parsers.ts

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,6 @@ function getFunction(frame: stacktrace.StackFrame): string {
2525
}
2626
}
2727

28-
/** JSDoc */
29-
function getTransaction(frame: StackFrame): string {
30-
return frame.module || frame.function ? `${frame.module || '?'} at ${frame.function || '?'}` : '<unknown>';
31-
}
32-
3328
const mainModule: string = `${(require.main && require.main.filename && path.dirname(require.main.filename)) ||
3429
global.process.cwd()}/`;
3530

@@ -216,17 +211,6 @@ export async function parseError(error: ExtendedError): Promise<SentryEvent> {
216211
};
217212
}
218213

219-
// use for loop so we don't have to reverse whole frames array
220-
const frames = (exception.stacktrace && exception.stacktrace.frames) || [];
221-
for (let i = frames.length - 1; i >= 0; i--) {
222-
const frame = frames[i];
223-
224-
if (frame.in_app === true) {
225-
event.transaction = getTransaction(frame);
226-
break;
227-
}
228-
}
229-
230214
return event;
231215
}
232216

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import { Transaction } from '../../src/integrations/pluggable/transaction';
2+
3+
const transaction: Transaction = new Transaction();
4+
5+
describe.only('Transaction', () => {
6+
describe('extracts info from module/function of the first `in_app` frame', () => {
7+
it('using module only', async () => {
8+
const event = await transaction.process({
9+
exception: {
10+
values: [
11+
{
12+
stacktrace: {
13+
frames: [
14+
{
15+
filename: '/some/file1.js',
16+
in_app: false,
17+
module: 'Foo',
18+
},
19+
{
20+
filename: '/some/file2.js',
21+
in_app: true,
22+
module: 'Qux',
23+
},
24+
],
25+
},
26+
},
27+
],
28+
},
29+
});
30+
expect(event.transaction).toEqual('Qux/?');
31+
});
32+
33+
it('using function only', async () => {
34+
const event = await transaction.process({
35+
exception: {
36+
values: [
37+
{
38+
stacktrace: {
39+
frames: [
40+
{
41+
filename: '/some/file1.js',
42+
function: 'Bar',
43+
in_app: false,
44+
},
45+
{
46+
filename: '/some/file2.js',
47+
function: 'Baz',
48+
in_app: true,
49+
},
50+
],
51+
},
52+
},
53+
],
54+
},
55+
});
56+
expect(event.transaction).toEqual('?/Baz');
57+
});
58+
59+
it('using module and function', async () => {
60+
const event = await transaction.process({
61+
exception: {
62+
values: [
63+
{
64+
stacktrace: {
65+
frames: [
66+
{
67+
filename: '/some/file1.js',
68+
function: 'Bar',
69+
in_app: true,
70+
module: 'Foo',
71+
},
72+
{
73+
filename: '/some/file2.js',
74+
function: 'Baz',
75+
in_app: false,
76+
module: 'Qux',
77+
},
78+
],
79+
},
80+
},
81+
],
82+
},
83+
});
84+
expect(event.transaction).toEqual('Foo/Bar');
85+
});
86+
87+
it('using default', async () => {
88+
const event = await transaction.process({
89+
exception: {
90+
values: [
91+
{
92+
stacktrace: {
93+
frames: [
94+
{
95+
filename: '/some/file1.js',
96+
in_app: false,
97+
},
98+
{
99+
filename: '/some/file2.js',
100+
in_app: true,
101+
},
102+
],
103+
},
104+
},
105+
],
106+
},
107+
});
108+
expect(event.transaction).toEqual('<unknown>');
109+
});
110+
111+
it('no value with no `in_app` frame', async () => {
112+
const event = await transaction.process({
113+
exception: {
114+
values: [
115+
{
116+
stacktrace: {
117+
frames: [
118+
{
119+
filename: '/some/file1.js',
120+
in_app: false,
121+
},
122+
{
123+
filename: '/some/file2.js',
124+
in_app: false,
125+
},
126+
],
127+
},
128+
},
129+
],
130+
},
131+
});
132+
expect(event.transaction).toBeUndefined();
133+
});
134+
});
135+
});

0 commit comments

Comments
 (0)