Skip to content

Commit 352de67

Browse files
committed
Add support for B3 parentspanid
Fixes #236
1 parent d081d6d commit 352de67

File tree

9 files changed

+208
-109
lines changed

9 files changed

+208
-109
lines changed

Diff for: docs/conf.py

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
sys.path[:0] = [
1717
os.path.abspath("../opentelemetry-api/src/"),
18+
os.path.abspath("../opentelemetry-sdk/src/"),
1819
os.path.abspath("../ext/opentelemetry-ext-opentracing-shim/src/"),
1920
]
2021

Diff for: ext/opentelemetry-ext-opentracing-shim/src/opentelemetry/ext/opentracing_shim/__init__.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
import opentelemetry.trace as trace_api
8787
from opentelemetry import propagators
8888
from opentelemetry.ext.opentracing_shim import util
89+
from opentelemetry.sdk.trace import Span
8990

9091
logger = logging.getLogger(__name__)
9192

@@ -323,7 +324,8 @@ class ScopeShim(opentracing.Scope):
323324
store the context manager as an attribute so that it can later be
324325
closed by calling its ``__exit__()`` method. Defaults to `None`.
325326
326-
TODO: Is :class:`contextlib.AbstractContextManager` the correct type for *span_cm*?
327+
TODO: Is :class:`contextlib.AbstractContextManager` the correct type for
328+
*span_cm*?
327329
"""
328330

329331
def __init__(self, manager, span, span_cm=None):
@@ -660,12 +662,14 @@ def inject(self, span_context, format, carrier):
660662
# uses the configured propagators in opentelemetry.propagators.
661663
# TODO: Support Format.BINARY once it is supported in
662664
# opentelemetry-python.
665+
663666
if format not in self._supported_formats:
664667
raise opentracing.UnsupportedFormatException
665668

666669
propagator = propagators.get_global_httptextformat()
670+
667671
propagator.inject(
668-
span_context.unwrap(), type(carrier).__setitem__, carrier
672+
Span("", span_context.unwrap()), type(carrier).__setitem__, carrier
669673
)
670674

671675
def extract(self, format, carrier):

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

+10-5
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# limitations under the License.
1414

1515
import time
16-
import unittest
16+
from unittest import TestCase
1717

1818
import opentracing
1919

@@ -24,7 +24,7 @@
2424
from opentelemetry.sdk.trace import Tracer
2525

2626

27-
class TestShim(unittest.TestCase):
27+
class TestShim(TestCase):
2828
# pylint: disable=too-many-public-methods
2929

3030
def setUp(self):
@@ -485,6 +485,7 @@ def test_inject_text_map(self):
485485

486486
# Verify Format.TEXT_MAP
487487
text_map = {}
488+
488489
self.shim.inject(context, opentracing.Format.TEXT_MAP, text_map)
489490
self.assertEqual(text_map[MockHTTPTextFormat.TRACE_ID_KEY], str(1220))
490491
self.assertEqual(text_map[MockHTTPTextFormat.SPAN_ID_KEY], str(7478))
@@ -550,6 +551,10 @@ def extract(cls, get_from_carrier, carrier):
550551
)
551552

552553
@classmethod
553-
def inject(cls, context, set_in_carrier, carrier):
554-
set_in_carrier(carrier, cls.TRACE_ID_KEY, str(context.trace_id))
555-
set_in_carrier(carrier, cls.SPAN_ID_KEY, str(context.span_id))
554+
def inject(cls, span, set_in_carrier, carrier):
555+
set_in_carrier(
556+
carrier, cls.TRACE_ID_KEY, str(span.get_context().trace_id)
557+
)
558+
set_in_carrier(
559+
carrier, cls.SPAN_ID_KEY, str(span.get_context().span_id)
560+
)

Diff for: opentelemetry-api/src/opentelemetry/context/propagation/httptextformat.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import abc
1616
import typing
1717

18-
from opentelemetry.trace import SpanContext
18+
from opentelemetry.trace import Span, SpanContext
1919

2020
_T = typing.TypeVar("_T")
2121

@@ -95,7 +95,7 @@ def extract(
9595

9696
@abc.abstractmethod
9797
def inject(
98-
self, context: SpanContext, set_in_carrier: Setter[_T], carrier: _T
98+
self, span: Span, set_in_carrier: Setter[_T], carrier: _T
9999
) -> None:
100100
"""Inject values from a SpanContext into a carrier.
101101

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,13 @@ def extract(
105105
@classmethod
106106
def inject(
107107
cls,
108-
context: trace.SpanContext,
108+
span: trace.Span,
109109
set_in_carrier: httptextformat.Setter[_T],
110110
carrier: _T,
111111
) -> None:
112+
113+
context = span.get_context()
114+
112115
if context == trace.INVALID_SPAN_CONTEXT:
113116
return
114117
traceparent_string = "00-{:032x}-{:016x}-{:02x}".format(

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def inject(
6464
should know how to set header values on the carrier.
6565
"""
6666
get_global_httptextformat().inject(
67-
tracer.get_current_span().get_context(), set_in_carrier, carrier
67+
tracer.get_current_span(), set_in_carrier, carrier
6868
)
6969

7070

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

+37-22
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import typing
1616
import unittest
17+
from unittest.mock import Mock
1718

1819
from opentelemetry import trace
1920
from opentelemetry.context.propagation import tracecontexthttptextformat
@@ -38,7 +39,8 @@ def test_no_traceparent_header(self):
3839
3940
RFC 4.2.2:
4041
41-
If no traceparent header is received, the vendor creates a new trace-id and parent-id that represents the current request.
42+
If no traceparent header is received, the vendor creates a new
43+
trace-id and parent-id that represents the current request.
4244
"""
4345
output = {} # type:typing.Dict[str, typing.List[str]]
4446
span_context = FORMAT.extract(get_as_list, output)
@@ -66,8 +68,10 @@ def test_headers_with_tracestate(self):
6668
span_context.trace_state, {"foo": "1", "bar": "2", "baz": "3"}
6769
)
6870

71+
mock_span = Mock()
72+
mock_span.configure_mock(**{"get_context.return_value": span_context})
6973
output = {} # type:typing.Dict[str, str]
70-
FORMAT.inject(span_context, dict.__setitem__, output)
74+
FORMAT.inject(mock_span, dict.__setitem__, output)
7175
self.assertEqual(output["traceparent"], traceparent_value)
7276
for pair in ["foo=1", "bar=2", "baz=3"]:
7377
self.assertIn(pair, output["tracestate"])
@@ -81,13 +85,16 @@ def test_invalid_trace_id(self):
8185
8286
RFC 3.2.2.3
8387
84-
If the trace-id value is invalid (for example if it contains non-allowed characters or all
85-
zeros), vendors MUST ignore the traceparent.
88+
If the trace-id value is invalid (for example if it contains
89+
non-allowed characters or all zeros), vendors MUST ignore the
90+
traceparent.
8691
8792
RFC 3.3
8893
89-
If the vendor failed to parse traceparent, it MUST NOT attempt to parse tracestate.
90-
Note that the opposite is not true: failure to parse tracestate MUST NOT affect the parsing of traceparent.
94+
If the vendor failed to parse traceparent, it MUST NOT attempt to
95+
parse tracestate.
96+
Note that the opposite is not true: failure to parse tracestate MUST
97+
NOT affect the parsing of traceparent.
9198
"""
9299
span_context = FORMAT.extract(
93100
get_as_list,
@@ -101,19 +108,22 @@ def test_invalid_trace_id(self):
101108
self.assertEqual(span_context, trace.INVALID_SPAN_CONTEXT)
102109

103110
def test_invalid_parent_id(self):
104-
"""If the parent id is invalid, we must ignore the full traceparent header.
111+
"""If the parent id is invalid, we must ignore the full traceparent
112+
header.
105113
106114
Also ignore any tracestate.
107115
108116
RFC 3.2.2.3
109117
110-
Vendors MUST ignore the traceparent when the parent-id is invalid (for example,
111-
if it contains non-lowercase hex characters).
118+
Vendors MUST ignore the traceparent when the parent-id is invalid (for
119+
example, if it contains non-lowercase hex characters).
112120
113121
RFC 3.3
114122
115-
If the vendor failed to parse traceparent, it MUST NOT attempt to parse tracestate.
116-
Note that the opposite is not true: failure to parse tracestate MUST NOT affect the parsing of traceparent.
123+
If the vendor failed to parse traceparent, it MUST NOT attempt to parse
124+
tracestate.
125+
Note that the opposite is not true: failure to parse tracestate MUST
126+
NOT affect the parsing of traceparent.
117127
"""
118128
span_context = FORMAT.extract(
119129
get_as_list,
@@ -131,15 +141,19 @@ def test_no_send_empty_tracestate(self):
131141
132142
RFC 3.3.1.1
133143
134-
Empty and whitespace-only list members are allowed. Vendors MUST accept empty
135-
tracestate headers but SHOULD avoid sending them.
144+
Empty and whitespace-only list members are allowed. Vendors MUST accept
145+
empty tracestate headers but SHOULD avoid sending them.
136146
"""
137147
output = {} # type:typing.Dict[str, str]
138-
FORMAT.inject(
139-
trace.SpanContext(self.TRACE_ID, self.SPAN_ID),
140-
dict.__setitem__,
141-
output,
148+
mock_span = Mock()
149+
mock_span.configure_mock(
150+
**{
151+
"get_context.return_value": trace.SpanContext(
152+
self.TRACE_ID, self.SPAN_ID
153+
)
154+
}
142155
)
156+
FORMAT.inject(mock_span, dict.__setitem__, output)
143157
self.assertTrue("traceparent" in output)
144158
self.assertFalse("tracestate" in output)
145159

@@ -155,22 +169,23 @@ def test_format_not_supported(self):
155169
get_as_list,
156170
{
157171
"traceparent": [
158-
"00-12345678901234567890123456789012-1234567890123456-00-residue"
172+
"00-12345678901234567890123456789012-"
173+
"1234567890123456-00-residue"
159174
],
160175
"tracestate": ["foo=1,bar=2,foo=3"],
161176
},
162177
)
163178
self.assertEqual(span_context, trace.INVALID_SPAN_CONTEXT)
164179

165180
def test_propagate_invalid_context(self):
166-
"""Do not propagate invalid trace context.
167-
"""
181+
"""Do not propagate invalid trace context."""
168182
output = {} # type:typing.Dict[str, str]
169-
FORMAT.inject(trace.INVALID_SPAN_CONTEXT, dict.__setitem__, output)
183+
FORMAT.inject(trace.Span(), dict.__setitem__, output)
170184
self.assertFalse("traceparent" in output)
171185

172186
def test_tracestate_empty_header(self):
173-
"""Test tracestate with an additional empty header (should be ignored)"""
187+
"""Test tracestate with an additional empty header (should be ignored)
188+
"""
174189
span_context = FORMAT.extract(
175190
get_as_list,
176191
{

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

0 commit comments

Comments
 (0)