Skip to content

Commit fcdd9fe

Browse files
mauriciovasquezbernalc24t
authored andcommitted
ext/opentracing-shim: implement inject and extract (#256)
This commit implements inject() and extract() support for TEXT_MAP and HTTP_HEADERS formats by using the configured OpenTelemetry propagators. The support for binary propagators is not completed on opentelemetry-python so this commit does not include for such format.
1 parent 850a7c2 commit fcdd9fe

File tree

3 files changed

+133
-10
lines changed

3 files changed

+133
-10
lines changed

ext/opentelemetry-ext-opentracing-shim/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Unreleased
44

5+
- Implement extract and inject support for HTTP_HEADERS and TEXT_MAP formats.
6+
57
## 0.2a0
68

79
Released 2019-10-29

ext/opentelemetry-ext-opentracing-shim/src/opentelemetry/ext/opentracing_shim/__init__.py

+30-9
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import opentracing
1818
from deprecated import deprecated
1919

20+
from opentelemetry import propagators
2021
from opentelemetry.ext.opentracing_shim import util
2122

2223
logger = logging.getLogger(__name__)
@@ -184,6 +185,10 @@ class TracerShim(opentracing.Tracer):
184185
def __init__(self, tracer):
185186
super().__init__(scope_manager=ScopeManagerShim(self))
186187
self._otel_tracer = tracer
188+
self._supported_formats = (
189+
opentracing.Format.TEXT_MAP,
190+
opentracing.Format.HTTP_HEADERS,
191+
)
187192

188193
def unwrap(self):
189194
"""Returns the wrapped OpenTelemetry `Tracer` object."""
@@ -249,16 +254,32 @@ def start_span(
249254

250255
def inject(self, span_context, format, carrier):
251256
# pylint: disable=redefined-builtin
252-
logger.warning(
253-
"Calling unimplemented method inject() on class %s",
254-
self.__class__.__name__,
257+
# This implementation does not perform the injecting by itself but
258+
# uses the configured propagators in opentelemetry.propagators.
259+
# TODO: Support Format.BINARY once it is supported in
260+
# opentelemetry-python.
261+
if format not in self._supported_formats:
262+
raise opentracing.UnsupportedFormatException
263+
264+
propagator = propagators.get_global_httptextformat()
265+
propagator.inject(
266+
span_context.unwrap(), type(carrier).__setitem__, carrier
255267
)
256-
# TODO: Implement.
257268

258269
def extract(self, format, carrier):
259270
# pylint: disable=redefined-builtin
260-
logger.warning(
261-
"Calling unimplemented method extract() on class %s",
262-
self.__class__.__name__,
263-
)
264-
# TODO: Implement.
271+
# This implementation does not perform the extracing by itself but
272+
# uses the configured propagators in opentelemetry.propagators.
273+
# TODO: Support Format.BINARY once it is supported in
274+
# opentelemetry-python.
275+
if format not in self._supported_formats:
276+
raise opentracing.UnsupportedFormatException
277+
278+
def get_as_list(dict_object, key):
279+
value = dict_object.get(key)
280+
return [value] if value is not None else []
281+
282+
propagator = propagators.get_global_httptextformat()
283+
otel_context = propagator.extract(get_as_list, carrier)
284+
285+
return SpanContextShim(otel_context)

ext/opentelemetry-ext-opentracing-shim/tests/test_shim.py

+101-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
import opentracing
1919

2020
import opentelemetry.ext.opentracing_shim as opentracingshim
21-
from opentelemetry import trace
21+
from opentelemetry import propagators, trace
22+
from opentelemetry.context.propagation.httptextformat import HTTPTextFormat
2223
from opentelemetry.ext.opentracing_shim import util
2324
from opentelemetry.sdk.trace import Tracer
2425

@@ -40,6 +41,17 @@ def setUpClass(cls):
4041

4142
trace.set_preferred_tracer_implementation(lambda T: Tracer())
4243

44+
# Save current propagator to be restored on teardown.
45+
cls._previous_propagator = propagators.get_global_httptextformat()
46+
47+
# Set mock propagator for testing.
48+
propagators.set_global_httptextformat(MockHTTPTextFormat)
49+
50+
@classmethod
51+
def tearDownClass(cls):
52+
# Restore previous propagator.
53+
propagators.set_global_httptextformat(cls._previous_propagator)
54+
4355
def test_shim_type(self):
4456
# Verify shim is an OpenTracing tracer.
4557
self.assertIsInstance(self.shim, opentracing.Tracer)
@@ -453,3 +465,91 @@ def test_span_on_error(self):
453465
self.assertEqual(
454466
scope.span.unwrap().events[0].attributes["error.kind"], Exception
455467
)
468+
469+
def test_inject_http_headers(self):
470+
"""Test `inject()` method for Format.HTTP_HEADERS."""
471+
472+
otel_context = trace.SpanContext(trace_id=1220, span_id=7478)
473+
context = opentracingshim.SpanContextShim(otel_context)
474+
475+
headers = {}
476+
self.shim.inject(context, opentracing.Format.HTTP_HEADERS, headers)
477+
self.assertEqual(headers[MockHTTPTextFormat.TRACE_ID_KEY], str(1220))
478+
self.assertEqual(headers[MockHTTPTextFormat.SPAN_ID_KEY], str(7478))
479+
480+
def test_inject_text_map(self):
481+
"""Test `inject()` method for Format.TEXT_MAP."""
482+
483+
otel_context = trace.SpanContext(trace_id=1220, span_id=7478)
484+
context = opentracingshim.SpanContextShim(otel_context)
485+
486+
# Verify Format.TEXT_MAP
487+
text_map = {}
488+
self.shim.inject(context, opentracing.Format.TEXT_MAP, text_map)
489+
self.assertEqual(text_map[MockHTTPTextFormat.TRACE_ID_KEY], str(1220))
490+
self.assertEqual(text_map[MockHTTPTextFormat.SPAN_ID_KEY], str(7478))
491+
492+
def test_inject_binary(self):
493+
"""Test `inject()` method for Format.BINARY."""
494+
495+
otel_context = trace.SpanContext(trace_id=1220, span_id=7478)
496+
context = opentracingshim.SpanContextShim(otel_context)
497+
498+
# Verify exception for non supported binary format.
499+
with self.assertRaises(opentracing.UnsupportedFormatException):
500+
self.shim.inject(context, opentracing.Format.BINARY, bytearray())
501+
502+
def test_extract_http_headers(self):
503+
"""Test `extract()` method for Format.HTTP_HEADERS."""
504+
505+
carrier = {
506+
MockHTTPTextFormat.TRACE_ID_KEY: 1220,
507+
MockHTTPTextFormat.SPAN_ID_KEY: 7478,
508+
}
509+
510+
ctx = self.shim.extract(opentracing.Format.HTTP_HEADERS, carrier)
511+
self.assertEqual(ctx.unwrap().trace_id, 1220)
512+
self.assertEqual(ctx.unwrap().span_id, 7478)
513+
514+
def test_extract_text_map(self):
515+
"""Test `extract()` method for Format.TEXT_MAP."""
516+
517+
carrier = {
518+
MockHTTPTextFormat.TRACE_ID_KEY: 1220,
519+
MockHTTPTextFormat.SPAN_ID_KEY: 7478,
520+
}
521+
522+
ctx = self.shim.extract(opentracing.Format.TEXT_MAP, carrier)
523+
self.assertEqual(ctx.unwrap().trace_id, 1220)
524+
self.assertEqual(ctx.unwrap().span_id, 7478)
525+
526+
def test_extract_binary(self):
527+
"""Test `extract()` method for Format.BINARY."""
528+
529+
# Verify exception for non supported binary format.
530+
with self.assertRaises(opentracing.UnsupportedFormatException):
531+
self.shim.extract(opentracing.Format.BINARY, bytearray())
532+
533+
534+
class MockHTTPTextFormat(HTTPTextFormat):
535+
"""Mock propagator for testing purposes."""
536+
537+
TRACE_ID_KEY = "mock-traceid"
538+
SPAN_ID_KEY = "mock-spanid"
539+
540+
@classmethod
541+
def extract(cls, get_from_carrier, carrier):
542+
trace_id_list = get_from_carrier(carrier, cls.TRACE_ID_KEY)
543+
span_id_list = get_from_carrier(carrier, cls.SPAN_ID_KEY)
544+
545+
if not trace_id_list or not span_id_list:
546+
return trace.INVALID_SPAN_CONTEXT
547+
548+
return trace.SpanContext(
549+
trace_id=int(trace_id_list[0]), span_id=int(span_id_list[0])
550+
)
551+
552+
@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))

0 commit comments

Comments
 (0)