-
Notifications
You must be signed in to change notification settings - Fork 631
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
OpenTracing Bridge - Initial implementation (#211)
Initial implementation, without baggage support.
- Loading branch information
1 parent
1fd8659
commit e4d8949
Showing
13 changed files
with
941 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
OpenTracing Shim for OpenTelemetry | ||
============================================================================ | ||
|
||
|pypi| | ||
|
||
.. |pypi| image:: https://badge.fury.io/py/opentelemetry-opentracing-shim.svg | ||
:target: https://pypi.org/project/opentelemetry-opentracing-shim/ | ||
|
||
Installation | ||
------------ | ||
|
||
:: | ||
|
||
pip install opentelemetry-opentracing-shim | ||
|
||
References | ||
---------- | ||
|
||
* `OpenTelemetry Project <https://opentelemetry.io/>`_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# Copyright 2019, OpenTelemetry Authors | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
# | ||
[metadata] | ||
name = opentelemetry-opentracing-shim | ||
description = OpenTracing Shim for OpenTelemetry | ||
long_description = file: README.rst | ||
long_description_content_type = text/x-rst | ||
author = OpenTelemetry Authors | ||
author_email = cncf-opentelemetry-contributors@lists.cncf.io | ||
url = https://github.com/open-telemetry/opentelemetry-python/ext/opentelemetry-ext-opentracing-shim | ||
platforms = any | ||
license = Apache-2.0 | ||
classifiers = | ||
Development Status :: 3 - Alpha | ||
Intended Audience :: Developers | ||
License :: OSI Approved :: Apache Software License | ||
Programming Language :: Python | ||
Programming Language :: Python :: 3 | ||
Programming Language :: Python :: 3.4 | ||
Programming Language :: Python :: 3.5 | ||
Programming Language :: Python :: 3.6 | ||
Programming Language :: Python :: 3.7 | ||
|
||
[options] | ||
python_requires = >=3.4 | ||
package_dir= | ||
=src | ||
packages=find_namespace: | ||
install_requires = | ||
opentracing | ||
opentelemetry-api | ||
|
||
[options.packages.find] | ||
where = src |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# Copyright 2019, OpenTelemetry Authors | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
import os | ||
|
||
import setuptools | ||
|
||
BASE_DIR = os.path.dirname(__file__) | ||
VERSION_FILENAME = os.path.join( | ||
BASE_DIR, "src", "opentelemetry", "ext", "opentracing_shim", "version.py" | ||
) | ||
PACKAGE_INFO = {} | ||
with open(VERSION_FILENAME) as f: | ||
exec(f.read(), PACKAGE_INFO) | ||
|
||
setuptools.setup(version=PACKAGE_INFO["__version__"]) |
259 changes: 259 additions & 0 deletions
259
ext/opentelemetry-ext-opentracing-shim/src/opentelemetry/ext/opentracing_shim/__init__.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,259 @@ | ||
# Copyright 2019, OpenTelemetry Authors | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
import logging | ||
|
||
import opentracing | ||
|
||
from opentelemetry.ext.opentracing_shim import util | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
def create_tracer(otel_tracer): | ||
return TracerShim(otel_tracer) | ||
|
||
|
||
class SpanContextShim(opentracing.SpanContext): | ||
def __init__(self, otel_context): | ||
self._otel_context = otel_context | ||
|
||
def unwrap(self): | ||
"""Returns the wrapped OpenTelemetry `SpanContext` object.""" | ||
|
||
return self._otel_context | ||
|
||
@property | ||
def baggage(self): | ||
logger.warning( | ||
"Using unimplemented property baggage on class %s.", | ||
self.__class__.__name__, | ||
) | ||
# TODO: Implement. | ||
|
||
|
||
class SpanShim(opentracing.Span): | ||
def __init__(self, tracer, context, span): | ||
super().__init__(tracer, context) | ||
self._otel_span = span | ||
|
||
def unwrap(self): | ||
"""Returns the wrapped OpenTelemetry `Span` object.""" | ||
|
||
return self._otel_span | ||
|
||
def set_operation_name(self, operation_name): | ||
self._otel_span.update_name(operation_name) | ||
return self | ||
|
||
def finish(self, finish_time=None): | ||
end_time = finish_time | ||
if end_time is not None: | ||
end_time = util.time_seconds_to_ns(finish_time) | ||
self._otel_span.end(end_time=end_time) | ||
|
||
def set_tag(self, key, value): | ||
self._otel_span.set_attribute(key, value) | ||
return self | ||
|
||
def log_kv(self, key_values, timestamp=None): | ||
if timestamp is not None: | ||
event_timestamp = util.time_seconds_to_ns(timestamp) | ||
else: | ||
event_timestamp = None | ||
|
||
event_name = util.event_name_from_kv(key_values) | ||
self._otel_span.add_event(event_name, event_timestamp, key_values) | ||
return self | ||
|
||
def set_baggage_item(self, key, value): | ||
logger.warning( | ||
"Calling unimplemented method set_baggage_item() on class %s", | ||
self.__class__.__name__, | ||
) | ||
# TODO: Implement. | ||
|
||
def get_baggage_item(self, key): | ||
logger.warning( | ||
"Calling unimplemented method get_baggage_item() on class %s", | ||
self.__class__.__name__, | ||
) | ||
# TODO: Implement. | ||
|
||
# TODO: Verify calls to deprecated methods `log_event()` and `log()` on | ||
# base class work properly (it's probably fine because both methods call | ||
# `log_kv()`). | ||
|
||
|
||
class ScopeShim(opentracing.Scope): | ||
"""A `ScopeShim` wraps the OpenTelemetry functionality related to span | ||
activation/deactivation while using OpenTracing `Scope` objects for | ||
presentation. | ||
There are two ways to construct a `ScopeShim` object: using the default | ||
initializer and using the `from_context_manager()` class method. | ||
It is necessary to have both ways for constructing `ScopeShim` objects | ||
because in some cases we need to create the object from a context manager, | ||
in which case our only way of retrieving a `Span` object is by calling the | ||
`__enter__()` method on the context manager, which makes the span active in | ||
the OpenTelemetry tracer; whereas in other cases we need to accept a | ||
`SpanShim` object and wrap it in a `ScopeShim`. | ||
""" | ||
|
||
def __init__(self, manager, span, span_cm=None): | ||
super().__init__(manager, span) | ||
self._span_cm = span_cm | ||
|
||
# TODO: Change type of `manager` argument to `opentracing.ScopeManager`? We | ||
# need to get rid of `manager.tracer` for this. | ||
@classmethod | ||
def from_context_manager(cls, manager, span_cm): | ||
"""Constructs a `ScopeShim` from an OpenTelemetry `Span` context | ||
manager (as returned by `Tracer.use_span()`). | ||
""" | ||
|
||
otel_span = span_cm.__enter__() | ||
span_context = SpanContextShim(otel_span.get_context()) | ||
span = SpanShim(manager.tracer, span_context, otel_span) | ||
return cls(manager, span, span_cm) | ||
|
||
def close(self): | ||
if self._span_cm is not None: | ||
# We don't have error information to pass to `__exit__()` so we | ||
# pass `None` in all arguments. If the OpenTelemetry tracer | ||
# implementation requires this information, the `__exit__()` method | ||
# on `opentracing.Scope` should be overridden and modified to pass | ||
# the relevant values to this `close()` method. | ||
self._span_cm.__exit__(None, None, None) | ||
else: | ||
self._span.unwrap().end() | ||
|
||
|
||
class ScopeManagerShim(opentracing.ScopeManager): | ||
def __init__(self, tracer): | ||
# The only thing the `__init__()` method on the base class does is | ||
# initialize `self._noop_span` and `self._noop_scope` with no-op | ||
# objects. Therefore, it doesn't seem useful to call it. | ||
# pylint: disable=super-init-not-called | ||
self._tracer = tracer | ||
|
||
def activate(self, span, finish_on_close): | ||
span_cm = self._tracer.unwrap().use_span( | ||
span.unwrap(), end_on_exit=finish_on_close | ||
) | ||
return ScopeShim.from_context_manager(self, span_cm=span_cm) | ||
|
||
@property | ||
def active(self): | ||
span = self._tracer.unwrap().get_current_span() | ||
if span is None: | ||
return None | ||
|
||
span_context = SpanContextShim(span.get_context()) | ||
wrapped_span = SpanShim(self._tracer, span_context, span) | ||
return ScopeShim(self, span=wrapped_span) | ||
# TODO: The returned `ScopeShim` instance here always ends the | ||
# corresponding span, regardless of the `finish_on_close` value used | ||
# when activating the span. This is because here we return a *new* | ||
# `ScopeShim` rather than returning a saved instance of `ScopeShim`. | ||
# https://github.com/open-telemetry/opentelemetry-python/pull/211/files#r335398792 | ||
|
||
@property | ||
def tracer(self): | ||
return self._tracer | ||
|
||
|
||
class TracerShim(opentracing.Tracer): | ||
def __init__(self, tracer): | ||
super().__init__(scope_manager=ScopeManagerShim(self)) | ||
self._otel_tracer = tracer | ||
|
||
def unwrap(self): | ||
"""Returns the wrapped OpenTelemetry `Tracer` object.""" | ||
|
||
return self._otel_tracer | ||
|
||
def start_active_span( | ||
self, | ||
operation_name, | ||
child_of=None, | ||
references=None, | ||
tags=None, | ||
start_time=None, | ||
ignore_active_span=False, | ||
finish_on_close=True, | ||
): | ||
span = self.start_span( | ||
operation_name=operation_name, | ||
child_of=child_of, | ||
references=references, | ||
tags=tags, | ||
start_time=start_time, | ||
ignore_active_span=ignore_active_span, | ||
) | ||
return self._scope_manager.activate(span, finish_on_close) | ||
|
||
def start_span( | ||
self, | ||
operation_name=None, | ||
child_of=None, | ||
references=None, | ||
tags=None, | ||
start_time=None, | ||
ignore_active_span=False, | ||
): | ||
# Use active span as parent when no explicit parent is specified. | ||
if not ignore_active_span and not child_of: | ||
child_of = self.active_span | ||
|
||
# Use the specified parent or the active span if possible. Otherwise, | ||
# use a `None` parent, which triggers the creation of a new trace. | ||
parent = child_of.unwrap() if child_of else None | ||
span = self._otel_tracer.create_span(operation_name, parent) | ||
|
||
if references: | ||
for ref in references: | ||
span.add_link(ref.referenced_context.unwrap()) | ||
|
||
if tags: | ||
for key, value in tags.items(): | ||
span.set_attribute(key, value) | ||
|
||
# The OpenTracing API expects time values to be `float` values which | ||
# represent the number of seconds since the epoch. OpenTelemetry | ||
# represents time values as nanoseconds since the epoch. | ||
start_time_ns = start_time | ||
if start_time_ns is not None: | ||
start_time_ns = util.time_seconds_to_ns(start_time) | ||
|
||
span.start(start_time=start_time_ns) | ||
context = SpanContextShim(span.get_context()) | ||
return SpanShim(self, context, 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. | ||
|
||
def extract(self, format, carrier): | ||
# pylint: disable=redefined-builtin | ||
logger.warning( | ||
"Calling unimplemented method extract() on class %s", | ||
self.__class__.__name__, | ||
) | ||
# TODO: Implement. |
Oops, something went wrong.