Skip to content

Commit

Permalink
ext/opentracing-shim: implement inject and extract (open-telemetry#256)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mauriciovasquezbernal authored and clsung committed Nov 14, 2019
1 parent 850a7c2 commit ceb7456
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 18 deletions.
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ If you are looking for someone to help you find a starting point and be a resour
Gitter and find a buddy!

1. Join [Gitter.im](https://gitter.im) and join our [chat room](https://gitter.im/open-telemetry/opentelemetry-python).
2. Post in the room with an introduction to yourself, what area you are interested in (checks issues marked "Help Wanted"),
2. Post in the room with an introduction to yourself, what area you are interested in (check issues marked "Help Wanted"),
and say you are looking for a buddy. We will match you with someone who has experience in that area.

Your OpenTelemetry buddy is your resource to talk to directly on all aspects of contributing to OpenTelemetry: providing
Expand All @@ -37,7 +37,7 @@ You can run:
under multiple Python versions
- `tox -e docs` to regenerate the API docs
- `tox -e test-api` and `tox -e test-sdk` to run the API and SDK unit tests
- `tox -e py37-test-api` to e.g. run the the API unit tests under a specific
- `tox -e py37-test-api` to e.g. run the API unit tests under a specific
Python version
- `tox -e lint` to run lint checks on all code

Expand Down Expand Up @@ -115,7 +115,7 @@ It's especially valuable to read through the [library guidelines](https://github
OpenTelemetry is an evolving specification, one where the desires and
use cases are clear, but the method to satisfy those uses cases are not.

As such, Contributions should provide functionality and behavior that
As such, contributions should provide functionality and behavior that
conforms to the specification, but the interface and structure is flexible.

It is preferable to have contributions follow the idioms of the language
Expand Down
2 changes: 2 additions & 0 deletions ext/opentelemetry-ext-opentracing-shim/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- Implement extract and inject support for HTTP_HEADERS and TEXT_MAP formats.

## 0.2a0

Released 2019-10-29
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import opentracing
from deprecated import deprecated

from opentelemetry import propagators
from opentelemetry.ext.opentracing_shim import util

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -184,6 +185,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."""
Expand Down Expand Up @@ -249,16 +254,32 @@ 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__,
# 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(), type(carrier).__setitem__, carrier
)
# TODO: Implement.

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)
102 changes: 101 additions & 1 deletion ext/opentelemetry-ext-opentracing-shim/tests/test_shim.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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)
Expand Down Expand Up @@ -453,3 +465,91 @@ def test_span_on_error(self):
self.assertEqual(
scope.span.unwrap().events[0].attributes["error.kind"], Exception
)

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)

# 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))

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.inject(context, opentracing.Format.BINARY, bytearray())

def test_extract_http_headers(self):
"""Test `extract()` method for Format.HTTP_HEADERS."""

carrier = {
MockHTTPTextFormat.TRACE_ID_KEY: 1220,
MockHTTPTextFormat.SPAN_ID_KEY: 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):
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))
6 changes: 3 additions & 3 deletions ext/opentelemetry-ext-wsgi/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ OpenTelemetry WSGI Middleware

|pypi|

.. |pypi| image:: https://badge.fury.io/py/opentelemetry-wsgi.svg
:target: https://pypi.org/project/opentelemetry-opentracing-wsgi/
.. |pypi| image:: https://badge.fury.io/py/opentelemetry-ext-wsgi.svg
:target: https://pypi.org/project/opentelemetry-ext-wsgi/


This library provides a WSGI middleware that can be used on any WSGI framework
Expand All @@ -15,7 +15,7 @@ Installation

::

pip install opentelemetry-opentracing-wsgi
pip install opentelemetry-ext-wsgi


Usage (Flask)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
_T = typing.TypeVar("_T")

# Keys and values are strings of up to 256 printable US-ASCII characters.
# Implementations should conform to the the `W3C Trace Context - Tracestate`_
# Implementations should conform to the `W3C Trace Context - Tracestate`_
# spec, which describes additional restrictions on valid field values.
#
# .. _W3C Trace Context - Tracestate:
Expand Down
2 changes: 1 addition & 1 deletion opentelemetry-api/src/opentelemetry/trace/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ class TraceState(typing.Dict[str, str]):
"""A list of key-value pairs representing vendor-specific trace info.
Keys and values are strings of up to 256 printable US-ASCII characters.
Implementations should conform to the the `W3C Trace Context - Tracestate`_
Implementations should conform to the `W3C Trace Context - Tracestate`_
spec, which describes additional restrictions on valid field values.
.. _W3C Trace Context - Tracestate:
Expand Down

0 comments on commit ceb7456

Please sign in to comment.