A collection of Python instrumentation tools for the OpenTracing API
Clone or download
Jamim and yurishkuro Improve testing (#70)
As a software engineer, I'd like to be able to conveniently run tests
in my local environment, so I want to add a tox config file.

These changes:

 - Add tox.ini

 - Update setup.cfg

 - Enable branch coverage measurement

 - Update the README

 - Unify tracer fixture and move it to conftest.py

 - Fix some deprecation warnings

 - Clean up imports
Latest commit 576d3ee Nov 28, 2018


PyPI version Build Status Coverage Status


A collection of instrumentation tools to enable tracing with OpenTracing API.


Make sure you are running recent enough versions of pip and setuptools, e.g. before installing your project requirements execute this:

pip install --upgrade "setuptools>=29" "pip>=9"

The module name is opentracing_instrumentation.

What's inside

Supported client frameworks

The following libraries are instrumented for tracing in this module:

  • urllib2
  • requests
  • SQLAlchemy
  • MySQLdb
  • psycopg2
  • Tornado HTTP client
  • redis

Server instrumentation

For inbound requests a helper function before_request is provided for creating middleware for frameworks like Flask and uWSGI.

Manual instrumentation

Finally, a @traced_function decorator is provided for manual instrumentation.

In-process Context Propagation

request_context implements thread-local based context propagation facility.


This library provides two types of instrumentation, explicit instrumentation for server endpoints, and implicit instrumentation for client call sites.

Server endpoints are instrumented by creating a middleware class that:

  1. initializes the specific tracer implementation
  2. wraps incoming request handlers into a method that reads the incoming tracing info from the request and creates a new tracing Span

Client call sites are instrumented implicitly by executing a set of available client_hooks that monkey-patch some API points in several common libraries like SQLAlchemy, urllib2, Tornado Async HTTP Client. The initialization of those hooks is usually also done from the middleware class's __init__ method.

There is a client-server example using this library with Flask instrumentation from opentracing-contrib: https://github.com/opentracing-contrib/python-flask/tree/master/example.

Here's an example of a middleware for Clay framework:

from opentracing_instrumentation.request_context import RequestContextManager
from opentracing_instrumentation.http_server import before_request
from opentracing_instrumentation.http_server import WSGIRequestWrapper
from opentracing_instrumentation.client_hooks import install_all_patches

class TracerMiddleware(object):

    def __init__(self, app, wsgi_app):
        self.wsgi_app = wsgi_app
        self.service_name = app.name

        CONFIG.app_name = self.service_name

        self.wsgi_app = create_wsgi_middleware(wsgi_app)

    def __call__(self, environ, start_response):
        return self.wsgi_app(environ, start_response)

    def init_tracer(self):
        # code specific to your tracer implementation

def create_wsgi_middleware(other_wsgi, tracer=None):
    Create a wrapper middleware for another WSGI response handler.
    If tracer is not passed in, 'opentracing.tracer' is used.

    def wsgi_tracing_middleware(environ, start_response):
        # TODO find out if the route can be retrieved from somewhere

        request = WSGIRequestWrapper.from_wsgi_environ(environ)
        span = before_request(request=request, tracer=tracer)

        # Wrapper around the real start_response object to log
        # additional information to opentracing Span
        def start_response_wrapper(status, response_headers, exc_info=None):
            if exc_info is not None:
                span.set_tag('error', str(exc_info))

            return start_response(status, response_headers)

        with RequestContextManager(span=span):
            return other_wsgi(environ, start_response_wrapper)

    return wsgi_tracing_middleware

And here's an example for middleware in Tornado-based app:

class TracerMiddleware(object):

    def __init__(self):
        # perform initialization similar to above, including installing
        # the client_hooks
    def __call__(self, request, handler, next_mw):
        request_wrapper = http_server.TornadoRequestWrapper(request=request)
        span = http_server.before_request(request=request_wrapper)

        def next_middleware_with_span():
            yield next_mw()

        yield run_coroutine_with_span(span=span,


def run_coroutine_with_span(span, func, *args, **kwargs):
    """Wrap the execution of a Tornado coroutine func in a tracing span.

    This makes the span available through the get_current_span() function.

    :param span: The tracing span to expose.
    :param func: Co-routine to execute in the scope of tracing span.
    :param args: Positional args to func, if any.
    :param kwargs: Keyword args to func, if any.
    def mgr():
        return RequestContextManager(span)

    with tornado.stack_context.StackContext(mgr):
        return func(*args, **kwargs)


To prepare a development environment please execute the following commands.

virtualenv env
source env/bin/activate
make bootstrap
make test

You can use tox to run tests as well.