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
.
The following libraries are instrumented for tracing in this module:
urllib2
requests
SQLAlchemy
MySQLdb
- Tornado HTTP client
redis
For inbound requests a helper function before_request
is provided for creating middleware for frameworks like Flask and uWSGI.
Finally, a @traced_function
decorator is provided for manual instrumentation.
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:
- initializes the specific tracer implementation
- 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
CONFIG.caller_name_headers.append('X-Uber-Source')
CONFIG.callee_endpoint_headers.append('X-Uber-Endpoint')
install_all_patches()
self.wsgi_app = create_wsgi_middleware(wsgi_app)
self.init_tracer()
def __call__(self, environ, start_response):
return self.wsgi_app(environ, start_response)
def init_tracer(self):
# code specific to your tracer implementation
pass
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.add_tag('error', str(exc_info))
span.finish()
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
@gen.coroutine
def __call__(self, request, handler, next_mw):
request_wrapper = http_server.TornadoRequestWrapper(request=request)
span = http_server.before_request(request=request_wrapper)
@gen.coroutine
def next_middleware_with_span():
yield next_mw()
yield run_coroutine_with_span(span=span,
func=next_middleware_with_span)
span.finish()
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)
virtualenv env
source env/bin/activate
make bootstrap
make test
Before new release, add a summary of changes since last version to CHANGELOG.rst
pip install zest.releaser[recommended]
prerelease
release
git push origin master --follow-tags
python setup.py sdist upload -r pypi
postrelease
git push