def test_env_var_disables_instrument(self, env_var, are_uninstrumented): os.environ['SIGNALFX_TRACING_ENABLED'] = env_var modules = [utils.get_module(l) for l in expected_traceable_libraries] assert self.all_are_uninstrumented(modules) for m in expected_traceable_libraries: instrument(**{m: True}) assert self.all_are_uninstrumented(modules) is are_uninstrumented
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 test_instrument_with_true_and_env_var_false_doesnt_instrument_specified_libraries( self, module_name): env_var = 'SIGNALFX_{0}_ENABLED'.format(module_name.upper()) os.environ[env_var] = 'False' try: instrument(**{module_name: True}) mod = utils.get_module(module_name) assert not hasattr(mod, instrumented_attr) finally: os.environ.pop(env_var)
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 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 test_env_var_disables_prevents_auto_instrument(self, env_var, are_uninstrumented): os.environ['SIGNALFX_TRACING_ENABLED'] = env_var modules = [ utils.get_module(l) for l in expected_auto_instrumentable_libraries ] assert self.all_are_uninstrumented(modules) auto_instrument() assert self.all_are_uninstrumented(modules) is are_uninstrumented
def test_get_module_imports_unimported_modules(): restore_logging = False if 'logging' in sys.modules: restore_logging = True del sys.modules['logging'] try: logging = utils.get_module('logging') assert logging == sys.modules['logging'] finally: if restore_logging: import logging
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 test_auto_instrument_instruments_all_available_libraries(self): modules = [(utils.get_module(l), l) for l in expected_traceable_libraries] assert self.all_are_uninstrumented(modules) auto_instrument() for module, library in modules: if library in expected_auto_instrumentable_libraries: assert getattr(module, instrumented_attr) is True else: assert not hasattr(module, instrumented_attr)
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): 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 test_env_var_disables_instrument(self, env_var, are_uninstrumented): os.environ[tracing_enabled_env_var] = env_var try: modules = [ utils.get_module(l) for l in expected_traceable_libraries ] assert self.all_are_uninstrumented(modules) for m in expected_traceable_libraries: instrument(**{m: True}) assert self.all_are_uninstrumented(modules) is are_uninstrumented finally: os.environ.pop(tracing_enabled_env_var)
def test_env_var_disables_prevents_auto_instrument(self, env_var, are_uninstrumented): os.environ[tracing_enabled_env_var] = env_var try: modules = [ utils.get_module(l) for l in expected_auto_instrumentable_libraries ] assert self.all_are_uninstrumented(modules) auto_instrument() assert self.all_are_uninstrumented(modules) is are_uninstrumented finally: os.environ.pop(tracing_enabled_env_var)
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): 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): """ 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 test_instrumentation_env_var_disabled_prevents_auto_instrument( self, env_var, are_uninstrumented): enableds = [ 'SIGNALFX_{0}_ENABLED'.format(lib.upper()) for lib in expected_auto_instrumentable_libraries ] for enabled in enableds: os.environ[enabled] = env_var try: modules = [ utils.get_module(l) for l in expected_auto_instrumentable_libraries ] assert self.all_are_uninstrumented(modules) auto_instrument() assert self.all_are_uninstrumented(modules) is are_uninstrumented finally: for enabled in enableds: os.environ.pop(enabled)
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 test_instrument_with_true_instruments_specified_libraries(self): tornado = utils.get_module('tornado') assert not hasattr(tornado, instrumented_attr) instrument(tornado=True) assert getattr(tornado, instrumented_attr) is True
@worker_process_init.connect(weak=False) def create_global_tracer(*args, **kwargs): tracer = create_tracer(access_token=access_token, set_global=False) tracer_proxy.set_tracer(tracer) if hasattr(sys, 'argv') and sys.argv[0].split( os.path.sep)[-1] == 'celery' and 'worker' in sys.argv: create_celery_tracer() else: try: auto_instrument( create_tracer(access_token=access_token, set_global=True)) except Exception: print(traceback.format_exc()) # Do not prevent existing sitecustomize module import. Done by # removing this module's package and attempting to import # sitecustomize module. # Removing references to this sitecustomize module # can trigger garbage collection and cause lookup failures in other modules. sys.modules['sfx_sitecustomize'] = sys.modules.pop('sitecustomize', None) sys.path.remove(os.path.abspath(os.path.dirname(__file__))) # Attempt to load any existing sitecustomize module = get_module('sitecustomize') if module is None: # reset to our own if no preexisting sys.modules['sitecustomize'] = sys.modules['sfx_sitecustomize']
def test_uninstrument_uninstruments_specified_libraries(self, module_name): instrument(**{module_name: True}) mod = utils.get_module(module_name) assert getattr(mod, instrumented_attr) is True uninstrument(module_name) assert not hasattr(mod, instrumented_attr)
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)
def test_get_module_is_none_for_unavailable_modules(): assert utils.get_module('not_a_real_module_12345') is None