Package: opentracing-instrumentation
opentracing-python-instrumentationA collection of instrumentation tools to enable tracing withOpenTracing API.ModuleMake sure you are running recent enough versions ofpipandsetuptools, e.g. before installing your project requirements execute this:pip install --upgrade "setuptools>=29" "pip>=9"The module name isopentracing_instrumentation.What's insideSupported client frameworksThe following libraries are instrumented for tracing in this module:boto3— AWS SDK for PythonCelery— Distributed Task Queueurllib2requestsSQLAlchemyMySQLdbpsycopg2Tornado HTTP clientredisLimitationsFor some operations,Boto3usesThreadPoolExecutorunder the hood.
So, in order to make it thread-safe, the instrumentation is implemented usingspan_in_stack_context()whichforces youto useTornadoScopeManager.Server instrumentationFor inbound requests a helper functionbefore_requestis provided for creating middleware for frameworks like Flask and uWSGI.Manual instrumentationFinally, a@traced_functiondecorator is provided for manual instrumentation.In-process Context PropagationAs part of the OpenTracing 2.0 API, in-processSpanpropagation happens through the newly definedScopeManagerinterface. However, the existing functionality has been kept to provide backwards compatibility and
ease code migration:span_in_context()implements context propagation using the currentopentracing.tracer.scope_manager,
expected to be a thread-local basedScopeManager, such asopentracing.scope_managers.ThreadLocalScopeManager.span_in_stack_context()implements context propagation for Tornado applications
using the currentopentracing.tracer.scope_managertoo, expected to be an instance ofopentracing.scope_managers.tornado.TornadoScopeManager.get_current_span()returns the currently activeSpan, if any.Direct access to therequest_contextmodule as well as usage ofRequestContextandRequestContextManagerhave beenfullydeprecated, as they do not integrate with the new OpenTracing 2.0 API.
Using them alongget_current_span()is guaranteed to work, but it ishighlyrecommended
to switch to the previously mentioned functions.UsageThis 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 implementationwraps incoming request handlers into a method that reads the incoming
tracing info from the request and creates a new tracing SpanClient call sites are instrumented implicitly by executing a set of
availableclient_hooksthat monkey-patch some API points in several
common libraries likeSQLAlchemy,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 forClay framework:fromopentracing_instrumentationimportspan_in_contextfromopentracing_instrumentation.http_serverimportbefore_requestfromopentracing_instrumentation.http_serverimportWSGIRequestWrapperfromopentracing_instrumentation.client_hooksimportinstall_all_patchesclassTracerMiddleware(object):def__init__(self,app,wsgi_app):self.wsgi_app=wsgi_appself.service_name=app.nameCONFIG.app_name=self.service_nameCONFIG.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):returnself.wsgi_app(environ,start_response)definit_tracer(self):# code specific to your tracer implementationpassdefcreate_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."""defwsgi_tracing_middleware(environ,start_response):# TODO find out if the route can be retrieved from somewhererequest=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 Spandefstart_response_wrapper(status,response_headers,exc_info=None):ifexc_infoisnotNone:span.set_tag('error',str(exc_info))span.finish()returnstart_response(status,response_headers)withspan_in_context(span):returnother_wsgi(environ,start_response_wrapper)returnwsgi_tracing_middlewareAnd here's an example for middleware in Tornado-based app:importopentracingfromopentracing.scope_managers.tornadoimportTornadoScopeManagerfromopentracing_instrumentationimportspan_in_stack_context,http_serveropentracing.tracer=MyOpenTracingTracer(scope_manager=TornadoScopeManager())classTracerMiddleware(object):def__init__(self):# perform initialization similar to above, including installing# the client_hooks@gen.coroutinedef__call__(self,request,handler,next_mw):request_wrapper=http_server.TornadoRequestWrapper(request=request)span=http_server.before_request(request=request_wrapper)@gen.coroutinedefnext_middleware_with_span():yieldnext_mw()yieldrun_coroutine_with_span(span=span,func=next_middleware_with_span)span.finish()defrun_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."""withspan_in_stack_context(span):returnfunc(*args,**kwargs)CustomizationFor therequestslibrary, in case you want to set custom tags
to spans depending on content or some metadata of responses,
you can setresponse_handler_hook.
The hook must be a method with a signature(response, span),
whereresponseandspanare positional arguments,
so you can use different names for them if needed.fromopentracing_instrumentation.client_hooks.requestsimportpatcherdefhook(response,span):ifnotresponse.ok:span.set_tag('error','true')patcher.set_response_handler_hook(hook)If you have issues with getting the parent span, it is possible to override
default function that retrieves parent span.fromopentracing_instrumentation.client_hooksimportinstall_all_patches,set_current_span_funcset_current_span_func(my_custom_extractor_func)install_all_patches()DevelopmentPostgreSQL,RabbitMQ,Redis, andDynamoDBare required for certain tests.docker-composeup-dTo prepare a development environment please execute the following commands.virtualenvenvsourceenv/bin/activate
makebootstrap
maketestYou can usetoxto run tests as well.tox