From 244a4f913b72bfb301ab7e23fe5671380333ad89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mauricio=20V=C3=A1squez?= Date: Wed, 30 Oct 2019 09:43:08 -0500 Subject: [PATCH 1/4] ext/opentracing-shim: implement inject and extract 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. --- .../CHANGELOG.md | 2 + .../ext/opentracing_shim/__init__.py | 39 +++++--- .../tests/test_shim.py | 88 ++++++++++++++++++- 3 files changed, 118 insertions(+), 11 deletions(-) diff --git a/ext/opentelemetry-ext-opentracing-shim/CHANGELOG.md b/ext/opentelemetry-ext-opentracing-shim/CHANGELOG.md index 73350ef7ad4..34017bb9114 100644 --- a/ext/opentelemetry-ext-opentracing-shim/CHANGELOG.md +++ b/ext/opentelemetry-ext-opentracing-shim/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +- Implement extract and inject support for HTTP_HEADERS and TEXT_MAP formats. + ## 0.2a.0 Released 2019-10-29 diff --git a/ext/opentelemetry-ext-opentracing-shim/src/opentelemetry/ext/opentracing_shim/__init__.py b/ext/opentelemetry-ext-opentracing-shim/src/opentelemetry/ext/opentracing_shim/__init__.py index 1a6479fc58e..5d3e1e4c5d7 100644 --- a/ext/opentelemetry-ext-opentracing-shim/src/opentelemetry/ext/opentracing_shim/__init__.py +++ b/ext/opentelemetry-ext-opentracing-shim/src/opentelemetry/ext/opentracing_shim/__init__.py @@ -16,6 +16,7 @@ import opentracing +from opentelemetry import propagators from opentelemetry.ext.opentracing_shim import util logger = logging.getLogger(__name__) @@ -179,6 +180,10 @@ class TracerShim(opentracing.Tracer): def __init__(self, tracer): super().__init__(scope_manager=ScopeManagerShim(self)) self._otel_tracer = tracer + self._supported_formats = ( + opentracing.Format.TEXT_MAP, + opentracing.Format.HTTP_HEADERS, + ) def unwrap(self): """Returns the wrapped OpenTelemetry `Tracer` object.""" @@ -244,16 +249,30 @@ def start_span( def inject(self, span_context, format, carrier): # pylint: disable=redefined-builtin - logger.warning( - "Calling unimplemented method inject() on class %s", - self.__class__.__name__, - ) - # TODO: Implement. + # This implementation does not perform the injecting by itself but + # uses the configured propagators in opentelemetry.propagators. + # TODO: Support Format.BINARY once it is supported in + # opentelemetry-python. + if format not in self._supported_formats: + raise opentracing.UnsupportedFormatException + + propagator = propagators.get_global_httptextformat() + propagator.inject(span_context.unwrap(), dict.__setitem__, carrier) def extract(self, format, carrier): # pylint: disable=redefined-builtin - logger.warning( - "Calling unimplemented method extract() on class %s", - self.__class__.__name__, - ) - # TODO: Implement. + # This implementation does not perform the extracing by itself but + # uses the configured propagators in opentelemetry.propagators. + # TODO: Support Format.BINARY once it is supported in + # opentelemetry-python. + if format not in self._supported_formats: + raise opentracing.UnsupportedFormatException + + def get_as_list(dict_object, key): + value = dict_object.get(key) + return [value] if value is not None else [] + + propagator = propagators.get_global_httptextformat() + otel_context = propagator.extract(get_as_list, carrier) + + return SpanContextShim(otel_context) diff --git a/ext/opentelemetry-ext-opentracing-shim/tests/test_shim.py b/ext/opentelemetry-ext-opentracing-shim/tests/test_shim.py index b6691911ddc..fffe55a0dd8 100644 --- a/ext/opentelemetry-ext-opentracing-shim/tests/test_shim.py +++ b/ext/opentelemetry-ext-opentracing-shim/tests/test_shim.py @@ -18,7 +18,8 @@ import opentracing import opentelemetry.ext.opentracing_shim as opentracingshim -from opentelemetry import trace +from opentelemetry import propagators, trace +from opentelemetry.context.propagation.httptextformat import HTTPTextFormat from opentelemetry.ext.opentracing_shim import util from opentelemetry.sdk.trace import Tracer @@ -40,6 +41,17 @@ def setUpClass(cls): trace.set_preferred_tracer_implementation(lambda T: Tracer()) + # Save current propagator to be restored on teardown. + cls._previous_propagator = propagators.get_global_httptextformat() + + # Set mock propagator for testing. + propagators.set_global_httptextformat(MockHTTPTextFormat) + + @classmethod + def tearDownClass(cls): + # Restore previous propagator. + propagators.set_global_httptextformat(cls._previous_propagator) + def test_shim_type(self): # Verify shim is an OpenTracing tracer. self.assertIsInstance(self.shim, opentracing.Tracer) @@ -431,3 +443,77 @@ def test_span_on_error(self): self.assertEqual( scope.span.unwrap().events[0].attributes["error.kind"], Exception ) + + def test_inject(self): + """Test `inject()` method.""" + + otel_context = trace.SpanContext(trace_id=1220, span_id=7478) + context = opentracingshim.SpanContextShim(otel_context) + + # Verify Format.TEXT_MAP + text_map = {} + self.shim.inject(context, opentracing.Format.TEXT_MAP, text_map) + self.assertEqual(text_map[MockHTTPTextFormat.TRACE_ID_KEY], str(1220)) + self.assertEqual(text_map[MockHTTPTextFormat.SPAN_ID_KEY], str(7478)) + + # Verify Format.HTTP_HEADERS + http_headers = {} + self.shim.inject( + context, opentracing.Format.HTTP_HEADERS, http_headers + ) + self.assertEqual( + http_headers[MockHTTPTextFormat.TRACE_ID_KEY], str(1220) + ) + self.assertEqual( + http_headers[MockHTTPTextFormat.SPAN_ID_KEY], str(7478) + ) + + # Verify exception for non supported binary format. + with self.assertRaises(opentracing.UnsupportedFormatException): + self.shim.extract(opentracing.Format.BINARY, bytearray()) + + def test_extract(self): + """Test `extract()` method.""" + + headers = { + MockHTTPTextFormat.TRACE_ID_KEY: 1220, + MockHTTPTextFormat.SPAN_ID_KEY: 7478, + } + + # Verify Format.TEXT_MAP + ctx_text_map = self.shim.extract(opentracing.Format.TEXT_MAP, headers) + self.assertEqual(ctx_text_map.unwrap().trace_id, 1220) + self.assertEqual(ctx_text_map.unwrap().span_id, 7478) + + # Verify Format.HTTP_HEADERS + ctx_http = self.shim.extract(opentracing.Format.HTTP_HEADERS, headers) + self.assertEqual(ctx_http.unwrap().trace_id, 1220) + self.assertEqual(ctx_http.unwrap().span_id, 7478) + + # Verify exception for non supported binary format. + with self.assertRaises(opentracing.UnsupportedFormatException): + self.shim.extract(opentracing.Format.BINARY, bytearray()) + + +class MockHTTPTextFormat(HTTPTextFormat): + """Mock propagator for testing purposes.""" + + TRACE_ID_KEY = "mock-traceid" + SPAN_ID_KEY = "mock-spanid" + + @classmethod + def extract(cls, get_from_carrier, carrier): + trace_id_list = get_from_carrier(carrier, cls.TRACE_ID_KEY) + span_id_list = get_from_carrier(carrier, cls.SPAN_ID_KEY) + + if not trace_id_list or not span_id_list: + return trace.INVALID_SPAN_CONTEXT + + return trace.SpanContext( + trace_id=int(trace_id_list[0]), span_id=int(span_id_list[0]) + ) + + @classmethod + def inject(cls, context, set_in_carrier, carrier): + set_in_carrier(carrier, cls.TRACE_ID_KEY, str(context.trace_id)) + set_in_carrier(carrier, cls.SPAN_ID_KEY, str(context.span_id)) From 30de89054e47c0fb2819d21c034c1a41076eb633 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mauricio=20V=C3=A1squez?= Date: Thu, 31 Oct 2019 09:13:19 -0500 Subject: [PATCH 2/4] separate tests cases Handle different formats in different test cases. --- .../tests/test_shim.py | 66 +++++++++++-------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/ext/opentelemetry-ext-opentracing-shim/tests/test_shim.py b/ext/opentelemetry-ext-opentracing-shim/tests/test_shim.py index fffe55a0dd8..f77b3f384b4 100644 --- a/ext/opentelemetry-ext-opentracing-shim/tests/test_shim.py +++ b/ext/opentelemetry-ext-opentracing-shim/tests/test_shim.py @@ -444,8 +444,19 @@ def test_span_on_error(self): scope.span.unwrap().events[0].attributes["error.kind"], Exception ) - def test_inject(self): - """Test `inject()` method.""" + def test_inject_http_headers(self): + """Test `inject()` method for Format.HTTP_HEADERS.""" + + otel_context = trace.SpanContext(trace_id=1220, span_id=7478) + context = opentracingshim.SpanContextShim(otel_context) + + headers = {} + self.shim.inject(context, opentracing.Format.HTTP_HEADERS, headers) + self.assertEqual(headers[MockHTTPTextFormat.TRACE_ID_KEY], str(1220)) + self.assertEqual(headers[MockHTTPTextFormat.SPAN_ID_KEY], str(7478)) + + def test_inject_text_map(self): + """Test `inject()` method for Format.TEXT_MAP.""" otel_context = trace.SpanContext(trace_id=1220, span_id=7478) context = opentracingshim.SpanContextShim(otel_context) @@ -456,39 +467,42 @@ def test_inject(self): self.assertEqual(text_map[MockHTTPTextFormat.TRACE_ID_KEY], str(1220)) self.assertEqual(text_map[MockHTTPTextFormat.SPAN_ID_KEY], str(7478)) - # Verify Format.HTTP_HEADERS - http_headers = {} - self.shim.inject( - context, opentracing.Format.HTTP_HEADERS, http_headers - ) - self.assertEqual( - http_headers[MockHTTPTextFormat.TRACE_ID_KEY], str(1220) - ) - self.assertEqual( - http_headers[MockHTTPTextFormat.SPAN_ID_KEY], str(7478) - ) + def test_inject_binary(self): + """Test `inject()` method for Format.BINARY.""" + + otel_context = trace.SpanContext(trace_id=1220, span_id=7478) + context = opentracingshim.SpanContextShim(otel_context) # Verify exception for non supported binary format. with self.assertRaises(opentracing.UnsupportedFormatException): - self.shim.extract(opentracing.Format.BINARY, bytearray()) + self.shim.inject(context, opentracing.Format.BINARY, bytearray()) - def test_extract(self): - """Test `extract()` method.""" + def test_extract_http_headers(self): + """Test `extract()` method for Format.HTTP_HEADERS.""" - headers = { + carrier = { MockHTTPTextFormat.TRACE_ID_KEY: 1220, MockHTTPTextFormat.SPAN_ID_KEY: 7478, } - # Verify Format.TEXT_MAP - ctx_text_map = self.shim.extract(opentracing.Format.TEXT_MAP, headers) - self.assertEqual(ctx_text_map.unwrap().trace_id, 1220) - self.assertEqual(ctx_text_map.unwrap().span_id, 7478) - - # Verify Format.HTTP_HEADERS - ctx_http = self.shim.extract(opentracing.Format.HTTP_HEADERS, headers) - self.assertEqual(ctx_http.unwrap().trace_id, 1220) - self.assertEqual(ctx_http.unwrap().span_id, 7478) + ctx = self.shim.extract(opentracing.Format.HTTP_HEADERS, carrier) + self.assertEqual(ctx.unwrap().trace_id, 1220) + self.assertEqual(ctx.unwrap().span_id, 7478) + + def test_extract_text_map(self): + """Test `extract()` method for Format.TEXT_MAP.""" + + carrier = { + MockHTTPTextFormat.TRACE_ID_KEY: 1220, + MockHTTPTextFormat.SPAN_ID_KEY: 7478, + } + + ctx = self.shim.extract(opentracing.Format.TEXT_MAP, carrier) + self.assertEqual(ctx.unwrap().trace_id, 1220) + self.assertEqual(ctx.unwrap().span_id, 7478) + + def test_extract_binary(self): + """Test `extract()` method for Format.BINARY.""" # Verify exception for non supported binary format. with self.assertRaises(opentracing.UnsupportedFormatException): From 67e45cdd7ae62defad0ad35398b781e257ed1988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mauricio=20V=C3=A1squez?= Date: Fri, 8 Nov 2019 08:26:46 -0500 Subject: [PATCH 3/4] Update ext/opentelemetry-ext-opentracing-shim/src/opentelemetry/ext/opentracing_shim/__init__.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Christian Neumüller --- .../src/opentelemetry/ext/opentracing_shim/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opentelemetry-ext-opentracing-shim/src/opentelemetry/ext/opentracing_shim/__init__.py b/ext/opentelemetry-ext-opentracing-shim/src/opentelemetry/ext/opentracing_shim/__init__.py index 5d3e1e4c5d7..53b1ab30583 100644 --- a/ext/opentelemetry-ext-opentracing-shim/src/opentelemetry/ext/opentracing_shim/__init__.py +++ b/ext/opentelemetry-ext-opentracing-shim/src/opentelemetry/ext/opentracing_shim/__init__.py @@ -257,7 +257,7 @@ def inject(self, span_context, format, carrier): raise opentracing.UnsupportedFormatException propagator = propagators.get_global_httptextformat() - propagator.inject(span_context.unwrap(), dict.__setitem__, carrier) + propagator.inject(span_context.unwrap(), type(carrier).__setitem__, carrier) def extract(self, format, carrier): # pylint: disable=redefined-builtin From c260d2735d8fae12f12699433d0e6d1df3293165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mauricio=20V=C3=A1squez?= Date: Fri, 8 Nov 2019 08:59:09 -0500 Subject: [PATCH 4/4] fix lint --- .../src/opentelemetry/ext/opentracing_shim/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ext/opentelemetry-ext-opentracing-shim/src/opentelemetry/ext/opentracing_shim/__init__.py b/ext/opentelemetry-ext-opentracing-shim/src/opentelemetry/ext/opentracing_shim/__init__.py index 53b1ab30583..e2185b923d0 100644 --- a/ext/opentelemetry-ext-opentracing-shim/src/opentelemetry/ext/opentracing_shim/__init__.py +++ b/ext/opentelemetry-ext-opentracing-shim/src/opentelemetry/ext/opentracing_shim/__init__.py @@ -257,7 +257,9 @@ def inject(self, span_context, format, carrier): raise opentracing.UnsupportedFormatException propagator = propagators.get_global_httptextformat() - propagator.inject(span_context.unwrap(), type(carrier).__setitem__, carrier) + propagator.inject( + span_context.unwrap(), type(carrier).__setitem__, carrier + ) def extract(self, format, carrier): # pylint: disable=redefined-builtin