Skip to content
This repository has been archived by the owner on May 23, 2023. It is now read-only.

Commit

Permalink
Update documentation prior to releasing a candidate package.
Browse files Browse the repository at this point in the history
  • Loading branch information
carlosalberto committed Feb 12, 2018
1 parent 9991463 commit 4db96b3
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 36 deletions.
79 changes: 67 additions & 12 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,18 @@ The work of instrumentation libraries generally consists of three steps:
Span object in the process. If the request does not contain an active trace,
the service starts a new trace and a new *root* Span.
2. The service needs to store the current Span in some request-local storage,
where it can be retrieved from when a child Span must be created, e.g. in case
of the service making an RPC to another service.
(called ``Span`` *activation*) where it can be retrieved from when a child Span must
be created, e.g. in case of the service making an RPC to another service.
3. When making outbound calls to another service, the current Span must be
retrieved from request-local storage, a child span must be created (e.g., by
using the ``start_child_span()`` helper), and that child span must be embedded
into the outbound request (e.g., using HTTP headers) via OpenTracing's
inject/extract API.

Below are the code examples for steps 1 and 3. Implementation of request-local
storage needed for step 2 is specific to the service and/or frameworks /
instrumentation libraries it is using (TODO: reference to other OSS projects
with examples of instrumentation).
Below are the code examples for the previously mentioned steps. Implementation
of request-local storage needed for step 2 is specific to the service and/or frameworks /
instrumentation libraries it is using, exposed as a ``ScopeManager`` child contained
as ``Tracer.scope_manager``. See details below.

Inbound request
^^^^^^^^^^^^^^^
Expand All @@ -56,12 +56,12 @@ Somewhere in your server's request handler code:
def handle_request(request):
span = before_request(request, opentracing.tracer)
# use span as Context Manager to ensure span.finish() will be called
with span:
# store span in some request-local storage
with RequestContext(span):
# actual business logic
handle_request_for_real(request)
# store span in some request-local storage using Tracer.scope_manager,
# using the returned `Scope` as Context Manager to ensure
# `Span` will be cleared and (in this case) `Span.finish()` be called.
with tracer.scope_manager.activate(span, True) as scope:
# actual business logic
handle_request_for_real(request)
def before_request(request, tracer):
Expand Down Expand Up @@ -141,6 +141,61 @@ Somewhere in your service that's about to make an outgoing call:
return outbound_span
Scope and within-process propagation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

For getting/setting the current active ``Span`` in the used request-local storage,
OpenTracing requires that every ``Tracer`` contains a ``ScopeManager`` that grants
access to the active ``Span`` through a ``Scope``. Any ``Span`` may be transferred to
another task or thread, but not ``Scope``.

.. code-block:: python
# Access to the active span is straightforward.
scope = tracer.scope_manager.active()
if scope is not None:
scope.span.set_tag('...', '...')
The common case starts a ``Scope`` that's automatically registered for intra-process
propagation via ``ScopeManager``.

Note that ``start_active_span('...', True)`` finishes the span on ``Scope.close()``
(``start_active_span('...', False)`` does not finish it, in contrast).

.. code-block:: python
# Manual activation of the Span.
span = tracer.start_span(operation_name='someWork')
with tracer.scope_manager.activate(span, True) as scope:
# Do things.
# Automatic activation of the Span.
# finish_on_close is a required parameter.
with tracer.start_active_span('someWork', finish_on_close=True) as scope:
# Do things.
# Handling done through a try construct:
span = tracer.start_span(operation_name='someWork')
scope = tracer.scope_manager.activate(span, True)
try:
# Do things.
except Exception as e:
scope.set_tag('error', '...')
finally:
scope.finish()
**If there is a Scope, it will act as the parent to any newly started Span** unless
the programmer passes ``ignore_active_span=True`` at ``start_span()``/``start_active_span()``
time or specified parent context explicitly:

.. code-block:: python
scope = tracer.start_active_span('someWork', ignore_active_span=True)
Each service/framework ought to provide a specific ``ScopeManager`` implementation
that relies on their own request-local storage (thread-local storage, or coroutine-based storage
for asynchronous frameworks, for example).

Development
-----------

Expand Down
6 changes: 6 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ Classes
.. autoclass:: opentracing.SpanContext
:members:

.. autoclass:: opentracing.Scope
:members:

.. autoclass:: opentracing.ScopeManager
:members:

.. autoclass:: opentracing.Tracer
:members:

Expand Down
14 changes: 7 additions & 7 deletions opentracing/scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class Scope(object):
and on the path.
"""
def __init__(self, manager, span):
"""Initialize a `Scope` for the given `Span` object
"""Initializes a `Scope` for the given `Span` object.
:param manager: the `ScopeManager` that created this `Scope`
:param span: the `Span` used for this `Scope`
Expand All @@ -41,29 +41,29 @@ def __init__(self, manager, span):

@property
def span(self):
"""Return the `Span` that's been scoped by this `Scope`."""
"""Returns the `Span` wrapped by this `Scope`."""
return self._span

@property
def manager(self):
"""Return the `ScopeManager` that created this `Scope`."""
"""Returns the `ScopeManager` that created this `Scope`."""
return self._manager

def close(self):
"""Mark the end of the active period for the current thread and `Scope`,
updating the `ScopeManager#active` in the process.
"""Marks the end of the active period for this `Scope`,
updating `ScopeManager#active` in the process.
NOTE: Calling `close()` more than once on a single `Scope` instance
leads to undefined behavior.
"""
pass

def __enter__(self):
"""Allow `Scope` to be used inside a Python Context Manager."""
"""Allows `Scope` to be used inside a Python Context Manager."""
return self

def __exit__(self, exc_type, exc_val, exc_tb):
"""Call `close()` when the execution is outside the Python
"""Calls `close()` when the execution is outside the Python
Context Manager.
"""
self.close()
19 changes: 10 additions & 9 deletions opentracing/scope_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@

class ScopeManager(object):
"""The `ScopeManager` interface abstracts both the activation of `Span`
instances (via `ScopeManager#activate(Span)`) and access to an active
`Span` / `Scope` (via `ScopeManager#active`).
instances (via `ScopeManager#activate(span, finish_on_close)`) and
access to an active `Span` / `Scope` (via `ScopeManager#active`).
"""
def __init__(self):
# TODO: `tracer` should not be None, but we don't have a reference;
Expand All @@ -37,20 +37,21 @@ def __init__(self):
self._noop_scope = Scope(self, self._noop_span)

def activate(self, span, finish_on_close):
"""Make a `Span` instance active.
"""Makes a `Span` instance active.
:param span: the `Span` that should become active.
:param finish_on_close: whether span should be automatically
finished when `Scope#close()` is called.
:param span: the `Span` that should become active
:param finish_on_close: whether span should automatically be
finished when `Scope#close()` is called
:return: a `Scope` instance to control the end of the active period for
the `Span`. It is a programming error to neglect to call
`Scope#close()` on the returned instance.
the `Span`. It is a programming error to neglect to call
`Scope#close()` on the returned instance.
"""
return self._noop_scope

@property
def active(self):
"""Return the currently active `Scope` which can be used to access the
"""Returns the currently active `Scope` which can be used to access the
currently active `Scope#span`.
If there is a non-null `Scope`, its wrapped `Span` becomes an implicit
Expand Down
17 changes: 9 additions & 8 deletions opentracing/tracer.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,24 +60,27 @@ def start_active_span(self,
start_time=None,
ignore_active_span=False):
"""Returns a newly started and activated `Scope`.
Returned `Scope` supports with-statement contexts. For example:
with tracer.start_active_span('...') as scope:
The returned `Scope` supports with-statement contexts. For example:
with tracer.start_active_span('...', False) as scope:
scope.span.set_tag('http.method', 'GET')
do_some_work()
# Span is *not* finished automatically outside the `Scope` `with`.
# Span is not finished outside the `Scope` `with`.
It's also possible to finish the `Span` when the `Scope` context
expires:
with tracer.start_active_span('...', True) as scope:
scope.span.set_tag('http.method', 'GET')
do_some_work()
# Span finishes automatically when the Scope is closed as
# Span finishes when the Scope is closed as
# `finish_on_close` is `True`
:param operation_name: name of the operation represented by the new
span from the perspective of the current service.
:param finish_on_close: whether span should automatically be finished
when `Scope#close()` is called.
:param child_of: (optional) a Span or SpanContext instance representing
the parent in a REFERENCE_CHILD_OF Reference. If specified, the
`references` parameter must be omitted.
Expand All @@ -89,10 +92,8 @@ def start_active_span(self,
to avoid extra data copying.
:param start_time: an explicit Span start time as a unix timestamp per
time.time().
:param ignore_active_span: an explicit flag that ignores the current
active `Scope` and creates a root `Span`.
:param finish_on_close: whether span should automatically be finished
when `Scope#close()` is called.
:param ignore_active_span: (optional) an explicit flag that ignores
the current active `Scope` and creates a root `Span`.
:return: a `Scope`, already registered via the `ScopeManager`.
"""
Expand Down

0 comments on commit 4db96b3

Please sign in to comment.