Skip to content

Commit db91e97

Browse files
committed
Add support for B3 parentspanid
Fixes #236
1 parent 3d441b1 commit db91e97

File tree

4 files changed

+151
-78
lines changed

4 files changed

+151
-78
lines changed

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

+16-5
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class B3Format(HTTPTextFormat):
2727
SINGLE_HEADER_KEY = "b3"
2828
TRACE_ID_KEY = "x-b3-traceid"
2929
SPAN_ID_KEY = "x-b3-spanid"
30+
PARENT_SPAN_ID_KEY = "x-b3-parentspanid"
3031
SAMPLED_KEY = "x-b3-sampled"
3132
FLAGS_KEY = "x-b3-flags"
3233
_SAMPLE_PROPAGATE_VALUES = set(["1", "True", "true", "d"])
@@ -55,7 +56,7 @@ def extract(cls, get_from_carrier, carrier):
5556
elif len(fields) == 3:
5657
trace_id, span_id, sampled = fields
5758
elif len(fields) == 4:
58-
trace_id, span_id, sampled, _parent_span_id = fields
59+
trace_id, span_id, sampled, _ = fields
5960
else:
6061
return trace.INVALID_SPAN_CONTEXT
6162
else:
@@ -100,14 +101,24 @@ def extract(cls, get_from_carrier, carrier):
100101
)
101102

102103
@classmethod
103-
def inject(cls, context, set_in_carrier, carrier):
104-
sampled = (trace.TraceOptions.SAMPLED & context.trace_options) != 0
104+
def inject(
105+
cls, span, set_in_carrier, carrier
106+
): # pylint: disable=arguments-differ
107+
sampled = (
108+
trace.TraceOptions.SAMPLED & span.context.trace_options
109+
) != 0
105110
set_in_carrier(
106-
carrier, cls.TRACE_ID_KEY, format_trace_id(context.trace_id)
111+
carrier, cls.TRACE_ID_KEY, format_trace_id(span.context.trace_id)
107112
)
108113
set_in_carrier(
109-
carrier, cls.SPAN_ID_KEY, format_span_id(context.span_id)
114+
carrier, cls.SPAN_ID_KEY, format_span_id(span.context.span_id)
110115
)
116+
if span.parent is not None:
117+
set_in_carrier(
118+
carrier,
119+
cls.PARENT_SPAN_ID_KEY,
120+
format_span_id(span.parent.context.span_id),
121+
)
111122
set_in_carrier(carrier, cls.SAMPLED_KEY, "1" if sampled else "0")
112123

113124

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,10 @@ def __repr__(self):
174174
)
175175

176176
def __str__(self):
177-
return '{}(name="{}", context={}, kind={}, parent={}, start_time={}, end_time={})'.format(
177+
return (
178+
'{}(name="{}", context={}, kind={}, parent={}, '
179+
"start_time={}, end_time={})"
180+
).format(
178181
type(self).__name__,
179182
self.name,
180183
self.context,

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

+130-71
Original file line numberDiff line numberDiff line change
@@ -35,40 +35,97 @@ def setUpClass(cls):
3535
cls.serialized_span_id = b3_format.format_span_id(
3636
trace.generate_span_id()
3737
)
38+
cls.serialized_parent_id = b3_format.format_span_id(
39+
trace.generate_span_id()
40+
)
41+
42+
def get_child_parent_new_carrier(self, old_carrier):
43+
44+
parent_context = FORMAT.extract(get_as_list, old_carrier)
45+
46+
parent = trace.Span("parent", parent_context)
47+
child = trace.Span(
48+
"child",
49+
trace_api.SpanContext(
50+
parent_context.trace_id,
51+
trace.generate_span_id(),
52+
trace_options=parent_context.trace_options,
53+
trace_state=parent_context.trace_state,
54+
),
55+
parent=parent
56+
)
57+
58+
new_carrier = {}
59+
FORMAT.inject(child, dict.__setitem__, new_carrier)
60+
61+
return child, parent, new_carrier
3862

3963
def test_extract_multi_header(self):
4064
"""Test the extraction of B3 headers."""
41-
carrier = {
42-
FORMAT.TRACE_ID_KEY: self.serialized_trace_id,
43-
FORMAT.SPAN_ID_KEY: self.serialized_span_id,
44-
FORMAT.SAMPLED_KEY: "1",
45-
}
46-
span_context = FORMAT.extract(get_as_list, carrier)
47-
new_carrier = {}
48-
FORMAT.inject(span_context, dict.__setitem__, new_carrier)
65+
child, parent, new_carrier = self.get_child_parent_new_carrier(
66+
{
67+
FORMAT.TRACE_ID_KEY: self.serialized_trace_id,
68+
FORMAT.SPAN_ID_KEY: self.serialized_span_id,
69+
FORMAT.PARENT_SPAN_ID_KEY: self.serialized_parent_id,
70+
FORMAT.SAMPLED_KEY: "1",
71+
}
72+
)
73+
74+
self.assertEqual(
75+
new_carrier[FORMAT.TRACE_ID_KEY],
76+
b3_format.format_trace_id(child.context.trace_id),
77+
)
4978
self.assertEqual(
50-
new_carrier[FORMAT.TRACE_ID_KEY], self.serialized_trace_id
79+
new_carrier[FORMAT.SPAN_ID_KEY],
80+
b3_format.format_span_id(child.context.span_id),
5181
)
5282
self.assertEqual(
53-
new_carrier[FORMAT.SPAN_ID_KEY], self.serialized_span_id
83+
new_carrier[FORMAT.PARENT_SPAN_ID_KEY],
84+
b3_format.format_span_id(parent.context.span_id),
5485
)
5586
self.assertEqual(new_carrier[FORMAT.SAMPLED_KEY], "1")
5687

5788
def test_extract_single_header(self):
5889
"""Test the extraction from a single b3 header."""
59-
carrier = {
60-
FORMAT.SINGLE_HEADER_KEY: "{}-{}".format(
61-
self.serialized_trace_id, self.serialized_span_id
62-
)
63-
}
64-
span_context = FORMAT.extract(get_as_list, carrier)
65-
new_carrier = {}
66-
FORMAT.inject(span_context, dict.__setitem__, new_carrier)
90+
child, parent, new_carrier = self.get_child_parent_new_carrier(
91+
{
92+
FORMAT.SINGLE_HEADER_KEY: "{}-{}".format(
93+
self.serialized_trace_id, self.serialized_span_id
94+
)
95+
}
96+
)
97+
6798
self.assertEqual(
68-
new_carrier[FORMAT.TRACE_ID_KEY], self.serialized_trace_id
99+
new_carrier[FORMAT.TRACE_ID_KEY],
100+
b3_format.format_trace_id(child.context.trace_id),
69101
)
70102
self.assertEqual(
71-
new_carrier[FORMAT.SPAN_ID_KEY], self.serialized_span_id
103+
new_carrier[FORMAT.SPAN_ID_KEY],
104+
b3_format.format_span_id(child.context.span_id),
105+
)
106+
self.assertEqual(new_carrier[FORMAT.SAMPLED_KEY], "1")
107+
108+
child, parent, new_carrier = self.get_child_parent_new_carrier(
109+
{
110+
FORMAT.SINGLE_HEADER_KEY: "{}-{}-1-{}".format(
111+
self.serialized_trace_id,
112+
self.serialized_span_id,
113+
self.serialized_parent_id,
114+
)
115+
}
116+
)
117+
118+
self.assertEqual(
119+
new_carrier[FORMAT.TRACE_ID_KEY],
120+
b3_format.format_trace_id(child.context.trace_id),
121+
)
122+
self.assertEqual(
123+
new_carrier[FORMAT.SPAN_ID_KEY],
124+
b3_format.format_span_id(child.context.span_id),
125+
)
126+
self.assertEqual(
127+
new_carrier[FORMAT.PARENT_SPAN_ID_KEY],
128+
b3_format.format_span_id(parent.context.span_id),
72129
)
73130
self.assertEqual(new_carrier[FORMAT.SAMPLED_KEY], "1")
74131

@@ -77,82 +134,84 @@ def test_extract_header_precedence(self):
77134
headers.
78135
"""
79136
single_header_trace_id = self.serialized_trace_id[:-3] + "123"
80-
carrier = {
81-
FORMAT.SINGLE_HEADER_KEY: "{}-{}".format(
82-
single_header_trace_id, self.serialized_span_id
83-
),
84-
FORMAT.TRACE_ID_KEY: self.serialized_trace_id,
85-
FORMAT.SPAN_ID_KEY: self.serialized_span_id,
86-
FORMAT.SAMPLED_KEY: "1",
87-
}
88-
span_context = FORMAT.extract(get_as_list, carrier)
89-
new_carrier = {}
90-
FORMAT.inject(span_context, dict.__setitem__, new_carrier)
137+
138+
child, parent, new_carrier = self.get_child_parent_new_carrier(
139+
{
140+
FORMAT.SINGLE_HEADER_KEY: "{}-{}".format(
141+
single_header_trace_id, self.serialized_span_id
142+
),
143+
FORMAT.TRACE_ID_KEY: self.serialized_trace_id,
144+
FORMAT.SPAN_ID_KEY: self.serialized_span_id,
145+
FORMAT.SAMPLED_KEY: "1",
146+
}
147+
)
148+
91149
self.assertEqual(
92150
new_carrier[FORMAT.TRACE_ID_KEY], single_header_trace_id
93151
)
94152

95153
def test_enabled_sampling(self):
96154
"""Test b3 sample key variants that turn on sampling."""
97155
for variant in ["1", "True", "true", "d"]:
98-
carrier = {
99-
FORMAT.TRACE_ID_KEY: self.serialized_trace_id,
100-
FORMAT.SPAN_ID_KEY: self.serialized_span_id,
101-
FORMAT.SAMPLED_KEY: variant,
102-
}
103-
span_context = FORMAT.extract(get_as_list, carrier)
104-
new_carrier = {}
105-
FORMAT.inject(span_context, dict.__setitem__, new_carrier)
156+
child, parent, new_carrier = self.get_child_parent_new_carrier(
157+
{
158+
FORMAT.TRACE_ID_KEY: self.serialized_trace_id,
159+
FORMAT.SPAN_ID_KEY: self.serialized_span_id,
160+
FORMAT.SAMPLED_KEY: variant,
161+
}
162+
)
163+
106164
self.assertEqual(new_carrier[FORMAT.SAMPLED_KEY], "1")
107165

108166
def test_disabled_sampling(self):
109167
"""Test b3 sample key variants that turn off sampling."""
110168
for variant in ["0", "False", "false", None]:
111-
carrier = {
112-
FORMAT.TRACE_ID_KEY: self.serialized_trace_id,
113-
FORMAT.SPAN_ID_KEY: self.serialized_span_id,
114-
FORMAT.SAMPLED_KEY: variant,
115-
}
116-
span_context = FORMAT.extract(get_as_list, carrier)
117-
new_carrier = {}
118-
FORMAT.inject(span_context, dict.__setitem__, new_carrier)
169+
child, parent, new_carrier = self.get_child_parent_new_carrier(
170+
{
171+
FORMAT.TRACE_ID_KEY: self.serialized_trace_id,
172+
FORMAT.SPAN_ID_KEY: self.serialized_span_id,
173+
FORMAT.SAMPLED_KEY: variant,
174+
}
175+
)
176+
119177
self.assertEqual(new_carrier[FORMAT.SAMPLED_KEY], "0")
120178

121179
def test_flags(self):
122180
"""x-b3-flags set to "1" should result in propagation."""
123-
carrier = {
124-
FORMAT.TRACE_ID_KEY: self.serialized_trace_id,
125-
FORMAT.SPAN_ID_KEY: self.serialized_span_id,
126-
FORMAT.FLAGS_KEY: "1",
127-
}
128-
span_context = FORMAT.extract(get_as_list, carrier)
129-
new_carrier = {}
130-
FORMAT.inject(span_context, dict.__setitem__, new_carrier)
181+
child, parent, new_carrier = self.get_child_parent_new_carrier(
182+
{
183+
FORMAT.TRACE_ID_KEY: self.serialized_trace_id,
184+
FORMAT.SPAN_ID_KEY: self.serialized_span_id,
185+
FORMAT.FLAGS_KEY: "1",
186+
}
187+
)
188+
131189
self.assertEqual(new_carrier[FORMAT.SAMPLED_KEY], "1")
132190

133191
def test_flags_and_sampling(self):
134192
"""Propagate if b3 flags and sampling are set."""
135-
carrier = {
136-
FORMAT.TRACE_ID_KEY: self.serialized_trace_id,
137-
FORMAT.SPAN_ID_KEY: self.serialized_span_id,
138-
FORMAT.FLAGS_KEY: "1",
139-
}
140-
span_context = FORMAT.extract(get_as_list, carrier)
141-
new_carrier = {}
142-
FORMAT.inject(span_context, dict.__setitem__, new_carrier)
193+
child, parent, new_carrier = self.get_child_parent_new_carrier(
194+
{
195+
FORMAT.TRACE_ID_KEY: self.serialized_trace_id,
196+
FORMAT.SPAN_ID_KEY: self.serialized_span_id,
197+
FORMAT.FLAGS_KEY: "1",
198+
}
199+
)
200+
143201
self.assertEqual(new_carrier[FORMAT.SAMPLED_KEY], "1")
144202

145203
def test_64bit_trace_id(self):
146204
"""64 bit trace ids should be padded to 128 bit trace ids."""
147205
trace_id_64_bit = self.serialized_trace_id[:16]
148-
carrier = {
149-
FORMAT.TRACE_ID_KEY: trace_id_64_bit,
150-
FORMAT.SPAN_ID_KEY: self.serialized_span_id,
151-
FORMAT.FLAGS_KEY: "1",
152-
}
153-
span_context = FORMAT.extract(get_as_list, carrier)
154-
new_carrier = {}
155-
FORMAT.inject(span_context, dict.__setitem__, new_carrier)
206+
207+
child, parent, new_carrier = self.get_child_parent_new_carrier(
208+
{
209+
FORMAT.TRACE_ID_KEY: trace_id_64_bit,
210+
FORMAT.SPAN_ID_KEY: self.serialized_span_id,
211+
FORMAT.FLAGS_KEY: "1",
212+
}
213+
)
214+
156215
self.assertEqual(
157216
new_carrier[FORMAT.TRACE_ID_KEY], "0" * 16 + trace_id_64_bit
158217
)

Diff for: tox.ini

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ commands_pre =
8888
mypyinstalled: pip install file://{toxinidir}/opentelemetry-api/
8989

9090
commands =
91-
test: pytest
91+
test: pytest {posargs}
9292
coverage: {toxinidir}/scripts/coverage.sh
9393

9494
mypy: mypy --namespace-packages opentelemetry-api/src/opentelemetry/

0 commit comments

Comments
 (0)