Skip to content
This repository was archived by the owner on May 23, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ install:
- make bootstrap

script:
- make test lint
- make test testbed lint

10 changes: 8 additions & 2 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
History
=======

2.0.0rc2 (2018-04-09)
---------------------

- Implement ScopeManager for in-process propagation.


1.3.0 (2018-01-14)
------------------

Expand All @@ -21,7 +27,7 @@ History
1.2.1 (2016-09-22)
------------------

- Make Span.log(self, **kwargs) smarter
- Make Span.log(self, \**kwargs) smarter


1.2.0 (2016-09-21)
Expand Down Expand Up @@ -109,7 +115,7 @@ History
------------------

- Change inheritance to match api-go: TraceContextSource extends codecs,
Tracer extends TraceContextSource
Tracer extends TraceContextSource
- Create API harness


Expand Down
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ recursive-include opentracing *
recursive-include example *.py
recursive-include example *.thrift
recursive-include tests *.py
prune testbed
include *
global-exclude *.pyc
graft docs
Expand Down
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pytest := PYTHONDONTWRITEBYTECODE=1 py.test --tb short -rxs \
html_report := --cov-report=html
test_args := --cov-report xml --cov-report term-missing

.PHONY: clean-pyc clean-build docs clean
.PHONY: clean-pyc clean-build docs clean testbed
.DEFAULT_GOAL : help

help:
Expand All @@ -17,6 +17,7 @@ help:
@echo "clean-test - remove test and coverage artifacts"
@echo "lint - check style with flake8"
@echo "test - run tests quickly with the default Python"
@echo "testbed - run testbed scenarios with the default Python"
@echo "coverage - check code coverage quickly with the default Python"
@echo "docs - generate Sphinx HTML documentation, including API docs"
@echo "release - package and upload a release"
Expand Down Expand Up @@ -57,6 +58,9 @@ lint:
test:
$(pytest) $(test_args)

testbed:
PYTHONDONTWRITEBYTECODE=1 python -m testbed

jenkins:
pip install -r requirements.txt
pip install -r requirements-test.txt
Expand Down
101 changes: 89 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 @@ -137,6 +137,78 @@ 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_scope('...')`` automatically finishes the span on ``Scope.close()``
(``start_active_scope('...', finish_on_close=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_scope('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_scope()``
time or specified parent context explicitly:

.. code-block:: python

scope = tracer.start_active_scope('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).

Scope managers
^^^^^^^^^^^^^^

This project includes a set of ``ScopeManager`` implementations under the ``opentracing.scope_managers`` submodule, which can be imported on demand:

.. code-block:: python

from opentracing.scope_managers import ThreadLocalScopeManager

There exist implementations for ``thread-local`` (the default), ``gevent``, ``Tornado`` and ``asyncio``:

.. code-block:: python

from opentracing.scope_managers.gevent import GeventScopeManager # requires gevent
from opentracing.scope_managers.tornado import TornadoScopeManager # requires Tornado
from opentracing.scope_managers.asyncio import AsyncioScopeManager # requires Python 3.4 or newer.

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

Expand All @@ -150,6 +222,11 @@ Tests
make bootstrap
make test

Testbed suite
^^^^^^^^^^^^^

A testbed suite designed to test API changes and experimental features is included under the *testbed* directory. For more information, see the `Testbed README <testbed/README.md>`_.

Instrumentation Tests
---------------------

Expand Down
27 changes: 27 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 Expand Up @@ -39,3 +45,24 @@ Exceptions

.. autoclass:: opentracing.UnsupportedFormatException
:members:

MockTracer
--------------
.. autoclass:: opentracing.mocktracer.MockTracer
:members:

Scope managers
--------------
.. autoclass:: opentracing.scope_managers.ThreadLocalScopeManager
:members:

.. autoclass:: opentracing.scope_managers.gevent.GeventScopeManager
:members:

.. autoclass:: opentracing.scope_managers.tornado.TornadoScopeManager
:members:

.. autofunction:: opentracing.scope_managers.tornado.tracer_stack_context

.. autoclass:: opentracing.scope_managers.asyncio.AsyncioScopeManager
:members:
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
html_static_path = ['_static']
html_static_path = []
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']

intersphinx_mapping = {
Expand Down
2 changes: 2 additions & 0 deletions opentracing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
from __future__ import absolute_import
from .span import Span # noqa
from .span import SpanContext # noqa
from .scope import Scope # noqa
from .scope_manager import ScopeManager # noqa
from .tracer import child_of # noqa
from .tracer import follows_from # noqa
from .tracer import Reference # noqa
Expand Down
Loading