Skip to content

Commit 8c495e1

Browse files
mariojonkec24t
authored andcommitted
Add indicator if SpanContext was propagated from remote parent (open-telemetry#516)
According to the spec a SpanContext should have an indicator if it was propagated from a remote parent. Introduces an is_remote flag on the SpanContext which gets set when the SpanContext is extracted in a propagaton.
1 parent 656d3a4 commit 8c495e1

File tree

13 files changed

+116
-31
lines changed

13 files changed

+116
-31
lines changed

Diff for: ext/opentelemetry-ext-jaeger/tests/test_jaeger_exporter.py

+10-3
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ def setUp(self):
3131
context = trace_api.SpanContext(
3232
trace_id=0x000000000000000000000000DEADBEEF,
3333
span_id=0x00000000DEADBEF0,
34+
is_remote=False,
3435
)
3536

3637
self._test_span = trace.Span("test_span", context=context)
@@ -133,9 +134,15 @@ def test_translate_to_jaeger(self):
133134
start_times[2] + durations[2],
134135
)
135136

136-
span_context = trace_api.SpanContext(trace_id, span_id)
137-
parent_context = trace_api.SpanContext(trace_id, parent_id)
138-
other_context = trace_api.SpanContext(trace_id, other_id)
137+
span_context = trace_api.SpanContext(
138+
trace_id, span_id, is_remote=False
139+
)
140+
parent_context = trace_api.SpanContext(
141+
trace_id, parent_id, is_remote=False
142+
)
143+
other_context = trace_api.SpanContext(
144+
trace_id, other_id, is_remote=False
145+
)
139146

140147
event_attributes = {
141148
"annotation_bool": True,

Diff for: ext/opentelemetry-ext-opentracing-shim/tests/test_shim.py

+11-4
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@ def test_log_event(self):
450450
def test_span_context(self):
451451
"""Test construction of `SpanContextShim` objects."""
452452

453-
otel_context = trace.SpanContext(1234, 5678)
453+
otel_context = trace.SpanContext(1234, 5678, is_remote=False)
454454
context = opentracingshim.SpanContextShim(otel_context)
455455

456456
self.assertIsInstance(context, opentracing.SpanContext)
@@ -476,7 +476,9 @@ def test_span_on_error(self):
476476
def test_inject_http_headers(self):
477477
"""Test `inject()` method for Format.HTTP_HEADERS."""
478478

479-
otel_context = trace.SpanContext(trace_id=1220, span_id=7478)
479+
otel_context = trace.SpanContext(
480+
trace_id=1220, span_id=7478, is_remote=False
481+
)
480482
context = opentracingshim.SpanContextShim(otel_context)
481483

482484
headers = {}
@@ -487,7 +489,9 @@ def test_inject_http_headers(self):
487489
def test_inject_text_map(self):
488490
"""Test `inject()` method for Format.TEXT_MAP."""
489491

490-
otel_context = trace.SpanContext(trace_id=1220, span_id=7478)
492+
otel_context = trace.SpanContext(
493+
trace_id=1220, span_id=7478, is_remote=False
494+
)
491495
context = opentracingshim.SpanContextShim(otel_context)
492496

493497
# Verify Format.TEXT_MAP
@@ -499,7 +503,9 @@ def test_inject_text_map(self):
499503
def test_inject_binary(self):
500504
"""Test `inject()` method for Format.BINARY."""
501505

502-
otel_context = trace.SpanContext(trace_id=1220, span_id=7478)
506+
otel_context = trace.SpanContext(
507+
trace_id=1220, span_id=7478, is_remote=False
508+
)
503509
context = opentracingshim.SpanContextShim(otel_context)
504510

505511
# Verify exception for non supported binary format.
@@ -561,6 +567,7 @@ def extract(
561567
trace.SpanContext(
562568
trace_id=int(trace_id_list[0]),
563569
span_id=int(span_id_list[0]),
570+
is_remote=True,
564571
)
565572
)
566573
)

Diff for: ext/opentelemetry-ext-otcollector/tests/test_otcollector_trace_exporter.py

+11-3
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,16 @@ def test_translate_to_collector(self):
9292
span_context = trace_api.SpanContext(
9393
trace_id,
9494
span_id,
95+
is_remote=False,
9596
trace_flags=TraceFlags(TraceFlags.SAMPLED),
9697
trace_state=trace_api.TraceState({"testKey": "testValue"}),
9798
)
98-
parent_context = trace_api.SpanContext(trace_id, parent_id)
99-
other_context = trace_api.SpanContext(trace_id, span_id)
99+
parent_context = trace_api.SpanContext(
100+
trace_id, parent_id, is_remote=False
101+
)
102+
other_context = trace_api.SpanContext(
103+
trace_id, span_id, is_remote=False
104+
)
100105
event_attributes = {
101106
"annotation_bool": True,
102107
"annotation_string": "annotation_test",
@@ -279,7 +284,10 @@ def test_export(self):
279284
trace_id = 0x6E0C63257DE34C926F9EFCD03927272E
280285
span_id = 0x34BF92DEEFC58C92
281286
span_context = trace_api.SpanContext(
282-
trace_id, span_id, trace_flags=TraceFlags(TraceFlags.SAMPLED)
287+
trace_id,
288+
span_id,
289+
is_remote=False,
290+
trace_flags=TraceFlags(TraceFlags.SAMPLED),
283291
)
284292
otel_spans = [
285293
trace.Span(

Diff for: ext/opentelemetry-ext-zipkin/tests/test_zipkin_exporter.py

+11-3
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ def setUp(self):
3535
context = trace_api.SpanContext(
3636
trace_id=0x000000000000000000000000DEADBEEF,
3737
span_id=0x00000000DEADBEF0,
38+
is_remote=False,
3839
)
3940

4041
self._test_span = trace.Span("test_span", context=context)
@@ -114,10 +115,17 @@ def test_export(self):
114115
)
115116

116117
span_context = trace_api.SpanContext(
117-
trace_id, span_id, trace_flags=TraceFlags(TraceFlags.SAMPLED)
118+
trace_id,
119+
span_id,
120+
is_remote=False,
121+
trace_flags=TraceFlags(TraceFlags.SAMPLED),
122+
)
123+
parent_context = trace_api.SpanContext(
124+
trace_id, parent_id, is_remote=False
125+
)
126+
other_context = trace_api.SpanContext(
127+
trace_id, other_id, is_remote=False
118128
)
119-
parent_context = trace_api.SpanContext(trace_id, parent_id)
120-
other_context = trace_api.SpanContext(trace_id, other_id)
121129

122130
event_attributes = {
123131
"annotation_bool": True,

Diff for: opentelemetry-api/src/opentelemetry/trace/__init__.py

+12-5
Original file line numberDiff line numberDiff line change
@@ -319,12 +319,14 @@ class SpanContext:
319319
span_id: This span's ID.
320320
trace_flags: Trace options to propagate.
321321
trace_state: Tracing-system-specific info to propagate.
322+
is_remote: True if propagated from a remote parent.
322323
"""
323324

324325
def __init__(
325326
self,
326327
trace_id: int,
327328
span_id: int,
329+
is_remote: bool,
328330
trace_flags: "TraceFlags" = DEFAULT_TRACE_OPTIONS,
329331
trace_state: "TraceState" = DEFAULT_TRACE_STATE,
330332
) -> None:
@@ -336,13 +338,17 @@ def __init__(
336338
self.span_id = span_id
337339
self.trace_flags = trace_flags
338340
self.trace_state = trace_state
341+
self.is_remote = is_remote
339342

340343
def __repr__(self) -> str:
341-
return "{}(trace_id={}, span_id={}, trace_state={!r})".format(
344+
return (
345+
"{}(trace_id={}, span_id={}, trace_state={!r}, is_remote={})"
346+
).format(
342347
type(self).__name__,
343348
format_trace_id(self.trace_id),
344349
format_span_id(self.span_id),
345350
self.trace_state,
351+
self.is_remote,
346352
)
347353

348354
def is_valid(self) -> bool:
@@ -402,10 +408,11 @@ def set_status(self, status: Status) -> None:
402408
INVALID_SPAN_ID = 0x0000000000000000
403409
INVALID_TRACE_ID = 0x00000000000000000000000000000000
404410
INVALID_SPAN_CONTEXT = SpanContext(
405-
INVALID_TRACE_ID,
406-
INVALID_SPAN_ID,
407-
DEFAULT_TRACE_OPTIONS,
408-
DEFAULT_TRACE_STATE,
411+
trace_id=INVALID_TRACE_ID,
412+
span_id=INVALID_SPAN_ID,
413+
is_remote=False,
414+
trace_flags=DEFAULT_TRACE_OPTIONS,
415+
trace_state=DEFAULT_TRACE_STATE,
409416
)
410417
INVALID_SPAN = DefaultSpan(INVALID_SPAN_CONTEXT)
411418

Diff for: opentelemetry-api/src/opentelemetry/trace/propagation/tracecontexthttptextformat.py

+1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ def extract(
105105
span_context = trace.SpanContext(
106106
trace_id=int(trace_id, 16),
107107
span_id=int(span_id, 16),
108+
is_remote=True,
108109
trace_flags=trace.TraceFlags(trace_flags),
109110
trace_state=tracestate,
110111
)

Diff for: opentelemetry-api/tests/trace/propagation/test_tracecontexthttptextformat.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ def test_headers_with_tracestate(self):
7272
self.assertEqual(
7373
span_context.trace_state, {"foo": "1", "bar": "2", "baz": "3"}
7474
)
75+
self.assertTrue(span_context.is_remote)
7576
output = {} # type:typing.Dict[str, str]
7677
span = trace.DefaultSpan(span_context)
7778

@@ -155,7 +156,7 @@ def test_no_send_empty_tracestate(self):
155156
"""
156157
output = {} # type:typing.Dict[str, str]
157158
span = trace.DefaultSpan(
158-
trace.SpanContext(self.TRACE_ID, self.SPAN_ID)
159+
trace.SpanContext(self.TRACE_ID, self.SPAN_ID, is_remote=False)
159160
)
160161
ctx = set_span_in_context(span)
161162
FORMAT.inject(dict.__setitem__, output, ctx)

Diff for: opentelemetry-api/tests/trace/test_defaultspan.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@
2020
class TestDefaultSpan(unittest.TestCase):
2121
def test_ctor(self):
2222
context = trace.SpanContext(
23-
1, 1, trace.DEFAULT_TRACE_OPTIONS, trace.DEFAULT_TRACE_STATE
23+
1,
24+
1,
25+
is_remote=False,
26+
trace_flags=trace.DEFAULT_TRACE_OPTIONS,
27+
trace_state=trace.DEFAULT_TRACE_STATE,
2428
)
2529
span = trace.DefaultSpan(context)
2630
self.assertEqual(context, span.get_context())

Diff for: opentelemetry-api/tests/trace/test_sampling.py

+32-10
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@
2525
class TestSampler(unittest.TestCase):
2626
def test_always_on(self):
2727
no_record_always_on = sampling.ALWAYS_ON.should_sample(
28-
trace.SpanContext(0xDEADBEEF, 0xDEADBEF0, trace_flags=TO_DEFAULT),
28+
trace.SpanContext(
29+
0xDEADBEEF, 0xDEADBEF0, is_remote=False, trace_flags=TO_DEFAULT
30+
),
2931
0xDEADBEF1,
3032
0xDEADBEF2,
3133
"unsampled parent, sampling on",
@@ -34,7 +36,9 @@ def test_always_on(self):
3436
self.assertEqual(no_record_always_on.attributes, {})
3537

3638
sampled_always_on = sampling.ALWAYS_ON.should_sample(
37-
trace.SpanContext(0xDEADBEEF, 0xDEADBEF0, trace_flags=TO_SAMPLED),
39+
trace.SpanContext(
40+
0xDEADBEEF, 0xDEADBEF0, is_remote=False, trace_flags=TO_SAMPLED
41+
),
3842
0xDEADBEF1,
3943
0xDEADBEF2,
4044
"sampled parent, sampling on",
@@ -44,7 +48,9 @@ def test_always_on(self):
4448

4549
def test_always_off(self):
4650
no_record_always_off = sampling.ALWAYS_OFF.should_sample(
47-
trace.SpanContext(0xDEADBEEF, 0xDEADBEF0, trace_flags=TO_DEFAULT),
51+
trace.SpanContext(
52+
0xDEADBEEF, 0xDEADBEF0, is_remote=False, trace_flags=TO_DEFAULT
53+
),
4854
0xDEADBEF1,
4955
0xDEADBEF2,
5056
"unsampled parent, sampling off",
@@ -53,7 +59,9 @@ def test_always_off(self):
5359
self.assertEqual(no_record_always_off.attributes, {})
5460

5561
sampled_always_on = sampling.ALWAYS_OFF.should_sample(
56-
trace.SpanContext(0xDEADBEEF, 0xDEADBEF0, trace_flags=TO_SAMPLED),
62+
trace.SpanContext(
63+
0xDEADBEEF, 0xDEADBEF0, is_remote=False, trace_flags=TO_SAMPLED
64+
),
5765
0xDEADBEF1,
5866
0xDEADBEF2,
5967
"sampled parent, sampling off",
@@ -63,7 +71,9 @@ def test_always_off(self):
6371

6472
def test_default_on(self):
6573
no_record_default_on = sampling.DEFAULT_ON.should_sample(
66-
trace.SpanContext(0xDEADBEEF, 0xDEADBEF0, trace_flags=TO_DEFAULT),
74+
trace.SpanContext(
75+
0xDEADBEEF, 0xDEADBEF0, is_remote=False, trace_flags=TO_DEFAULT
76+
),
6777
0xDEADBEF1,
6878
0xDEADBEF2,
6979
"unsampled parent, sampling on",
@@ -72,7 +82,9 @@ def test_default_on(self):
7282
self.assertEqual(no_record_default_on.attributes, {})
7383

7484
sampled_default_on = sampling.DEFAULT_ON.should_sample(
75-
trace.SpanContext(0xDEADBEEF, 0xDEADBEF0, trace_flags=TO_SAMPLED),
85+
trace.SpanContext(
86+
0xDEADBEEF, 0xDEADBEF0, is_remote=False, trace_flags=TO_SAMPLED
87+
),
7688
0xDEADBEF1,
7789
0xDEADBEF2,
7890
"sampled parent, sampling on",
@@ -82,7 +94,9 @@ def test_default_on(self):
8294

8395
def test_default_off(self):
8496
no_record_default_off = sampling.DEFAULT_OFF.should_sample(
85-
trace.SpanContext(0xDEADBEEF, 0xDEADBEF0, trace_flags=TO_DEFAULT),
97+
trace.SpanContext(
98+
0xDEADBEEF, 0xDEADBEF0, is_remote=False, trace_flags=TO_DEFAULT
99+
),
86100
0xDEADBEF1,
87101
0xDEADBEF2,
88102
"unsampled parent, sampling off",
@@ -91,7 +105,9 @@ def test_default_off(self):
91105
self.assertEqual(no_record_default_off.attributes, {})
92106

93107
sampled_default_off = sampling.DEFAULT_OFF.should_sample(
94-
trace.SpanContext(0xDEADBEEF, 0xDEADBEF0, trace_flags=TO_SAMPLED),
108+
trace.SpanContext(
109+
0xDEADBEEF, 0xDEADBEF0, is_remote=False, trace_flags=TO_SAMPLED
110+
),
95111
0xDEADBEF1,
96112
0xDEADBEF2,
97113
"sampled parent, sampling off",
@@ -120,7 +136,10 @@ def test_probability_sampler(self):
120136
self.assertFalse(
121137
sampler.should_sample(
122138
trace.SpanContext(
123-
0xDEADBEF0, 0xDEADBEF1, trace_flags=TO_DEFAULT
139+
0xDEADBEF0,
140+
0xDEADBEF1,
141+
is_remote=False,
142+
trace_flags=TO_DEFAULT,
124143
),
125144
0x7FFFFFFFFFFFFFFF,
126145
0xDEADBEEF,
@@ -130,7 +149,10 @@ def test_probability_sampler(self):
130149
self.assertTrue(
131150
sampler.should_sample(
132151
trace.SpanContext(
133-
0xDEADBEF0, 0xDEADBEF1, trace_flags=TO_SAMPLED
152+
0xDEADBEF0,
153+
0xDEADBEF1,
154+
is_remote=False,
155+
trace_flags=TO_SAMPLED,
134156
),
135157
0x8000000000000000,
136158
0xDEADBEEF,

Diff for: opentelemetry-sdk/src/opentelemetry/sdk/context/propagation/b3_format.py

+1
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ def extract(
112112
# trace an span ids are encoded in hex, so must be converted
113113
trace_id=int(trace_id, 16),
114114
span_id=int(span_id, 16),
115+
is_remote=True,
115116
trace_flags=trace.TraceFlags(options),
116117
trace_state=trace.TraceState(),
117118
)

Diff for: opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,11 @@ def start_span( # pylint: disable=too-many-locals
453453
trace_state = parent_context.trace_state
454454

455455
context = trace_api.SpanContext(
456-
trace_id, generate_span_id(), trace_flags, trace_state
456+
trace_id,
457+
generate_span_id(),
458+
is_remote=False,
459+
trace_flags=trace_flags,
460+
trace_state=trace_state,
457461
)
458462

459463
# The sampler decides whether to create a real or no-op span at the

Diff for: opentelemetry-sdk/tests/context/propagation/test_b3_format.py

+4
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def get_child_parent_new_carrier(old_carrier):
4141
trace_api.SpanContext(
4242
parent_context.trace_id,
4343
trace.generate_span_id(),
44+
is_remote=False,
4445
trace_flags=parent_context.trace_flags,
4546
trace_state=parent_context.trace_state,
4647
),
@@ -90,6 +91,7 @@ def test_extract_multi_header(self):
9091
new_carrier[FORMAT.PARENT_SPAN_ID_KEY],
9192
b3_format.format_span_id(parent.context.span_id),
9293
)
94+
self.assertTrue(parent.context.is_remote)
9395
self.assertEqual(new_carrier[FORMAT.SAMPLED_KEY], "1")
9496

9597
def test_extract_single_header(self):
@@ -111,6 +113,7 @@ def test_extract_single_header(self):
111113
b3_format.format_span_id(child.context.span_id),
112114
)
113115
self.assertEqual(new_carrier[FORMAT.SAMPLED_KEY], "1")
116+
self.assertTrue(parent.context.is_remote)
114117

115118
child, parent, new_carrier = get_child_parent_new_carrier(
116119
{
@@ -134,6 +137,7 @@ def test_extract_single_header(self):
134137
new_carrier[FORMAT.PARENT_SPAN_ID_KEY],
135138
b3_format.format_span_id(parent.context.span_id),
136139
)
140+
self.assertTrue(parent.context.is_remote)
137141
self.assertEqual(new_carrier[FORMAT.SAMPLED_KEY], "1")
138142

139143
def test_extract_header_precedence(self):

0 commit comments

Comments
 (0)