def test_mark_instrumented_and_uninstrumented(): class MockModule(object): pass module = MockModule() utils.mark_instrumented(module) assert getattr(module, constants.instrumented_attr) is True assert utils.is_instrumented(module) is True utils.mark_uninstrumented(module) assert not hasattr(module, constants.instrumented_attr) assert utils.is_instrumented(module) is False
def instrument(tracer=None): tornado = utils.get_module('tornado') if utils.is_instrumented(tornado): return tornado_opentracing = utils.get_module('tornado_opentracing') def _tracer_config(wrapped_tracer_config, _, wrapt_args, __): """ A function wrapper for tornado_opentracing's monkey patcher of tornado.web.Application.__init__() used to inject tracer configuration as settings arguments. As a wrapt function wrapper of a function_wrapper, _tracer_config's meaningful arguments are oddly nested. """ __init__ = wrapt_args[0] app = wrapt_args[1] args = wrapt_args[2] kwargs = wrapt_args[3] _tracer = tracer or config.tracer or opentracing.tracer kwargs['opentracing_tracing'] = tornado_opentracing.TornadoTracing(_tracer) kwargs['opentracing_trace_all'] = config.trace_all kwargs['opentracing_trace_client'] = config.trace_client kwargs['opentracing_traced_attributes'] = config.traced_attributes kwargs['opentracing_start_span_cb'] = config.start_span_cb wrapped_tracer_config(__init__, app, args, kwargs) wrap_function_wrapper('tornado_opentracing.application', 'tracer_config', _tracer_config) tornado_opentracing.init_tracing() utils.mark_instrumented(tornado)
def uninstrument(): elasticsearch = utils.get_module('elasticsearch') if not utils.is_instrumented(elasticsearch): return from elasticsearch_opentracing import disable_tracing, TracingTransport disable_tracing() # because of https://bugs.python.org/issue25731 we cannot simply restore # built-in __new__. Use a generic implementation as a workaround def __new__(cls, *_, **__): return object.__new__(cls) if _tracing_transport_new[0] is not None: if hasattr(_tracing_transport_new[0], '__get__'): TracingTransport.__new__ = _tracing_transport_new[0].__get__( TracingTransport) else: # builtin doesn't follow descriptor protocol TracingTransport.__new__ = __new__.__get__(TracingTransport) _tracing_transport_new[0] = None if _transport_new[0] is not None: if hasattr(_transport_new[0], '__get__'): elasticsearch.transport.Transport.__new__ = _transport_new[ 0].__get__(elasticsearch.transport.Transport) else: elasticsearch.transport.Transport.__new__ = __new__.__get__( elasticsearch.transport.Transport) _transport_new[0] = None utils.mark_uninstrumented(elasticsearch)
def uninstrument(): falcon = utils.get_module("falcon") if not utils.is_instrumented(falcon): return utils.revert_wrapper(falcon.API, "__init__") utils.mark_uninstrumented(falcon)
def instrument(tracer=None): pymongo = utils.get_module('pymongo') if utils.is_instrumented(pymongo): return pymongo_opentracing = utils.get_module('pymongo_opentracing') def pymongo_tracer(__init__, app, args, kwargs): """ A function wrapper of pymongo.MongoClient.__init__ to register a corresponding pymongo_opentracing.CommandTracing upon client instantiation. """ _tracer = tracer or config.tracer or opentracing.tracer command_tracing = pymongo_opentracing.CommandTracing( tracer=_tracer, span_tags=config.span_tags or {}, ) event_listeners = list(kwargs.pop('event_listeners', [])) event_listeners.insert(0, command_tracing) kwargs['event_listeners'] = event_listeners __init__(*args, **kwargs) wrap_function_wrapper('pymongo', 'MongoClient.__init__', pymongo_tracer) utils.mark_instrumented(pymongo)
def instrument(tracer=None): """ Requests auto-instrumentation works by hooking a __new__ proxy for a CeleryTracing instance upon celery.Celery initialization to trigger proper inheritance. CeleryTracing.__init__ is also wrapped for correct argument injection. """ celery = utils.get_module('celery') if utils.is_instrumented(celery): return import celery.app def celery_tracing_init(__init__, instance, args, kwargs): _tracer = tracer or config.tracer or opentracing.tracer __init__(*args, tracer=_tracer, propagate=config.propagate, span_tags=config.span_tags or {}, **kwargs) from celery_opentracing import CeleryTracing _celery_new[0] = celery.Celery.__new__ _celery_tracing_new[0] = CeleryTracing.__new__ CeleryTracing.__new__ = celery_tracing_new.__get__(CeleryTracing) celery.app.base.Celery.__new__ = celery_new.__get__(celery.Celery) wrap_function_wrapper('celery_opentracing.tracing', 'CeleryTracing.__init__', celery_tracing_init) utils.mark_instrumented(celery)
def instrument(tracer=None): django = utils.get_module('django') if utils.is_instrumented(django): return settings = utils.get_module('django.conf').settings # Tracer settings (need to be before initialization) settings.OPENTRACING_TRACE_ALL = config.trace_all settings.OPENTRACING_TRACED_ATTRIBUTES = config.traced_attributes settings.OPENTRACING_SET_GLOBAL_TRACER = config.set_global_tracer # DjangoTracing will obtain global tracer for us _tracer = tracer or config.tracer if _tracer is not None: django_opentracing = utils.get_module('django_opentracing') settings.OPENTRACING_TRACING = django_opentracing.DjangoTracer(tracer) if config.tracer_callable: settings.OPENTRACING_TRACER_CALLABLE = config.tracer_callable settings.OPENTRACING_TRACER_PARAMETERS = config.tracer_parameters or {} middleware_classes, setting = get_middleware_and_setting_name() setattr(settings, setting, [config.middleware_class] + list(middleware_classes)) utils.mark_instrumented(django)
def uninstrument(): logging = utils.get_module('logging') if not utils.is_instrumented(logging): return utils.revert_wrapper(logging, 'Logger.makeRecord') utils.mark_uninstrumented(logging)
def instrument(tracer=None): flask = utils.get_module('flask') if utils.is_instrumented(flask): return flask_opentracing = utils.get_module('flask_opentracing') def flask_tracer(__init__, app, args, kwargs): """ A function wrapper of flask.Flask.__init__ to create a corresponding flask_opentracing.FlaskTracer upon app instantiation. """ __init__(*args, **kwargs) _tracer = tracer or config.tracer or opentracing.tracer app.config['FLASK_TRACER'] = flask_opentracing.FlaskTracer( tracer=_tracer, trace_all_requests=config.trace_all, app=app, traced_attributes=config.traced_attributes) wrap_function_wrapper('flask', 'Flask.__init__', flask_tracer) utils.mark_instrumented(flask)
def instrument(tracer=None): """ Requests auto-instrumentation works by hooking a __new__ proxy for a SessionTracing instance upon requests.sessions.Session initialization to trigger proper inheritance. SessionTracing.__init__ is also wrapped for correct argument injection. """ requests = utils.get_module('requests') if utils.is_instrumented(requests): return def session_tracing_init(__init__, instance, _, __): _tracer = tracer or config.tracer or opentracing.tracer __init__(_tracer, propagate=config.propagate, span_tags=config.span_tags or {}) from requests_opentracing import SessionTracing _session_new[0] = requests.Session.__new__ _session_tracing_new[0] = SessionTracing.__new__ SessionTracing.__new__ = session_tracing_new.__get__(SessionTracing) requests.Session.__new__ = session_new.__get__(requests.Session) wrap_function_wrapper('requests_opentracing.tracing', 'SessionTracing.__init__', session_tracing_init) utils.mark_instrumented(requests)
def uninstrument(): """Will only prevent new clients from registering tracers.""" redis = utils.get_module('redis') if not utils.is_instrumented(redis): return from redis.client import StrictRedis utils.revert_wrapper(StrictRedis, '__init__') utils.mark_uninstrumented(redis)
def uninstrument(): tornado = utils.get_module('tornado') if not utils.is_instrumented(tornado): return tornado_initialization = utils.get_module('tornado_opentracing.initialization') tornado_initialization._unpatch_tornado() tornado_initialization._unpatch_tornado_client() utils.mark_uninstrumented(tornado)
def uninstrument(): """ Will only prevent new Connections from registering tracers. It's not reasonably feasible to unwrap existing ConnectionTracing instances """ psycopg2 = utils.get_module('psycopg2') if not utils.is_instrumented(psycopg2): return utils.revert_wrapper(psycopg2, 'connect') utils.mark_uninstrumented(psycopg2)
def uninstrument(): """ Will only prevent new applications from registering tracers. It's not reasonably feasible to remove existing before/after_request trace methods of existing apps. """ bottle = utils.get_module('bottle') if not utils.is_instrumented(bottle): return utils.revert_wrapper(bottle, 'run') utils.mark_uninstrumented(bottle)
def uninstrument(): """ Will only prevent new applications from registering tracers. It's not reasonably feasible to remove existing before/after_request trace methods of existing apps. """ flask = utils.get_module('flask') if not utils.is_instrumented(flask): return utils.revert_wrapper(flask.Flask, '__init__') utils.mark_uninstrumented(flask)
def uninstrument(): """ Will only prevent new clients from registering tracers. It's not reasonably feasible to remove existing before/after_request trace methods of existing clients. """ pymongo = utils.get_module('pymongo') if not utils.is_instrumented(pymongo): return utils.revert_wrapper(pymongo.MongoClient, '__init__') utils.mark_uninstrumented(pymongo)
def instrument(tracer=None): aioredis = utils.get_module('aioredis') if utils.is_instrumented(aioredis): return tracing.init_tracing(tracer=tracer or config.tracer or opentracing.tracer) def traced_client(__init__, client, args, kwargs): __init__(*args, **kwargs) tracing.trace_client(client) wrap_function_wrapper('aioredis', 'Redis.__init__', traced_client) utils.mark_instrumented(aioredis)
def instrument(tracer=None): redis = utils.get_module('redis') if utils.is_instrumented(redis): return redis_opentracing = utils.get_module('redis_opentracing') redis_opentracing.init_tracing(tracer=tracer or config.tracer or opentracing.tracer, trace_all_classes=False) def traced_client(__init__, client, args, kwargs): __init__(*args, **kwargs) redis_opentracing.trace_client(client) wrap_function_wrapper('redis.client', 'StrictRedis.__init__', traced_client) utils.mark_instrumented(redis)
def instrument(tracer=None): falcon = utils.get_module("falcon") if utils.is_instrumented(falcon): return _tracer = tracer or config.tracer or opentracing.tracer def traced_init(wrapped, instance, args, kwargs): mw = kwargs.pop("middleware", []) mw.insert(0, TraceMiddleware(_tracer, config.traced_attributes)) kwargs["middleware"] = mw wrapped(*args, **kwargs) wrap_function_wrapper("falcon", "API.__init__", traced_init) utils.mark_instrumented(falcon)
def instrument(tracer=None): pymysql = utils.get_module('pymysql') if utils.is_instrumented(pymysql): return dbapi_opentracing = utils.get_module('dbapi_opentracing') def pymysql_tracer(connect, _, args, kwargs): """ A function wrapper of pymysql.connect() to create a corresponding dbapi_opentracing.ConnectionTracing upon database connection. """ connection = connect(*args, **kwargs) _tracer = tracer or config.tracer or opentracing.tracer traced_commands = set(config.traced_commands) traced_commands_kwargs = dict(trace_execute=False, trace_executemany=False, trace_callproc=False, trace_commit=False, trace_rollback=False) for command in traced_commands: flag = 'trace_{}'.format(command.lower()) if flag not in traced_commands_kwargs: log.warn( 'Unable to trace PyMySQL command "{}". Ignoring.'.format( command)) continue traced_commands_kwargs[flag] = True span_tags = {tags.DATABASE_TYPE: 'MySQL'} if connection.db is not None: span_tags[tags.DATABASE_INSTANCE] = ensure_str(connection.db) if config.span_tags is not None: span_tags.update(config.span_tags) return dbapi_opentracing.ConnectionTracing(connection, _tracer, span_tags=span_tags, **traced_commands_kwargs) wrap_function_wrapper('pymysql', 'connect', pymysql_tracer) utils.mark_instrumented(pymysql)
def uninstrument(): django = utils.get_module('django') if not utils.is_instrumented(django): return settings = utils.get_module('django.conf').settings for setting in ('OPENTRACING_TRACE_ALL', 'OPENTRACING_TRACED_ATTRIBUTES', 'OPENTRACING_TRACER_CALLABLE', 'OPENTRACING_TRACER_PARAMETERS', 'OPENTRACING_SET_GLOBAL_TRACER', 'OPENTRACING_TRACING', 'OPENTRACING_TRACER'): try: delattr(settings, setting) except AttributeError: pass middleware, setting = get_middleware_and_setting_name() middleware_classes = [i for i in middleware if i != config.middleware_class] setattr(settings, setting, middleware_classes) utils.mark_uninstrumented(django)
def instrument(tracer=None): """ Unlike all other instrumentations, this instrumentation does not patch the logging lib to automatically generate spans. Instead it patches the lib to automatically inject trace context into logs. """ logging = utils.get_module('logging') if utils.is_instrumented(logging): return wrap_function_wrapper(logging, 'Logger.makeRecord', makeRecordPatched) if config.injection_enabled: level = logging.INFO if utils.is_truthy(os.environ.get('SIGNALFX_TRACING_DEBUG', False)): level = logging.DEBUG logging.basicConfig(level=level, format=config.logging_format) utils.mark_instrumented(logging)
def instrument(tracer=None): bottle = utils.get_module('bottle') if utils.is_instrumented(bottle): return def bottle_tracer(run, _, args, kwargs): """ A function wrapper of bottle.run to create a corresponding BottleTracer upon app instantiation. """ app = kwargs.get('app', default_app()) _tracer = tracer or config.tracer or opentracing.tracer BottleTracing(tracer=_tracer, trace_all_requests=config.trace_all, app=app, traced_attributes=config.traced_attributes) run(**kwargs) wrap_function_wrapper('bottle', 'run', bottle_tracer) utils.mark_instrumented(bottle)
def uninstrument(): requests = utils.get_module('requests') if not utils.is_instrumented(requests): return from requests_opentracing import SessionTracing if _session_tracing_new[0] is not None: if hasattr(_session_tracing_new[0], '__get__'): SessionTracing.__new__ = _session_tracing_new[0].__get__(SessionTracing) else: # builtin doesn't follow descriptor protocol SessionTracing.__new__ = _session_tracing_new[0] if _session_new[0] is not None: if hasattr(_session_new[0], '__get__'): requests.Session.__new__ = _session_new[0].__get__(requests.Session) else: requests.Session.__new__ = _session_new[0] utils.revert_wrapper(SessionTracing, '__init__') utils.mark_uninstrumented(requests)
def instrument(tracer=None): """ Elasticsearch auto-instrumentation works by hooking a __new__ proxy for a TracingTransport instance upon elasticsearch.transports.Transport initialization to trigger proper inheritance. """ elasticsearch = utils.get_module('elasticsearch') if utils.is_instrumented(elasticsearch): return from elasticsearch_opentracing import init_tracing, TracingTransport _tracer = tracer or config.tracer or opentracing.tracer init_tracing(_tracer, trace_all_requests=True, prefix=config.prefix) _transport_new[0] = elasticsearch.transport.Transport.__new__ _tracing_transport_new[0] = TracingTransport.__new__ TracingTransport.__new__ = tracing_transport_new.__get__(TracingTransport) elasticsearch.transport.Transport.__new__ = transport_new.__get__( elasticsearch.transport.Transport) utils.mark_instrumented(elasticsearch)
def uninstrument(): celery = utils.get_module('celery') if not utils.is_instrumented(celery): return import celery.app from celery_opentracing import CeleryTracing if _celery_tracing_new[0] is not None: if hasattr(_celery_tracing_new[0], '__get__'): CeleryTracing.__new__ = _celery_tracing_new[0].__get__( CeleryTracing) else: # builtin doesn't follow descriptor protocol CeleryTracing.__new__ = _celery_tracing_new[0] if _celery_new[0] is not None: if hasattr(_celery_new[0], '__get__'): celery.app.base.Celery.__new__ = _celery_new[0].__get__( celery.Celery) else: celery.app.base.Celery.__new__ = _celery_new[0] utils.revert_wrapper(CeleryTracing, '__init__') utils.mark_uninstrumented(celery)
def instrument(tracer=None): psycopg2 = utils.get_module('psycopg2') if psycopg2 is None or utils.is_instrumented(psycopg2): return dbapi_opentracing = utils.get_module('dbapi_opentracing') if dbapi_opentracing is None: return def psycopg2_tracer(connect, _, args, kwargs): """ A function wrapper of psycopg2.connect() to create a corresponding dbapi_opentracing.ConnectionTracing upon database connection. """ _tracer = tracer or config.tracer or opentracing.tracer traced_commands = set(config.traced_commands) traced_commands_kwargs = dict(trace_execute=False, trace_executemany=False, trace_callproc=False, trace_commit=False, trace_rollback=False) for command in traced_commands: flag = 'trace_{}'.format(command.lower()) if flag not in traced_commands_kwargs: log.warn( 'Unable to trace Psycopg command "{}". Ignoring.'.format( command)) continue traced_commands_kwargs[flag] = True span_tags = {tags.DATABASE_TYPE: 'PostgreSQL'} if config.span_tags is not None: span_tags.update(config.span_tags) if 'connection_factory' in kwargs: connection_factory = kwargs['connection_factory'] if not inspect.isclass(connection_factory): log.error( 'connection_factory value %s is not a class, so it cannot be subclassed along with ' 'PsycopgConnectionTracing. No auto-instrumentation possible.', connection_factory) return connect(*args, **kwargs) traced_commands_kwargs['connection_factory'] = kwargs.pop( 'connection_factory') traced_commands_kwargs['tracer'] = _tracer traced_commands_kwargs['span_tags'] = span_tags def init_psycopg_connection_tracing(*a): return dbapi_opentracing.PsycopgConnectionTracing( *a, **traced_commands_kwargs) connection = connect( *args, connection_factory=init_psycopg_connection_tracing, **kwargs) if tags.DATABASE_INSTANCE not in span_tags: db_name = connection.get_dsn_parameters().get('dbname') if db_name: span_tags[tags.DATABASE_INSTANCE] = db_name return connection wrap_function_wrapper('psycopg2', 'connect', psycopg2_tracer) utils.mark_instrumented(psycopg2)