Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OpenTracing Bridge - Initial implementation #211

Merged
merged 103 commits into from
Oct 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
103 commits
Select commit Hold shift + click to select a range
3e495b9
Add OpenTracing shim package skeleton
johananl Sep 9, 2019
18a71c1
Test OT shim
johananl Sep 11, 2019
4745eca
Rename wrapper classes
johananl Sep 11, 2019
16d5126
Add missing stubs
johananl Sep 11, 2019
ebfbcd2
Return an initialized ScopeWrapper
johananl Sep 11, 2019
fea256c
Add TODO
johananl Sep 11, 2019
ad0ac64
Test setting span name
johananl Sep 11, 2019
3830cfc
Run black
johananl Sep 11, 2019
564c1f2
Re-organize imports
johananl Sep 13, 2019
bb72218
Make start_active_span() a context manager
johananl Sep 13, 2019
419c6f4
Verify OTel span ends after OT span ends
johananl Sep 13, 2019
d6f5224
Verify OTel span starts and stops properly
johananl Sep 13, 2019
af35112
Implement set_tag
johananl Sep 15, 2019
2ce0fb8
Implement start_span
johananl Sep 16, 2019
3cf9fa3
Refactor basic shim test
johananl Sep 16, 2019
aa7024a
Verify span parent-child relationship
johananl Sep 16, 2019
4361e48
Ignore wrong pointless-statement Pylint error
johananl Sep 16, 2019
ac128bb
Handle explicit parent
johananl Sep 16, 2019
d60927b
Add TODOs
johananl Sep 16, 2019
8a759cd
Implement SpanContextWrapper
johananl Sep 16, 2019
190b480
Add TODOs
johananl Sep 16, 2019
941d629
Run Black
johananl Sep 19, 2019
ecb3026
Add opentracing to lint dependencies
johananl Sep 19, 2019
db05d6c
Add baggage method stub
johananl Sep 19, 2019
9efa7c5
Implement active_span()
johananl Sep 20, 2019
1fe644d
Include context in SpanWrapper
johananl Sep 20, 2019
43bbac8
Verify active span is correct
johananl Sep 20, 2019
c53ff56
Separate parent-child tests
johananl Sep 20, 2019
5e5e6cf
Run Black
johananl Sep 21, 2019
fe41317
Ignore "format" argument in inject/extract
johananl Sep 21, 2019
1bdf9a7
Disable super-init-not-called warnings
johananl Sep 21, 2019
550b461
Sort imports
johananl Sep 21, 2019
13433b2
Remove TODO
johananl Sep 22, 2019
26908a3
Verify child span's `parent` field is correct
johananl Sep 22, 2019
d1f1afc
Handle explicit parent of type SpanContext
johananl Sep 24, 2019
b16650b
Rename self.ot_tracer to self.shim
johananl Sep 24, 2019
d398c8c
Make active_span return a wrapped span
johananl Sep 24, 2019
f214aa7
Implement tracer property on SpanWrapper
johananl Sep 24, 2019
89ec88c
Implement finish() on SpanWrapper
johananl Sep 24, 2019
cdf42b9
Clean up old comments and docstrings
johananl Sep 24, 2019
ac45a5c
Implement log_kv
johananl Sep 27, 2019
a21f490
Handle event name in log_kv()
johananl Sep 30, 2019
b9c8ae3
Remove unused context manager methods
johananl Sep 30, 2019
5bf6592
Verify context manager behavior of `start_span()`
johananl Sep 30, 2019
5eba267
Remove deprecated logging methods on `SpanWrapper`
johananl Sep 30, 2019
29e75a9
Refactor tests
johananl Oct 1, 2019
1537c52
Implement ScopeManager
johananl Oct 2, 2019
25ceeca
Implement close() on ScopeWrapper
johananl Oct 2, 2019
f1e4cba
Rename ScopeManager to ScopeManagerWrapper
johananl Oct 2, 2019
4bf91c1
Implement start_active_span() using start_span()
johananl Oct 2, 2019
6f9d22c
Wrap context in start_span()
johananl Oct 2, 2019
a3cb8b0
Raise exception on invalid parent type
johananl Oct 2, 2019
b726baf
Use active span as default parent
johananl Oct 2, 2019
74735d5
Add TODO
johananl Oct 2, 2019
cfa9ff8
Add TODOs
johananl Oct 2, 2019
1c68052
Implement references
johananl Oct 2, 2019
af038a2
Add TODO
johananl Oct 2, 2019
e57b5f0
Handle tags on span creation
johananl Oct 2, 2019
0d15ec6
Handle explicit span start time
johananl Oct 2, 2019
efa0f0c
Use time_ns() instead of time()
johananl Oct 2, 2019
f45ef7b
Overall refactor
johananl Oct 4, 2019
a4cce55
Test explicit span finish
johananl Oct 4, 2019
1901cec
Refactor finish_on_close tests
johananl Oct 4, 2019
1f8fd20
Use context manager where possible
johananl Oct 4, 2019
45ed0b9
Disable too-many-public-methods Pylint check
johananl Oct 4, 2019
c32c278
Update README
johananl Oct 4, 2019
92b760f
Add missing dependencies to setup.py
johananl Oct 7, 2019
b6abe12
Handle time conversion between OT and OTel
johananl Oct 7, 2019
fca38ad
Handle explicit end time when ending spans
johananl Oct 7, 2019
e04c073
Return self for call chaining on SpanWrapper
johananl Oct 7, 2019
ca8146f
Remove type hints
johananl Oct 7, 2019
a263f18
Add TODO
johananl Oct 7, 2019
b1f362f
Raise NotImplementedError in unimplemented methods
johananl Oct 7, 2019
fe77380
Use super() instead of an explicit class name
johananl Oct 7, 2019
0252f6c
Clean up OpenTracing inheritance
johananl Oct 7, 2019
442dd5c
Make comments in tests consistent
johananl Oct 7, 2019
8fd39ef
Handle inaccuracies in time conversions
johananl Oct 9, 2019
9453625
Move OpenTracing shim under ext dir
johananl Oct 10, 2019
78183cc
Rename package to opentelemetry-opentracing-shim
johananl Oct 10, 2019
4f88e7c
Call init on super before setting own attributes
johananl Oct 10, 2019
8a7c1a7
Don't override active_span on TracerWrapper
johananl Oct 10, 2019
cbe971b
Refactor handling of invalid parent type
johananl Oct 11, 2019
97f3734
Don't raise on unimplemented methods
johananl Oct 11, 2019
0017eee
Allow passing a timestamp to add_event()
johananl Oct 11, 2019
c644c10
Let the OTel implementation create event timestamps
johananl Oct 11, 2019
55e692a
Refactor scope handling
johananl Oct 14, 2019
9210853
Remove opentracing runtime dependency from tox.ini
johananl Oct 14, 2019
cb41689
Clean up test setup
johananl Oct 14, 2019
04d44e3
Rename package to opentracing_shim
johananl Oct 14, 2019
add02e7
Don't allow using arbitrary scope managers
johananl Oct 14, 2019
c2b03a7
Remove unnecessary comments.
johananl Oct 14, 2019
ecfd2b8
Remove TODO
johananl Oct 14, 2019
3a78b2c
Don't accept scalar values in references argument
johananl Oct 15, 2019
10f72ac
Rename XWrapper to XShim
johananl Oct 15, 2019
aee40a3
Don't call active_span() when we have a parent
johananl Oct 15, 2019
f2e91c8
Append exception info to span
johananl Oct 15, 2019
f093ee1
Refactor ScopeShim
johananl Oct 15, 2019
1f4cc3c
Don't validate parent type
johananl Oct 16, 2019
970ce6a
Verify ScopeShim.close() deactivates span
johananl Oct 16, 2019
fc12138
Wrap span context properly
johananl Oct 17, 2019
fc6f34e
Refactor ScopeShim initializers
johananl Oct 18, 2019
7860f55
Add TODO
johananl Oct 23, 2019
082183a
Rename tracer argument of create_tracer()
johananl Oct 23, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions ext/opentelemetry-ext-opentracing-shim/README.rst
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/>`_
46 changes: 46 additions & 0 deletions ext/opentelemetry-ext-opentracing-shim/setup.cfg
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
26 changes: 26 additions & 0 deletions ext/opentelemetry-ext-opentracing-shim/setup.py
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__"])
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)
johananl marked this conversation as resolved.
Show resolved Hide resolved
# 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):
johananl marked this conversation as resolved.
Show resolved Hide resolved
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)
johananl marked this conversation as resolved.
Show resolved Hide resolved

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.
Loading