From 4a2c6432c224a8df4afc2bba81826d28858ca9b2 Mon Sep 17 00:00:00 2001 From: Johannes Liebermann Date: Wed, 23 Oct 2019 15:51:15 +0200 Subject: [PATCH 01/11] Add documentation for OpenTracing shim --- docs/conf.py | 3 + docs/index.rst | 1 + docs/opentelemetry.ext.opentracing_shim.rst | 7 + .../ext/opentracing_shim/__init__.py | 423 +++++++++++++++++- tox.ini | 1 + 5 files changed, 422 insertions(+), 13 deletions(-) create mode 100644 docs/opentelemetry.ext.opentracing_shim.rst diff --git a/docs/conf.py b/docs/conf.py index 8b8c11d47b9..5ad04038f02 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -14,6 +14,7 @@ import sys sys.path.insert(0, os.path.abspath("../opentelemetry-api/src/")) +sys.path.insert(1, os.path.abspath("../ext/opentelemetry-ext-opentracing-shim/src/")) # -- Project information ----------------------------------------------------- @@ -83,3 +84,5 @@ # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = [] + +autodoc_mock_imports = ["opentracing"] diff --git a/docs/index.rst b/docs/index.rst index f4c8e6e3366..b98ef55316b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -19,6 +19,7 @@ abstract types for OpenTelemetry implementations. opentelemetry.metrics opentelemetry.trace opentelemetry.util.loader + opentelemetry.ext.opentracing_shim Indices and tables diff --git a/docs/opentelemetry.ext.opentracing_shim.rst b/docs/opentelemetry.ext.opentracing_shim.rst new file mode 100644 index 00000000000..c5502db3cd4 --- /dev/null +++ b/docs/opentelemetry.ext.opentracing_shim.rst @@ -0,0 +1,7 @@ +opentelemetry.ext.opentracing_shim package +========================================== + +Module contents +--------------- + +.. automodule:: opentelemetry.ext.opentracing_shim 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 eefb85466ae..43f397c2a46 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 @@ -12,6 +12,72 @@ # See the License for the specific language governing permissions and # limitations under the License. +""" +The OpenTelemetry OpenTracing shim is a library which allows an easy migration +from OpenTracing to OpenTelemetry. + +The shim consists of a set of classes which implement the OpenTracing Python +API while using OpenTelemetry constructs behind the scenes. Its purpose is to +allow applications which are already instrumented using OpenTracing to start +using OpenTelemetry with a minimal effort, without having to rewrite large +portions of the codebase. + +To use the shim, a :class:`TracerShim` instance is created and then used as if +it were an "ordinary" OpenTracing :class:`opentracing.Tracer`, as in the +following example:: + + import time + + from opentelemetry import trace + from opentelemetry.sdk.trace import Tracer + from opentelemetry.ext.opentracing_shim import create_tracer + + # Tell OpenTelemetry which Tracer implementation to use. + trace.set_preferred_tracer_implementation(lambda T: Tracer()) + # Create an OpenTelemetry Tracer. + otel_tracer = trace.tracer() + # Create an OpenTracing shim. + shim = create_tracer(otel_tracer) + + with shim.start_active_span("ProcessHTTPRequest"): + print("Processing HTTP request") + # Sleeping to mock real work. + time.sleep(0.1) + with shim.start_active_span("GetDataFromDB"): + print("Getting data from DB") + # Sleeping to mock real work. + time.sleep(0.2) + +Note: + While the OpenTracing Python API represents time values as the number of + **seconds** since the epoch expressed as :obj:`float` values, the + OpenTelemetry Python API represents time values as the number of + **nanoseconds** since the epoch expressed as :obj:`int` values. This fact + requires the OpenTracing shim to convert time values back and forth between + the two representations, which involves floating point arithmetic. + + Due to the way computers represent floating point values in hardware, + representation of decimal floating point values in binary-based hardware is + imprecise by definition. + + The above results in **slight imprecisions** in time values passed to the + shim via the OpenTracing API when comparing the value passed to the shim + and the value stored in the OpenTelemetry :class:`opentelemetry.trace.Span` + object behind the scenes. **This is not a bug in this library or in + Python**. Rather, this is a generic problem which stems from the fact that + not every decimal floating point number can be correctly represented in + binary, and therefore affects other libraries and programming languages as + well. More information about this problem can be found in the + `Floating Point Arithmetic\\: Issues and Limitations`_ section of the + Python documentation. + + While testing this library, the aforementioned imprecisions were observed + to be of *less than a microsecond*. + +.. _Floating Point Arithmetic\\: Issues and Limitations: + https://docs.python.org/3/tutorial/floatingpoint.html +""" + import logging import opentracing @@ -25,20 +91,56 @@ def create_tracer(otel_tracer): + """Creates a :class:`TracerShim` object from the provided OpenTelemetry + :class:`opentelemetry.trace.Tracer`. + + The returned :class:`TracerShim` is an implementation of + :class:`opentracing.Tracer` using OpenTelemetry under the hood. + + Args: + otel_tracer: A :class:`opentelemetry.trace.Tracer` to be used for + constructing the :class:`TracerShim`. This tracer will be used + to perform the actual tracing when user code is instrumented using + the OpenTracing API. + + Returns: + The created :class:`TracerShim`. + """ + return TracerShim(otel_tracer) class SpanContextShim(opentracing.SpanContext): + """Implements :class:`opentracing.SpanContext` by wrapping a + :class:`opentelemetry.trace.SpanContext` object. + + Args: + otel_context: A :class:`opentelemetry.trace.SpanContext` to be used for + constructing the :class:`SpanContextShim`. + """ + def __init__(self, otel_context): self._otel_context = otel_context def unwrap(self): - """Returns the wrapped OpenTelemetry `SpanContext` object.""" + """Returns the wrapped :class:`opentelemetry.trace.SpanContext` + object. + + Returns: + The :class:`opentelemetry.trace.SpanContext` object wrapped by this + :class:`SpanContextShim`. + """ return self._otel_context @property def baggage(self): + """Implements the `baggage` property from the base class. + + Warning: + Not implemented yet. + """ + logger.warning( "Using unimplemented property baggage on class %s.", self.__class__.__name__, @@ -47,30 +149,102 @@ def baggage(self): class SpanShim(opentracing.Span): + """Implements :class:`opentracing.Span` by wrapping a + :class:`opentelemetry.trace.Span` object. + + Args: + tracer: The :class:`opentracing.Tracer` that created this `SpanShim`. + context: A :class:`SpanContextShim` which contains the context for this + :class:`SpanShim`. + span: A :class:`opentelemetry.trace.Span` to wrap. + """ + def __init__(self, tracer, context, span): super().__init__(tracer, context) self._otel_span = span def unwrap(self): - """Returns the wrapped OpenTelemetry `Span` object.""" + """Returns the wrapped :class:`opentelemetry.trace.Span` object. + + Returns: + The :class:`opentelemetry.trace.Span` object wrapped by this + :class:`SpanShim`. + """ return self._otel_span def set_operation_name(self, operation_name): + """Implements the `set_operation_name()` method from the base class. + + Updates the name of the wrapped OpenTelemetry span. + + Returns: + Returns this :class:`SpanShim` instance to allow call chaining. + """ + self._otel_span.update_name(operation_name) return self def finish(self, finish_time=None): + """Implements the `finish()` method from the base class. + + Ends the OpenTelemetry span wrapped by this :class:`SpanShim`. + + If `finish_time` is provided, the time value is converted to the + OpenTelemetry time format (number of nanoseconds since the epoch, + expressed as an integer) and passed on to the OpenTelemetry tracer when + ending the OpenTelemetry span. If `finish_time` isn't provided, it is + up to the OpenTelemetry tracer implementation to generate a timestamp + when ending the span. + + Args: + finish_time(:obj:`float`, optional): An explicit finish time + expressed as the number of seconds since the epoch as returned + by :func:`time.time()`. Defaults to `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): + """Implements the `set_tag()` method from the base class. + + Sets an OpenTelemetry `Attribute` on the wrapped OpenTelemetry span. + + Args: + key(:obj:`str`): A tag key. + value: A tag value. Can be one of :obj:`str`, :obj:`bool`, + :obj:`int`, :obj:`float` + + Returns: + Returns this :class:`SpanShim` instance to allow call chaining. + """ + self._otel_span.set_attribute(key, value) return self def log_kv(self, key_values, timestamp=None): + """Implements the `log_kv()` method from the base class. + + Logs an :class:`opentelemetry.trace.Event` for the wrapped + OpenTelemetry span. + + Note: + The OpenTracing API defines the values of `key_values` to be of any + type. However, the OpenTelemetry API requires that the values be + one of :obj:`str`, :obj:`bool`, :obj:`float`. Therefore, only these + types are supported as values. + + Args: + key_values(:obj:`dict`): A dict with :obj:`str` keys and values of + type :obj:`str`, :obj:`bool` or :obj:`float`. + + Returns: + Returns this :class:`SpanShim` instance to allow call chaining. + """ + if timestamp is not None: event_timestamp = util.time_seconds_to_ns(timestamp) else: @@ -89,6 +263,12 @@ def log_event(self, event, payload=None): super().log_event(event, payload=payload) def set_baggage_item(self, key, value): + """Implements the `set_baggage_item()` method from the base class. + + Warning: + Not implemented yet. + """ + logger.warning( "Calling unimplemented method set_baggage_item() on class %s", self.__class__.__name__, @@ -96,6 +276,12 @@ def set_baggage_item(self, key, value): # TODO: Implement. def get_baggage_item(self, key): + """Implements the `get_baggage_item()` method from the base class. + + Warning: + Not implemented yet. + """ + logger.warning( "Calling unimplemented method get_baggage_item() on class %s", self.__class__.__name__, @@ -105,18 +291,37 @@ def get_baggage_item(self, key): class ScopeShim(opentracing.Scope): """A `ScopeShim` wraps the OpenTelemetry functionality related to span - activation/deactivation while using OpenTracing `Scope` objects for - presentation. + activation/deactivation while using OpenTracing :class:`opentracing.Scope` + objects for presentation. + + Unlike other classes in this package, the `ScopeShim` class doesn't wrap an + OpenTelemetry class because OpenTelemetry doesn't have the notion of + "scope" (though it *does* have similar functionality). There are two ways to construct a `ScopeShim` object: using the default - initializer and using the `from_context_manager()` class method. + initializer and using the :meth:`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`. + because in some cases we need to create the object from an OpenTelemetry + `Span` context manager (as returned by + :meth:`opentelemetry.trace.Tracer.use_span`), 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`. The former is used mainly when the instrumentation code + retrieves the currently-active span using `ScopeManagerShim.active`. The + latter is mainly used when the instrumentation code activates a span using + :meth:`ScopeManagerShim.activate`. + + Args: + manager: The :class:`ScopeManagerShim` that created this + :class:`ScopeShim`. + span: The :class:`SpanShim` this :class:`ScopeShim` controls. + span_cm(optional): A Python context manager which yields an + OpenTelemetry `Span` from its `__enter__()` method. Used by + :meth:`from_context_manager` to store the context manager as an + attribute so that it can later be closed by calling its + `__exit__()` method. Defaults to `None`. """ def __init__(self, manager, span, span_cm=None): @@ -127,8 +332,27 @@ def __init__(self, manager, span, span_cm=None): # 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()`). + """Constructs a :class:`ScopeShim` from an OpenTelemetry `Span` context + manager. + + The method extracts a `Span` object from the context manager by calling + the context manager's `__enter__()` method. This causes the span to + start in the OpenTelemetry tracer. + + Example usage:: + + span = otel_tracer.start_span("TestSpan") + span_cm = otel_tracer.use_span(span) + scope_shim = ScopeShim.from_context_manager( + scope_manager_shim, + span_cm=span_cm, + ) + + Args: + manager: The :class:`ScopeManagerShim` that created this + :class:`ScopeShim`. + span_cm: An OpenTelemetry `Span` context manager as returned by + :meth:`opentelemetry.trace.Tracer.use_span`. """ otel_span = span_cm.__enter__() @@ -137,6 +361,26 @@ def from_context_manager(cls, manager, span_cm): return cls(manager, span, span_cm) def close(self): + """Implements the `close()` method from :class:`opentracing.Scope`. + + Closes the `ScopeShim`. If the `ScopeShim` was created from a context + manager, calling this method sets the active span in the + OpenTelemetry tracer back to the span which was active before this + `ScopeShim` was created. In addition, if the span represented by this + `ScopeShim` was activated with the `finish_on_close` argument set to + `True`, calling this method will end the span. + + Warning: + In the current state of the implementation it is possible to create + a `ScopeShim` directly from a `SpanShim`, that is - without using + :meth:`from_context_manager()`. For that reason we need to be able + to end the span represented by the `ScopeShim` in this case, too. + Please note that closing a `ScopeShim` created this way (for + example as returned by :meth:`ScopeManagerShim.active`) **always + ends the associated span**, regardless of the value passed in + `finish_on_close` when activating the span. + """ + 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 @@ -149,6 +393,21 @@ def close(self): class ScopeManagerShim(opentracing.ScopeManager): + """Implements :class:`opentracing.ScopeManager` by setting and getting the + active `Span` in the OpenTelemetry tracer. + + This class keeps a reference to a :class:`TracerShim` as an attribute. This + reference is used to communicate with the OpenTelemetry tracer. It is + necessary to have a reference to the :class:`TracerShim` rather than the + :class:`opentelemetry.trace.Tracer` wrapped by it because when constructing + a :class:`SpanShim` we need to pass a reference to a + :class:`opentracing.Tracer`. + + Args: + tracer: A :class:`TracerShim` to use for setting and getting active + span state. + """ + 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 @@ -157,6 +416,21 @@ def __init__(self, tracer): self._tracer = tracer def activate(self, span, finish_on_close): + """Implements the `activate()` method from the base class. + + Activates a :class:`SpanShim` and returns a :class:`ScopeShim` which + represents the active span. + + Args: + span: A :class:`SpanShim` to be activated. + finish_on_close(:obj:`bool`): Determines whether the OpenTelemetry + span should be ended when the returned :class:`ScopeShim` is + closed. + + Returns: + A :class:`ScopeShim` representing the activated span. + """ + span_cm = self._tracer.unwrap().use_span( span.unwrap(), end_on_exit=finish_on_close ) @@ -164,6 +438,23 @@ def activate(self, span, finish_on_close): @property def active(self): + """Implements the `active` property from the base class. + + Returns a :class:`ScopeShim` object representing the currently-active + span in the OpenTelemetry tracer. + + Returns: + A :class:`ScopeShim` representing the active span in the + OpenTelemetry tracer, or `None` if no span is currently active. + + Warning: + Calling :meth:`ScopeShim.close` on the :class:`ScopeShim` returned + by this property **always ends the corresponding span**, regardless + of the `finish_on_close` value used when activating the span. This + is a limitation of the current implementation of the OpenTracing + shim and is likely to be handled in future versions. + """ + span = self._tracer.unwrap().get_current_span() if span is None: return None @@ -179,10 +470,41 @@ def active(self): @property def tracer(self): + """Returns the :class:`TracerShim` reference used by this + :class:`ScopeManagerShim` for setting and getting the active span from + the OpenTelemetry tracer. + + Returns: + The :class:`TracerShim` used for setting and getting the active + span. + + Warning: + This property is *not* a part of the OpenTracing API. It used + internally by the current implementation of the OpenTracing shim + and will likely be removed in future versions. + """ + return self._tracer class TracerShim(opentracing.Tracer): + """Implements :class:`opentracing.Tracer` by wrapping a + :class:`opentelemetry.trace.Tracer` object. + + This wrapper class allows using an OpenTelemetry tracer as if it were an + OpenTracing tracer. It exposes the same methods as an "ordinary" + OpenTracing tracer, and uses OpenTelemetry transparently for performing the + actual tracing. + + This class depends on the *OpenTelemetry API*. Therefore, any + implementation of a :class:`opentelemetry.trace.Tracer` should work with + this class. + + Args: + tracer: A :class:`opentelemetry.trace.Tracer` to use for tracing. This + tracer will be invoked by the shim to create actual spans. + """ + def __init__(self, tracer): super().__init__(scope_manager=ScopeManagerShim(self)) self._otel_tracer = tracer @@ -192,7 +514,12 @@ def __init__(self, tracer): ) def unwrap(self): - """Returns the wrapped OpenTelemetry `Tracer` object.""" + """Returns the :class:`opentelemetry.trace.Tracer` object that is + wrapped by this :class:`TracerShim` and used for actual tracing. + + Returns: + The :class:`opentelemetry.trace.Tracer` used for actual tracing. + """ return self._otel_tracer @@ -206,6 +533,40 @@ def start_active_span( ignore_active_span=False, finish_on_close=True, ): + """Implements the `start_active_span()` method from the base class. + + Starts and activates a span. In terms of functionality, this method + behaves exactly like the same method on a "regular" OpenTracing tracer. + See :meth:`opentracing.Tracer.start_active_span` for more details. + + Args: + operation_name(:obj:`str`): Name of the operation represented by + the new span from the perspective of the current service. + child_of(optional): A :class:`SpanShim` or :class:`SpanContextShim` + representing the parent in a "child of" reference. If + specified, the `references` parameter must be omitted. Defaults + to `None`. + references(optional): A list of :class:`opentracing.Reference` + objects that identify one or more parents of type + :class:`SpanContextShim`. Defaults to `None`. + tags(optional): A dictionary of tags. The keys must be of type + :obj:`str`. The values may be one of :obj:`str`, :obj:`bool`, + :obj:`int`, :obj:`float`. Defaults to `None`. + start_time(:obj:`float`, optional): An explicit start time + expressed as the number of seconds since the epoch as returned + by :meth:`time.time()`. Defaults to `None`. + ignore_active_span(optional): Ignore the currently-active span in + the OpenTelemetry tracer and make the created span the root + span of a new trace. Defaults to `False`. + finish_on_close(optional): Determines whether the created span + should end automatically when closing the returned + :class:`ScopeShim`. Defaults to `True`. + + Returns: + A :class:`ScopeShim` that is already activated by the + :class:`ScopeManagerShim`. + """ + span = self.start_span( operation_name=operation_name, child_of=child_of, @@ -225,6 +586,36 @@ def start_span( start_time=None, ignore_active_span=False, ): + """Implements the `start_span()` method from the base class. + + Starts a span. In terms of functionality, this method behaves exactly + like the same method on a "regular" OpenTracing tracer. See + :meth:`opentracing.Tracer.start_span` for more details. + + Args: + operation_name(:obj:`str`): Name of the operation represented by + the new span from the perspective of the current service. + child_of(optional): A :class:`SpanShim` or :class:`SpanContextShim` + representing the parent in a "child of" reference. If + specified, the `references` parameter must be omitted. Defaults + to `None`. + references(optional): A list of :class:`opentracing.Reference` + objects that identify one or more parents of type + :class:`SpanContextShim`. Defaults to `None`. + tags(optional): A dictionary of tags. The keys must be of type + :obj:`str`. The values may be one of :obj:`str`, :obj:`bool`, + :obj:`int`, :obj:`float`. Defaults to `None`. + start_time(:obj:`float`, optional): An explicit start time + expressed as the number of seconds since the epoch as returned + by :meth:`time.time()`. Defaults to `None`. + ignore_active_span(optional): Ignore the currently-active span in + the OpenTelemetry tracer and make the created span the root + span of a new trace. Defaults to `False`. + + Returns: + An already-started :class:`SpanShim` instance. + """ + # 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 @@ -258,6 +649,9 @@ def start_span( return SpanShim(self, context, span) def inject(self, span_context, format, carrier): + """Implements the `inject` method from the base class.""" + + # TODO: Finish documentation. # pylint: disable=redefined-builtin # This implementation does not perform the injecting by itself but # uses the configured propagators in opentelemetry.propagators. @@ -272,6 +666,9 @@ def inject(self, span_context, format, carrier): ) def extract(self, format, carrier): + """Implements the `extract` method from the base class.""" + + # TODO: Finish documentation. # pylint: disable=redefined-builtin # This implementation does not perform the extracing by itself but # uses the configured propagators in opentelemetry.propagators. diff --git a/tox.ini b/tox.ini index 8f0d02d1ccb..65f1c66ed42 100644 --- a/tox.ini +++ b/tox.ini @@ -146,6 +146,7 @@ deps = sphinx~=2.1 sphinx-rtd-theme~=0.4 sphinx-autodoc-typehints~=1.10.2 + opentracing~=2.2.0 changedir = docs From 9c8845e08991f79714338d3b6311f481e812541a Mon Sep 17 00:00:00 2001 From: Johannes Liebermann Date: Thu, 24 Oct 2019 22:00:04 +0200 Subject: [PATCH 02/11] Fix types and references in start_span() --- .../ext/opentracing_shim/__init__.py | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) 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 43f397c2a46..c9d69d2e7a1 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 @@ -554,7 +554,7 @@ def start_active_span( :obj:`int`, :obj:`float`. Defaults to `None`. start_time(:obj:`float`, optional): An explicit start time expressed as the number of seconds since the epoch as returned - by :meth:`time.time()`. Defaults to `None`. + by :func:`time.time()`. Defaults to `None`. ignore_active_span(optional): Ignore the currently-active span in the OpenTelemetry tracer and make the created span the root span of a new trace. Defaults to `False`. @@ -595,22 +595,23 @@ def start_span( Args: operation_name(:obj:`str`): Name of the operation represented by the new span from the perspective of the current service. - child_of(optional): A :class:`SpanShim` or :class:`SpanContextShim` - representing the parent in a "child of" reference. If - specified, the `references` parameter must be omitted. Defaults - to `None`. - references(optional): A list of :class:`opentracing.Reference` - objects that identify one or more parents of type - :class:`SpanContextShim`. Defaults to `None`. - tags(optional): A dictionary of tags. The keys must be of type - :obj:`str`. The values may be one of :obj:`str`, :obj:`bool`, - :obj:`int`, :obj:`float`. Defaults to `None`. + child_of(:class:`SpanShim` or :class:`SpanContextShim`, optional): + A :class:`SpanShim` or :class:`SpanContextShim` representing + the parent in a "child of" reference. If specified, the + *references* parameter must be omitted. Defaults to `None`. + references(:obj:`list`, optional): A list of + :class:`opentracing.Reference` objects that identify one or + more parents of type :class:`SpanContextShim`. Defaults to + `None`. + tags(:obj:`dict`, optional): A dictionary of tags. The keys must be + of type :obj:`str`. The values may be one of :obj:`str`, + :obj:`bool`, :obj:`int`, :obj:`float`. Defaults to `None`. start_time(:obj:`float`, optional): An explicit start time expressed as the number of seconds since the epoch as returned - by :meth:`time.time()`. Defaults to `None`. - ignore_active_span(optional): Ignore the currently-active span in - the OpenTelemetry tracer and make the created span the root - span of a new trace. Defaults to `False`. + by :func:`time.time()`. Defaults to `None`. + ignore_active_span(:obj:`bool`, optional): Ignore the + currently-active span in the OpenTelemetry tracer and make the + created span the root span of a new trace. Defaults to `False`. Returns: An already-started :class:`SpanShim` instance. From 807b3f9fadecf81c3137db236e982580ecbf1675 Mon Sep 17 00:00:00 2001 From: Johannes Liebermann Date: Fri, 25 Oct 2019 15:46:33 +0200 Subject: [PATCH 03/11] Add intersphinx config for resolving opentracing --- docs/conf.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 5ad04038f02..a1a7f97265a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -48,7 +48,10 @@ "sphinx.ext.githubpages", ] -intersphinx_mapping = {"python": ("https://docs.python.org/3/", None)} +intersphinx_mapping = { + "python": ("https://docs.python.org/3/", None), + "opentracing": ("https://opentracing-python.readthedocs.io/en/latest/", None), +} # http://www.sphinx-doc.org/en/master/config.html#confval-nitpicky # Sphinx will warn about all references where the target cannot be found. @@ -84,5 +87,3 @@ # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = [] - -autodoc_mock_imports = ["opentracing"] From 4df5237e793390ef91e8f0ac0b7f1acb57f0dcd7 Mon Sep 17 00:00:00 2001 From: Johannes Liebermann Date: Fri, 25 Oct 2019 17:52:53 +0200 Subject: [PATCH 04/11] Fix types and references in start_active_span() --- .../ext/opentracing_shim/__init__.py | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) 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 c9d69d2e7a1..82de07367dd 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 @@ -542,24 +542,25 @@ def start_active_span( Args: operation_name(:obj:`str`): Name of the operation represented by the new span from the perspective of the current service. - child_of(optional): A :class:`SpanShim` or :class:`SpanContextShim` - representing the parent in a "child of" reference. If - specified, the `references` parameter must be omitted. Defaults - to `None`. - references(optional): A list of :class:`opentracing.Reference` - objects that identify one or more parents of type - :class:`SpanContextShim`. Defaults to `None`. - tags(optional): A dictionary of tags. The keys must be of type - :obj:`str`. The values may be one of :obj:`str`, :obj:`bool`, - :obj:`int`, :obj:`float`. Defaults to `None`. + child_of(:class:`SpanShim` or :class:`SpanContextShim`, optional): + A :class:`SpanShim` or :class:`SpanContextShim` representing + the parent in a "child of" reference. If specified, the + *references* parameter must be omitted. Defaults to `None`. + references(:obj:`list`, optional): A list of + :class:`opentracing.Reference` objects that identify one or + more parents of type :class:`SpanContextShim`. Defaults to + `None`. + tags(:obj:`dict`, optional): A dictionary of tags. The keys must be + of type :obj:`str`. The values may be one of :obj:`str`, + :obj:`bool`, :obj:`int`, :obj:`float`. Defaults to `None`. start_time(:obj:`float`, optional): An explicit start time expressed as the number of seconds since the epoch as returned by :func:`time.time()`. Defaults to `None`. - ignore_active_span(optional): Ignore the currently-active span in - the OpenTelemetry tracer and make the created span the root - span of a new trace. Defaults to `False`. - finish_on_close(optional): Determines whether the created span - should end automatically when closing the returned + ignore_active_span(:obj:`bool`, optional): Ignore the + currently-active span in the OpenTelemetry tracer and make the + created span the root span of a new trace. Defaults to `False`. + finish_on_close(:obj:`bool`, optional): Determines whether the + created span should end automatically when closing the returned :class:`ScopeShim`. Defaults to `True`. Returns: From cb03ff26db71360b607ef8b34c7bbcefd7105933 Mon Sep 17 00:00:00 2001 From: Johannes Liebermann Date: Fri, 25 Oct 2019 17:56:42 +0200 Subject: [PATCH 05/11] Don't show inheritance in docs Sphinx is trying to resolve references such as `opentracing.Span` when `show-inheritance` is enabled. However, the actual modules live in places like `opentracing.span.Span` and are *imported* in `__init__.py` at the root of the `opentracing` package. This makes Sphinx try to link to `opentracing.span.Span` instead of `opentracing.Span` which fails because the OpenTracing docs don't contain documentation for `opentracing.span.Span` since most likely aren't expected to use that path in imports. This is a workaround until we find a better solution. --- docs/opentelemetry.ext.opentracing_shim.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/opentelemetry.ext.opentracing_shim.rst b/docs/opentelemetry.ext.opentracing_shim.rst index c5502db3cd4..921d6c290b3 100644 --- a/docs/opentelemetry.ext.opentracing_shim.rst +++ b/docs/opentelemetry.ext.opentracing_shim.rst @@ -5,3 +5,4 @@ Module contents --------------- .. automodule:: opentelemetry.ext.opentracing_shim + :no-show-inheritance: From 6e9bf62d81c7c6cbd89d3cad41727fae6a5943d6 Mon Sep 17 00:00:00 2001 From: Johannes Liebermann Date: Fri, 25 Oct 2019 18:27:21 +0200 Subject: [PATCH 06/11] Fix types and references --- .../ext/opentracing_shim/__init__.py | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) 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 82de07367dd..e46fce56423 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 @@ -190,10 +190,10 @@ def finish(self, finish_time=None): Ends the OpenTelemetry span wrapped by this :class:`SpanShim`. - If `finish_time` is provided, the time value is converted to the + If *finish_time* is provided, the time value is converted to the OpenTelemetry time format (number of nanoseconds since the epoch, expressed as an integer) and passed on to the OpenTelemetry tracer when - ending the OpenTelemetry span. If `finish_time` isn't provided, it is + ending the OpenTelemetry span. If *finish_time* isn't provided, it is up to the OpenTelemetry tracer implementation to generate a timestamp when ending the span. @@ -211,7 +211,7 @@ def finish(self, finish_time=None): def set_tag(self, key, value): """Implements the `set_tag()` method from the base class. - Sets an OpenTelemetry `Attribute` on the wrapped OpenTelemetry span. + Sets an OpenTelemetry attribute on the wrapped OpenTelemetry span. Args: key(:obj:`str`): A tag key. @@ -232,7 +232,7 @@ def log_kv(self, key_values, timestamp=None): OpenTelemetry span. Note: - The OpenTracing API defines the values of `key_values` to be of any + The OpenTracing API defines the values of *key_values* to be of any type. However, the OpenTelemetry API requires that the values be one of :obj:`str`, :obj:`bool`, :obj:`float`. Therefore, only these types are supported as values. @@ -305,7 +305,7 @@ class ScopeShim(opentracing.Scope): because in some cases we need to create the object from an OpenTelemetry `Span` context manager (as returned by :meth:`opentelemetry.trace.Tracer.use_span`), in which case our only way of - retrieving a `Span` object is by calling the `__enter__()` method on the + 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`. The former is used mainly when the instrumentation code @@ -317,11 +317,11 @@ class ScopeShim(opentracing.Scope): manager: The :class:`ScopeManagerShim` that created this :class:`ScopeShim`. span: The :class:`SpanShim` this :class:`ScopeShim` controls. - span_cm(optional): A Python context manager which yields an - OpenTelemetry `Span` from its `__enter__()` method. Used by - :meth:`from_context_manager` to store the context manager as an - attribute so that it can later be closed by calling its - `__exit__()` method. Defaults to `None`. + span_cm(:class:`contextlib.AbstractContextManager`, optional): A + Python context manager which yields an OpenTelemetry `Span` from + its ``__enter__()`` method. Used by :meth:`from_context_manager` to + store the context manager as an attribute so that it can later be + closed by calling its ``__exit__()`` method. Defaults to `None`. """ def __init__(self, manager, span, span_cm=None): @@ -336,7 +336,7 @@ def from_context_manager(cls, manager, span_cm): manager. The method extracts a `Span` object from the context manager by calling - the context manager's `__enter__()` method. This causes the span to + the context manager's ``__enter__()`` method. This causes the span to start in the OpenTelemetry tracer. Example usage:: @@ -367,7 +367,7 @@ def close(self): manager, calling this method sets the active span in the OpenTelemetry tracer back to the span which was active before this `ScopeShim` was created. In addition, if the span represented by this - `ScopeShim` was activated with the `finish_on_close` argument set to + `ScopeShim` was activated with the *finish_on_close* argument set to `True`, calling this method will end the span. Warning: @@ -378,7 +378,7 @@ def close(self): Please note that closing a `ScopeShim` created this way (for example as returned by :meth:`ScopeManagerShim.active`) **always ends the associated span**, regardless of the value passed in - `finish_on_close` when activating the span. + *finish_on_close* when activating the span. """ if self._span_cm is not None: @@ -450,7 +450,7 @@ def active(self): Warning: Calling :meth:`ScopeShim.close` on the :class:`ScopeShim` returned by this property **always ends the corresponding span**, regardless - of the `finish_on_close` value used when activating the span. This + of the *finish_on_close* value used when activating the span. This is a limitation of the current implementation of the OpenTracing shim and is likely to be handled in future versions. """ From 1286698b79b89c90412517bea10d6c4a03a86ede Mon Sep 17 00:00:00 2001 From: Johannes Liebermann Date: Fri, 25 Oct 2019 18:36:32 +0200 Subject: [PATCH 07/11] Run Black on conf.py --- docs/conf.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index a1a7f97265a..8865f81b352 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -14,7 +14,9 @@ import sys sys.path.insert(0, os.path.abspath("../opentelemetry-api/src/")) -sys.path.insert(1, os.path.abspath("../ext/opentelemetry-ext-opentracing-shim/src/")) +sys.path.insert( + 1, os.path.abspath("../ext/opentelemetry-ext-opentracing-shim/src/") +) # -- Project information ----------------------------------------------------- @@ -50,7 +52,10 @@ intersphinx_mapping = { "python": ("https://docs.python.org/3/", None), - "opentracing": ("https://opentracing-python.readthedocs.io/en/latest/", None), + "opentracing": ( + "https://opentracing-python.readthedocs.io/en/latest/", + None, + ), } # http://www.sphinx-doc.org/en/master/config.html#confval-nitpicky From 2f1b72df746902dd8f61cf7c4605a1fb7780437a Mon Sep 17 00:00:00 2001 From: Johannes Liebermann Date: Fri, 25 Oct 2019 19:33:09 +0200 Subject: [PATCH 08/11] Add TODO --- .../src/opentelemetry/ext/opentracing_shim/__init__.py | 2 ++ 1 file changed, 2 insertions(+) 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 e46fce56423..7a02b0dbd76 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 @@ -322,6 +322,8 @@ class ScopeShim(opentracing.Scope): its ``__enter__()`` method. Used by :meth:`from_context_manager` to store the context manager as an attribute so that it can later be closed by calling its ``__exit__()`` method. Defaults to `None`. + + TODO: Is :class:`contextlib.AbstractContextManager` the correct type for *span_cm*? """ def __init__(self, manager, span, span_cm=None): From f97fd3bd2990cf40da595fac7da5b9aeadd05174 Mon Sep 17 00:00:00 2001 From: Johannes Liebermann Date: Mon, 18 Nov 2019 17:30:50 +0100 Subject: [PATCH 09/11] Add Deprecated to docs dependencies --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index 65f1c66ed42..b858bb8c815 100644 --- a/tox.ini +++ b/tox.ini @@ -147,6 +147,7 @@ deps = sphinx-rtd-theme~=0.4 sphinx-autodoc-typehints~=1.10.2 opentracing~=2.2.0 + Deprecated>=1.2.6 changedir = docs From f85b2f4f1e9e51012c94a6b3d324c1a8b3a070a5 Mon Sep 17 00:00:00 2001 From: Johannes Liebermann Date: Mon, 18 Nov 2019 17:53:51 +0100 Subject: [PATCH 10/11] Properly escape backticks to avoid wrong linking --- .../ext/opentracing_shim/__init__.py | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) 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 7a02b0dbd76..e8b3ba46761 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 @@ -135,7 +135,7 @@ def unwrap(self): @property def baggage(self): - """Implements the `baggage` property from the base class. + """Implements the ``baggage`` property from the base class. Warning: Not implemented yet. @@ -174,7 +174,7 @@ def unwrap(self): return self._otel_span def set_operation_name(self, operation_name): - """Implements the `set_operation_name()` method from the base class. + """Implements the ``set_operation_name()`` method from the base class. Updates the name of the wrapped OpenTelemetry span. @@ -186,7 +186,7 @@ def set_operation_name(self, operation_name): return self def finish(self, finish_time=None): - """Implements the `finish()` method from the base class. + """Implements the ``finish()`` method from the base class. Ends the OpenTelemetry span wrapped by this :class:`SpanShim`. @@ -209,7 +209,7 @@ def finish(self, finish_time=None): self._otel_span.end(end_time=end_time) def set_tag(self, key, value): - """Implements the `set_tag()` method from the base class. + """Implements the ``set_tag()`` method from the base class. Sets an OpenTelemetry attribute on the wrapped OpenTelemetry span. @@ -226,7 +226,7 @@ def set_tag(self, key, value): return self def log_kv(self, key_values, timestamp=None): - """Implements the `log_kv()` method from the base class. + """Implements the ``log_kv()`` method from the base class. Logs an :class:`opentelemetry.trace.Event` for the wrapped OpenTelemetry span. @@ -263,7 +263,7 @@ def log_event(self, event, payload=None): super().log_event(event, payload=payload) def set_baggage_item(self, key, value): - """Implements the `set_baggage_item()` method from the base class. + """Implements the ``set_baggage_item()`` method from the base class. Warning: Not implemented yet. @@ -276,7 +276,7 @@ def set_baggage_item(self, key, value): # TODO: Implement. def get_baggage_item(self, key): - """Implements the `get_baggage_item()` method from the base class. + """Implements the ``get_baggage_item()`` method from the base class. Warning: Not implemented yet. @@ -411,14 +411,14 @@ class ScopeManagerShim(opentracing.ScopeManager): """ def __init__(self, tracer): - # The only thing the `__init__()` method on the base class does is + # 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): - """Implements the `activate()` method from the base class. + """Implements the ``activate()`` method from the base class. Activates a :class:`SpanShim` and returns a :class:`ScopeShim` which represents the active span. @@ -440,7 +440,7 @@ def activate(self, span, finish_on_close): @property def active(self): - """Implements the `active` property from the base class. + """Implements the ``active`` property from the base class. Returns a :class:`ScopeShim` object representing the currently-active span in the OpenTelemetry tracer. @@ -535,7 +535,7 @@ def start_active_span( ignore_active_span=False, finish_on_close=True, ): - """Implements the `start_active_span()` method from the base class. + """Implements the ``start_active_span()`` method from the base class. Starts and activates a span. In terms of functionality, this method behaves exactly like the same method on a "regular" OpenTracing tracer. @@ -589,7 +589,7 @@ def start_span( start_time=None, ignore_active_span=False, ): - """Implements the `start_span()` method from the base class. + """Implements the ``start_span()`` method from the base class. Starts a span. In terms of functionality, this method behaves exactly like the same method on a "regular" OpenTracing tracer. See @@ -653,7 +653,7 @@ def start_span( return SpanShim(self, context, span) def inject(self, span_context, format, carrier): - """Implements the `inject` method from the base class.""" + """Implements the ``inject`` method from the base class.""" # TODO: Finish documentation. # pylint: disable=redefined-builtin @@ -670,7 +670,7 @@ def inject(self, span_context, format, carrier): ) def extract(self, format, carrier): - """Implements the `extract` method from the base class.""" + """Implements the ``extract`` method from the base class.""" # TODO: Finish documentation. # pylint: disable=redefined-builtin From 347cbdf548c849937f386e4bce00930ab72a09c2 Mon Sep 17 00:00:00 2001 From: Johannes Liebermann Date: Mon, 18 Nov 2019 19:15:54 +0100 Subject: [PATCH 11/11] Make Sphinx path configuration compact --- docs/conf.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 8865f81b352..538472cf052 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -13,10 +13,10 @@ import os import sys -sys.path.insert(0, os.path.abspath("../opentelemetry-api/src/")) -sys.path.insert( - 1, os.path.abspath("../ext/opentelemetry-ext-opentracing-shim/src/") -) +sys.path[:0] = [ + os.path.abspath("../opentelemetry-api/src/"), + os.path.abspath("../ext/opentelemetry-ext-opentracing-shim/src/"), +] # -- Project information -----------------------------------------------------