Skip to content

Commit 2fb2b9d

Browse files
committed
add link to trace context
1 parent 84860b4 commit 2fb2b9d

File tree

10 files changed

+265
-166
lines changed

10 files changed

+265
-166
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { loggingTransport } from '@sentry-internal/node-integration-tests';
2+
import * as Sentry from '@sentry/node';
3+
4+
Sentry.init({
5+
dsn: 'https://[email protected]/1337',
6+
release: '1.0',
7+
tracesSampleRate: 1.0,
8+
integrations: [],
9+
transport: loggingTransport,
10+
});
11+
12+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
13+
Sentry.startSpan({ name: 'parent1' }, async parentSpan1 => {
14+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
15+
Sentry.startSpan({ name: 'child1.1' }, async childSpan1 => {
16+
childSpan1.addLink({
17+
context: parentSpan1.spanContext(),
18+
attributes: { 'sentry.link.type': 'previous_trace' },
19+
});
20+
21+
childSpan1.end();
22+
});
23+
24+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
25+
Sentry.startSpan({ name: 'child1.2' }, async childSpan2 => {
26+
childSpan2.addLink({
27+
context: parentSpan1.spanContext(),
28+
attributes: { 'sentry.link.type': 'previous_trace' },
29+
});
30+
31+
childSpan2.end();
32+
});
33+
});

dev-packages/node-integration-tests/suites/tracing/linking/scenario-addLink.ts

+6-19
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,12 @@ Sentry.init({
99
transport: loggingTransport,
1010
});
1111

12-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
13-
Sentry.startSpan({ name: 'parent1' }, async parentSpan1 => {
14-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
15-
Sentry.startSpan({ name: 'child1.1' }, async childSpan1 => {
16-
childSpan1.addLink({
17-
context: parentSpan1.spanContext(),
18-
attributes: { 'sentry.link.type': 'previous_trace' },
19-
});
12+
const span1 = Sentry.startInactiveSpan({ name: 'span1' });
13+
span1.end();
2014

21-
childSpan1.end();
22-
});
23-
24-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
25-
Sentry.startSpan({ name: 'child1.2' }, async childSpan2 => {
26-
childSpan2.addLink({
27-
context: parentSpan1.spanContext(),
28-
attributes: { 'sentry.link.type': 'previous_trace' },
29-
});
30-
31-
childSpan2.end();
15+
Sentry.startSpan({ name: 'rootSpan' }, rootSpan => {
16+
rootSpan.addLink({
17+
context: span1.spanContext(),
18+
attributes: { 'sentry.link.type': 'previous_trace' },
3219
});
3320
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { loggingTransport } from '@sentry-internal/node-integration-tests';
2+
import * as Sentry from '@sentry/node';
3+
4+
Sentry.init({
5+
dsn: 'https://[email protected]/1337',
6+
release: '1.0',
7+
tracesSampleRate: 1.0,
8+
integrations: [],
9+
transport: loggingTransport,
10+
});
11+
12+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
13+
Sentry.startSpan({ name: 'parent1' }, async parentSpan1 => {
14+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
15+
Sentry.startSpan({ name: 'child1.1' }, async childSpan1 => {
16+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
17+
Sentry.startSpan({ name: 'child2.1' }, async childSpan2 => {
18+
childSpan2.addLinks([
19+
{ context: parentSpan1.spanContext() },
20+
{
21+
context: childSpan1.spanContext(),
22+
attributes: { 'sentry.link.type': 'previous_trace' },
23+
},
24+
]);
25+
26+
childSpan2.end();
27+
});
28+
29+
childSpan1.end();
30+
});
31+
});

dev-packages/node-integration-tests/suites/tracing/linking/scenario-addLinks.ts

+12-17
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,18 @@ Sentry.init({
99
transport: loggingTransport,
1010
});
1111

12-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
13-
Sentry.startSpan({ name: 'parent1' }, async parentSpan1 => {
14-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
15-
Sentry.startSpan({ name: 'child1.1' }, async childSpan1 => {
16-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
17-
Sentry.startSpan({ name: 'child2.1' }, async childSpan2 => {
18-
childSpan2.addLinks([
19-
{ context: parentSpan1.spanContext() },
20-
{
21-
context: childSpan1.spanContext(),
22-
attributes: { 'sentry.link.type': 'previous_trace' },
23-
},
24-
]);
12+
const span1 = Sentry.startInactiveSpan({ name: 'span1' });
13+
span1.end();
2514

26-
childSpan2.end();
27-
});
15+
const span2 = Sentry.startInactiveSpan({ name: 'span2' });
16+
span2.end();
2817

29-
childSpan1.end();
30-
});
18+
Sentry.startSpan({ name: 'rootSpan' }, rootSpan => {
19+
rootSpan.addLinks([
20+
{ context: span1.spanContext() },
21+
{
22+
context: span2.spanContext(),
23+
attributes: { 'sentry.link.type': 'previous_trace' },
24+
},
25+
]);
3126
});

dev-packages/node-integration-tests/suites/tracing/linking/test.ts

+142-51
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,164 @@
11
import { createRunner } from '../../../utils/runner';
22

3-
// A general note regarding this test:
4-
// The fact that the trace_id and span_id are correctly linked is tested in a unit test
5-
63
describe('span links', () => {
7-
test('should link spans with addLink()', done => {
4+
test('should link spans with addLink() in trace context', done => {
5+
let span1_traceId: string, span1_spanId: string;
6+
87
createRunner(__dirname, 'scenario-addLink.ts')
98
.expect({
10-
transaction: {
11-
transaction: 'parent1',
12-
spans: [
9+
transaction: event => {
10+
expect(event.transaction).toBe('span1');
11+
12+
// assigning a string to prevent "might be undefined"
13+
span1_traceId = event.contexts?.trace?.trace_id || 'non-existent-trace-id';
14+
span1_spanId = event.contexts?.trace?.span_id || 'non-existent-span-id';
15+
16+
expect(event.spans).toEqual([]);
17+
},
18+
})
19+
.expect({
20+
transaction: event => {
21+
expect(event.transaction).toBe('rootSpan');
22+
23+
expect(event.contexts?.trace?.links).toEqual([
1324
expect.objectContaining({
14-
description: 'child1.1',
15-
links: [
16-
expect.objectContaining({
17-
trace_id: expect.any(String),
18-
span_id: expect.any(String),
19-
attributes: expect.objectContaining({
20-
'sentry.link.type': 'previous_trace',
21-
}),
22-
}),
23-
],
25+
trace_id: expect.stringMatching(span1_traceId),
26+
span_id: expect.stringMatching(span1_spanId),
27+
attributes: expect.objectContaining({
28+
'sentry.link.type': 'previous_trace',
29+
}),
2430
}),
31+
]);
32+
},
33+
})
34+
.start(done);
35+
});
36+
37+
test('should link spans with addLinks() in trace context', done => {
38+
let span1_traceId: string, span1_spanId: string, span2_traceId: string, span2_spanId: string;
39+
40+
createRunner(__dirname, 'scenario-addLinks.ts')
41+
.expect({
42+
transaction: event => {
43+
expect(event.transaction).toBe('span1');
44+
45+
// assigning a string to prevent "might be undefined"
46+
span1_traceId = event.contexts?.trace?.trace_id || 'non-existent-trace-id';
47+
span1_spanId = event.contexts?.trace?.span_id || 'non-existent-span-id';
48+
49+
expect(event.spans).toEqual([]);
50+
},
51+
})
52+
.expect({
53+
transaction: event => {
54+
expect(event.transaction).toBe('span2');
55+
56+
// assigning a string to prevent "might be undefined"
57+
span2_traceId = event.contexts?.trace?.trace_id || 'non-existent-trace-id';
58+
span2_spanId = event.contexts?.trace?.span_id || 'non-existent-span-id';
59+
60+
expect(event.spans).toEqual([]);
61+
},
62+
})
63+
.expect({
64+
transaction: event => {
65+
expect(event.transaction).toBe('rootSpan');
66+
67+
expect(event.contexts?.trace?.links).toEqual([
68+
expect.not.objectContaining({ attributes: expect.anything() }) &&
69+
expect.objectContaining({
70+
trace_id: expect.stringMatching(span1_traceId),
71+
span_id: expect.stringMatching(span1_spanId),
72+
}),
2573
expect.objectContaining({
26-
description: 'child1.2',
27-
links: [
28-
expect.objectContaining({
29-
trace_id: expect.any(String),
30-
span_id: expect.any(String),
31-
attributes: expect.objectContaining({
32-
'sentry.link.type': 'previous_trace',
33-
}),
34-
}),
35-
],
74+
trace_id: expect.stringMatching(span2_traceId),
75+
span_id: expect.stringMatching(span2_spanId),
76+
attributes: expect.objectContaining({
77+
'sentry.link.type': 'previous_trace',
78+
}),
3679
}),
37-
],
80+
]);
3881
},
3982
})
4083
.start(done);
4184
});
4285

43-
test('should link spans with addLinks()', done => {
44-
createRunner(__dirname, 'scenario-addLinks.ts')
86+
test('should link spans with addLink() in nested startSpan() calls', done => {
87+
createRunner(__dirname, 'scenario-addLink-nested.ts')
4588
.expect({
46-
transaction: {
47-
transaction: 'parent1',
48-
spans: [
89+
transaction: event => {
90+
expect(event.transaction).toBe('parent1');
91+
92+
// assigning a string to prevent "might be undefined"
93+
const parent1_traceId = event.contexts?.trace?.trace_id || 'non-existent-span-id';
94+
const parent1_spanId = event.contexts?.trace?.span_id || 'non-existent-span-id';
95+
96+
const spans = event.spans || [];
97+
const child1_1 = spans.find(span => span.description === 'child1.1');
98+
const child1_2 = spans.find(span => span.description === 'child1.2');
99+
100+
expect(child1_1).toBeDefined();
101+
expect(child1_1?.links).toEqual([
102+
expect.objectContaining({
103+
trace_id: expect.stringMatching(parent1_traceId),
104+
span_id: expect.stringMatching(parent1_spanId),
105+
attributes: expect.objectContaining({
106+
'sentry.link.type': 'previous_trace',
107+
}),
108+
}),
109+
]);
110+
111+
expect(child1_2).toBeDefined();
112+
expect(child1_2?.links).toEqual([
49113
expect.objectContaining({
50-
description: 'child1.1',
51-
links: [],
114+
trace_id: expect.stringMatching(parent1_traceId),
115+
span_id: expect.stringMatching(parent1_spanId),
116+
attributes: expect.objectContaining({
117+
'sentry.link.type': 'previous_trace',
118+
}),
52119
}),
120+
]);
121+
},
122+
})
123+
.start(done);
124+
});
125+
126+
test('should link spans with addLinks() in nested startSpan() calls', done => {
127+
createRunner(__dirname, 'scenario-addLinks-nested.ts')
128+
.expect({
129+
transaction: event => {
130+
expect(event.transaction).toBe('parent1');
131+
132+
// assigning a string to prevent "might be undefined"
133+
const parent1_traceId = event.contexts?.trace?.trace_id || 'non-existent-span-id';
134+
const parent1_spanId = event.contexts?.trace?.span_id || 'non-existent-span-id';
135+
136+
const spans = event.spans || [];
137+
const child1_1 = spans.find(span => span.description === 'child1.1');
138+
const child2_1 = spans.find(span => span.description === 'child2.1');
139+
140+
expect(child1_1).toBeDefined();
141+
142+
expect(child2_1).toBeDefined();
143+
144+
expect(child2_1?.links).toEqual([
145+
expect.not.objectContaining({ attributes: expect.anything() }) &&
146+
expect.objectContaining({
147+
trace_id: expect.stringMatching(parent1_traceId),
148+
span_id: expect.stringMatching(parent1_spanId),
149+
}),
53150
expect.objectContaining({
54-
description: 'child2.1',
55-
links: [
56-
expect.not.objectContaining({ attributes: expect.anything() }) &&
57-
expect.objectContaining({
58-
trace_id: expect.any(String),
59-
span_id: expect.any(String),
60-
}),
61-
expect.objectContaining({
62-
trace_id: expect.any(String),
63-
span_id: expect.any(String),
64-
attributes: expect.objectContaining({
65-
'sentry.link.type': 'previous_trace',
66-
}),
67-
}),
68-
],
151+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
152+
// @ts-expect-error traceID is defined
153+
trace_id: expect.stringMatching(child1_1?.trace_id),
154+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
155+
// @ts-expect-error spanID is defined
156+
span_id: expect.stringMatching(child1_1?.span_id),
157+
attributes: expect.objectContaining({
158+
'sentry.link.type': 'previous_trace',
159+
}),
69160
}),
70-
],
161+
]);
71162
},
72163
})
73164
.start(done);

packages/core/src/types-hoist/context.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { FeatureFlag } from '../featureFlags';
2+
import type { SpanLinkJSON } from './link';
23
import type { Primitive } from './misc';
34
import type { SpanOrigin } from './span';
45

@@ -106,6 +107,7 @@ export interface TraceContext extends Record<string, unknown> {
106107
tags?: { [key: string]: Primitive };
107108
trace_id: string;
108109
origin?: SpanOrigin;
110+
links?: SpanLinkJSON[];
109111
}
110112

111113
export interface CloudResourceContext extends Record<string, unknown> {

0 commit comments

Comments
 (0)