Skip to content

Commit 0ae5a19

Browse files
committed
feat(opentelemetry): Ignore propagation context when not continuing trace
1 parent d8d9324 commit 0ae5a19

File tree

5 files changed

+45
-56
lines changed

5 files changed

+45
-56
lines changed

dev-packages/node-integration-tests/suites/public-api/startSpan/parallel-root-spans/scenario.ts

+2-14
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,5 @@ Sentry.getCurrentScope().setPropagationContext({
1414
traceId: '12345678901234567890123456789012',
1515
});
1616

17-
const spanIdTraceId = Sentry.startSpan(
18-
{
19-
name: 'test_span_1',
20-
},
21-
span1 => span1.spanContext().traceId,
22-
);
23-
24-
Sentry.startSpan(
25-
{
26-
name: 'test_span_2',
27-
attributes: { spanIdTraceId },
28-
},
29-
() => undefined,
30-
);
17+
Sentry.startSpan({ name: 'test_span_1' }, () => undefined);
18+
Sentry.startSpan({ name: 'test_span_2' }, () => undefined);

dev-packages/node-integration-tests/suites/public-api/startSpan/parallel-root-spans/test.ts

+21-16
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,29 @@ afterAll(() => {
55
});
66

77
test('should send manually started parallel root spans in root context', done => {
8-
expect.assertions(7);
9-
108
createRunner(__dirname, 'scenario.ts')
11-
.expect({ transaction: { transaction: 'test_span_1' } })
129
.expect({
13-
transaction: transaction => {
14-
expect(transaction).toBeDefined();
15-
const traceId = transaction.contexts?.trace?.trace_id;
16-
expect(traceId).toBeDefined();
17-
18-
// It ignores propagation context of the root context
19-
expect(traceId).not.toBe('12345678901234567890123456789012');
20-
expect(transaction.contexts?.trace?.parent_span_id).toBeUndefined();
21-
22-
// Different trace ID than the first span
23-
const trace1Id = transaction.contexts?.trace?.data?.spanIdTraceId;
24-
expect(trace1Id).toBeDefined();
25-
expect(trace1Id).not.toBe(traceId);
10+
transaction: {
11+
transaction: 'test_span_1',
12+
contexts: {
13+
trace: {
14+
span_id: expect.any(String),
15+
parent_span_id: '1234567890123456',
16+
trace_id: '12345678901234567890123456789012',
17+
},
18+
},
19+
},
20+
})
21+
.expect({
22+
transaction: {
23+
transaction: 'test_span_2',
24+
contexts: {
25+
trace: {
26+
span_id: expect.any(String),
27+
parent_span_id: '1234567890123456',
28+
trace_id: '12345678901234567890123456789012',
29+
},
30+
},
2631
},
2732
})
2833
.start(done);

dev-packages/node-integration-tests/suites/public-api/startSpan/parallel-spans-in-scope/test.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ test('should send manually started parallel root spans outside of root context',
1919
const trace1Id = transaction.contexts?.trace?.data?.spanIdTraceId;
2020
expect(trace1Id).toBeDefined();
2121

22-
// Same trace ID as the first span
23-
expect(trace1Id).toBe(traceId);
22+
expect(trace1Id).not.toBe(traceId);
2423
},
2524
})
2625
.start(done);

packages/opentelemetry/src/trace.ts

+11-10
Original file line numberDiff line numberDiff line change
@@ -176,18 +176,18 @@ function ensureTimestampInMilliseconds(timestamp: number): number {
176176

177177
function getContext(scope: Scope | undefined, forceTransaction: boolean | undefined): Context {
178178
const ctx = getContextForScope(scope);
179-
// Note: If the context is the ROOT_CONTEXT, no scope is attached
180-
// Thus we will not use the propagation context in this case, which is desired
181-
const actualScope = getScopesFromContext(ctx)?.scope;
182179
const parentSpan = trace.getSpan(ctx);
183180

184181
// In the case that we have no parent span, we need to "simulate" one to ensure the propagation context is correct
185182
if (!parentSpan) {
186-
const client = getClient();
187-
188-
if (actualScope && client) {
189-
const propagationContext = actualScope.getPropagationContext();
190-
183+
// If we cannot pick a scope from the context (e.g. if it is the root context), we just take the current scope
184+
const actualScope = getScopesFromContext(ctx)?.scope || getCurrentScope();
185+
const propagationContext = actualScope.getPropagationContext();
186+
187+
// If we are continuing an incoming trace, we use it
188+
// This is signified by the presence of `parentSpanId` -
189+
// if this is not set, then we are not continuing an incoming trace
190+
if (propagationContext.parentSpanId) {
191191
// We store the DSC as OTEL trace state on the span context
192192
const traceState = makeTraceState({
193193
parentSpanId: propagationContext.parentSpanId,
@@ -198,7 +198,7 @@ function getContext(scope: Scope | undefined, forceTransaction: boolean | undefi
198198

199199
const spanOptions: SpanContext = {
200200
traceId: propagationContext.traceId,
201-
spanId: propagationContext.parentSpanId || propagationContext.spanId,
201+
spanId: propagationContext.parentSpanId,
202202
isRemote: true,
203203
traceFlags: propagationContext.sampled ? TraceFlags.SAMPLED : TraceFlags.NONE,
204204
traceState,
@@ -208,7 +208,8 @@ function getContext(scope: Scope | undefined, forceTransaction: boolean | undefi
208208
return trace.setSpanContext(ctx, spanOptions);
209209
}
210210

211-
// if we have no scope or client, we just return the context as-is
211+
// if we have no scope, or the propagationContext is not continuing an incoming trace,
212+
// we just let OTEL start a new trace
212213
return ctx;
213214
}
214215

packages/opentelemetry/test/trace.test.ts

+10-14
Original file line numberDiff line numberDiff line change
@@ -329,9 +329,7 @@ describe('trace', () => {
329329
it('allows to pass parentSpan=null', () => {
330330
startSpan({ name: 'GET users/[id' }, () => {
331331
startSpan({ name: 'child', parentSpan: null }, span => {
332-
// Due to the way we propagate the scope in OTEL,
333-
// the parent_span_id is not actually undefined here, but comes from the propagation context
334-
expect(spanToJSON(span).parent_span_id).toBe(getCurrentScope().getPropagationContext().spanId);
332+
expect(spanToJSON(span).parent_span_id).toBe(undefined);
335333
});
336334
});
337335
});
@@ -591,10 +589,7 @@ describe('trace', () => {
591589
it('allows to pass parentSpan=null', () => {
592590
startSpan({ name: 'outer' }, () => {
593591
const span = startInactiveSpan({ name: 'test span', parentSpan: null });
594-
595-
// Due to the way we propagate the scope in OTEL,
596-
// the parent_span_id is not actually undefined here, but comes from the propagation context
597-
expect(spanToJSON(span).parent_span_id).toBe(getCurrentScope().getPropagationContext().spanId);
592+
expect(spanToJSON(span).parent_span_id).toBe(undefined);
598593
span.end();
599594
});
600595
});
@@ -881,9 +876,7 @@ describe('trace', () => {
881876
it('allows to pass parentSpan=null', () => {
882877
startSpan({ name: 'outer' }, () => {
883878
startSpanManual({ name: 'GET users/[id]', parentSpan: null }, span => {
884-
// Due to the way we propagate the scope in OTEL,
885-
// the parent_span_id is not actually undefined here, but comes from the propagation context
886-
expect(spanToJSON(span).parent_span_id).toBe(getCurrentScope().getPropagationContext().spanId);
879+
expect(spanToJSON(span).parent_span_id).toBe(undefined);
887880
span.end();
888881
});
889882
});
@@ -1016,20 +1009,23 @@ describe('trace', () => {
10161009
});
10171010

10181011
describe('propagation', () => {
1019-
it('picks up the trace context from the scope, if there is no parent', () => {
1012+
it('ignores the trace context from the scope, if there is no parent & no propagationContext.parentSpanId', () => {
10201013
withScope(scope => {
10211014
const propagationContext = scope.getPropagationContext();
10221015
const span = startInactiveSpan({ name: 'test span' });
10231016

10241017
expect(span).toBeDefined();
1025-
expect(spanToJSON(span).trace_id).toEqual(propagationContext.traceId);
1026-
expect(spanToJSON(span).parent_span_id).toEqual(propagationContext.spanId);
1018+
expect(spanToJSON(span).trace_id).toMatch(/^[0-9a-f]{32}$/);
1019+
expect(spanToJSON(span).trace_id).not.toEqual(propagationContext.traceId);
1020+
expect(spanToJSON(span).parent_span_id).toEqual(undefined);
10271021

10281022
expect(getDynamicSamplingContextFromSpan(span)).toEqual({
1029-
...getDynamicSamplingContextFromClient(propagationContext.traceId, getClient()!),
1023+
environment: 'production',
1024+
public_key: 'username',
10301025
sample_rate: '1',
10311026
sampled: 'true',
10321027
transaction: 'test span',
1028+
trace_id: spanToJSON(span).trace_id,
10331029
});
10341030
});
10351031
});

0 commit comments

Comments
 (0)