def test_patch_unpatch(self): tracer = get_dummy_tracer() writer = tracer.writer # Test patch idempotence patch() patch() client = pymongo.MongoClient(port=MONGO_CONFIG['port']) Pin.get_from(client).clone(tracer=tracer).onto(client) client['testdb'].drop_collection('whatever') spans = writer.pop() assert spans, spans assert len(spans) == 1 # Test unpatch unpatch() client = pymongo.MongoClient(port=MONGO_CONFIG['port']) client['testdb'].drop_collection('whatever') spans = writer.pop() assert not spans, spans # Test patch again patch() client = pymongo.MongoClient(port=MONGO_CONFIG['port']) Pin.get_from(client).clone(tracer=tracer).onto(client) client['testdb'].drop_collection('whatever') spans = writer.pop() assert spans, spans assert len(spans) == 1
def test_patch_unpatch(self): # Test patch idempotence patch() patch() tracer = get_dummy_tracer() Pin.get_from(Cluster).clone(tracer=tracer).onto(Cluster) session = Cluster(port=CASSANDRA_CONFIG['port']).connect(self.TEST_KEYSPACE) session.execute(self.TEST_QUERY) spans = tracer.writer.pop() assert spans, spans assert len(spans) == 1 # Test unpatch unpatch() session = Cluster(port=CASSANDRA_CONFIG['port']).connect(self.TEST_KEYSPACE) session.execute(self.TEST_QUERY) spans = tracer.writer.pop() assert not spans, spans # Test patch again patch() Pin.get_from(Cluster).clone(tracer=tracer).onto(Cluster) session = Cluster(port=CASSANDRA_CONFIG['port']).connect(self.TEST_KEYSPACE) session.execute(self.TEST_QUERY) spans = tracer.writer.pop() assert spans, spans
def get_tracer_and_connect(self): tracer = get_dummy_tracer() Pin.get_from(mongoengine.connect).clone(tracer=tracer).onto( mongoengine.connect) mongoengine.connect(port=MONGO_CONFIG['port']) return tracer
def test_blueprint_add_url_rule(self): """ When we call ``flask.Blueprint.add_url_rule`` When the ``Blueprint`` has a ``Pin`` attached We clone the Blueprint's ``Pin`` to the view When the ``Blueprint`` does not have a ``Pin`` attached We do not attach a ``Pin`` to the func """ # When the Blueprint has a Pin attached bp = flask.Blueprint('pinned', __name__) Pin(service='flask-bp', tracer=self.tracer).onto(bp) @bp.route('/') def test_view(): pass # Assert the view func has a `Pin` attached with the Blueprint's service name pin = Pin.get_from(test_view) self.assertIsNotNone(pin) self.assertEqual(pin.service, 'flask-bp') # When the Blueprint does not have a Pin attached bp = flask.Blueprint('not-pinned', __name__) @bp.route('/') def test_view(): pass # Assert the view does not have a `Pin` attached pin = Pin.get_from(test_view) self.assertIsNone(pin)
def test_patch_unpatch(self): tracer = get_dummy_tracer() writer = tracer.writer # Test patch idempotence patch() patch() r = self._get_test_client() Pin.get_from(r).clone(tracer=tracer).onto(r) r.get('key') spans = writer.pop() assert spans, spans assert len(spans) == 1 # Test unpatch unpatch() r = self._get_test_client() r.get('key') spans = writer.pop() assert not spans, spans # Test patch again patch() r = self._get_test_client() Pin.get_from(r).clone(tracer=tracer).onto(r) r.get('key') spans = writer.pop() assert spans, spans assert len(spans) == 1
def trace_before_publish(*args, **kwargs): # `before_task_publish` signal doesn't propagate the task instance so # we need to retrieve it from the Celery Registry to access the `Pin`. The # `Task` instance **does not** include any information about the current # execution, so it **must not** be used to retrieve `request` data. task_name = kwargs.get('sender') task = registry.tasks.get(task_name) task_id = retrieve_task_id(kwargs) # safe-guard to avoid crashes in case the signals API # changes in Celery if task is None or task_id is None: log.debug( 'unable to extract the Task and the task_id. This version of Celery may not be supported.' ) return # propagate the `Span` in the current task Context pin = Pin.get_from(task) or Pin.get_from(task.app) if pin is None: return # apply some tags here because most of the data is not available # in the task_after_publish signal service = config.celery['producer_service_name'] span = pin.tracer.trace(c.PRODUCER_ROOT_SPAN, service=service, resource=task_name) span.set_tag(c.TASK_TAG_KEY, c.TASK_APPLY_ASYNC) span.set_tag('celery.id', task_id) span.set_tags(tags_from_context(kwargs)) # Note: adding tags from `traceback` or `state` calls will make an # API call to the backend for the properties so we should rely # only on the given `Context` attach_span(task, task_id, span, is_publish=True)
def test_patch_unpatch(self): tracer = get_dummy_tracer() writer = tracer.writer # Test patch idempotence patch() patch() r = redis.Redis(port=REDIS_CONFIG['port']) Pin.get_from(r).clone(tracer=tracer).onto(r) r.get('key') spans = writer.pop() assert spans, spans assert len(spans) == 1 # Test unpatch unpatch() r = redis.Redis(port=REDIS_CONFIG['port']) r.get('key') spans = writer.pop() assert not spans, spans # Test patch again patch() r = redis.Redis(port=REDIS_CONFIG['port']) Pin.get_from(r).clone(tracer=tracer).onto(r) r.get('key') spans = writer.pop() assert spans, spans assert len(spans) == 1
def test_same_tracer(self): """Ensure same tracer reference is used by the pin on pymemache and Clients. """ client = pymemcache.client.base.Client((TEST_HOST, TEST_PORT)) self.assertEqual( Pin.get_from(client).tracer, Pin.get_from(pymemcache).tracer)
def get_client(self): url = '%s:%s' % (cfg['host'], cfg['port']) client = pylibmc.Client([url]) client.flush_all() Pin.get_from(client).clone(tracer=self.tracer).onto(client) return client, self.tracer
def trace_spans(self): conn, _ = yield from self._get_conn_and_tracer() Pin.get_from(conn).clone(service='db', tracer=self.tracer).onto(conn) cursor = yield from conn.cursor() yield from cursor.execute('select \'foobar\'') rows = yield from cursor.fetchall() assert rows return self.get_spans()
def test_override_missing(self): # ensure overriding an instance doesn't override the Class class A(object): pass a = A() assert Pin.get_from(a) is None Pin.override(a, service='metrics') assert Pin.get_from(a).service == 'metrics' b = A() assert Pin.get_from(b) is None
def test_patch_unpatch(self): """ Tests repatch-unpatch cycle """ # Already call patch in setUp self.assertTrue(Pin.get_from(molten) is not None) molten_client() spans = self.tracer.writer.pop() self.assertTrue(len(spans) > 0) # Test unpatch unpatch() self.assertTrue(Pin.get_from(molten) is None) molten_client() spans = self.tracer.writer.pop() self.assertEqual(len(spans), 0)
def test_connect_factory(self): tracer = get_dummy_tracer() services = ['db', 'another'] for service in services: conn, _ = yield from self._get_conn_and_tracer() Pin.get_from(conn).clone(service=service, tracer=tracer).onto(conn) yield from self.assert_conn_is_traced(tracer, conn, service) conn.close() # ensure we have the service types service_meta = tracer.writer.pop_services() expected = {} assert service_meta == expected
def test_override(self): # ensure Override works for an instance object class A(object): pass Pin(service='metrics', app='flask').onto(A) a = A() Pin.override(a, app='django') assert Pin.get_from(a).app == 'django' assert Pin.get_from(a).service == 'metrics' b = A() assert Pin.get_from(b).app == 'flask' assert Pin.get_from(b).service == 'metrics'
def traced_blueprint_register(wrapped, instance, args, kwargs): """ Wrapper for flask.blueprints.Blueprint.register This wrapper just ensures the blueprint has a pin, either set manually on itself from the user or inherited from the application """ app = kwargs.get('app', args[0]) # Check if this Blueprint has a pin, otherwise clone the one from the app onto it pin = Pin.get_from(instance) if not pin: pin = Pin.get_from(app) if pin: pin.clone().onto(instance) return wrapped(*args, **kwargs)
def test_unpatch_patch(self): """ Tests unpatch-patch cycle """ unpatch() self.assertIsNone(Pin.get_from(molten)) molten_client() spans = self.tracer.writer.pop() self.assertEqual(len(spans), 0) patch() # Need to override Pin here as we do in setUp Pin.override(molten, tracer=self.tracer) self.assertTrue(Pin.get_from(molten) is not None) molten_client() spans = self.tracer.writer.pop() self.assertTrue(len(spans) > 0)
def test_pin_config_is_a_copy(self): # ensure that when a `Pin` is cloned, the config is a copy obj = self.Obj() Pin.override(obj, service='metrics') p1 = Pin.get_from(obj) assert p1._config is not None p1._config['distributed_tracing'] = True Pin.override(obj, service='intake') p2 = Pin.get_from(obj) assert p2._config is not None p2._config['distributed_tracing'] = False assert p1._config['distributed_tracing'] is True assert p2._config['distributed_tracing'] is False
def test_patch_before_import(self): from oteltrace import patch patch(celery=True) import celery app = celery.Celery() assert Pin.get_from(app) is not None
def test_patch_after_import(self): import celery from oteltrace import patch patch(celery=True) app = celery.Celery() assert Pin.get_from(app) is not None
def _get_conn(self, service=None): conn = psycopg2.connect(**POSTGRES_CONFIG) pin = Pin.get_from(conn) if pin: pin.clone(service=service, tracer=self.tracer).onto(conn) return conn
def get_spans(self): spans = [] for _, client in self.client.clients.items(): pin = Pin.get_from(client) tracer = pin.tracer spans.extend(tracer.writer.pop()) return spans
def test_patch_unpatch(self): unpatch() # assert we start unpatched conn = mysql.connector.connect(**MYSQL_CONFIG) assert not Pin.get_from(conn) conn.close() patch() try: writer = self.tracer.writer conn = mysql.connector.connect(**MYSQL_CONFIG) pin = Pin.get_from(conn) assert pin pin.clone(service=self.TEST_SERVICE, tracer=self.tracer).onto(conn) assert conn.is_connected() cursor = conn.cursor() cursor.execute('SELECT 1') rows = cursor.fetchall() assert len(rows) == 1 spans = writer.pop() assert len(spans) == 1 span = spans[0] assert span.service == self.TEST_SERVICE assert span.name == 'mysql.query' assert span.span_type == 'sql' assert span.error == 0 assert_dict_issuperset( span.meta, { 'out.host': u'127.0.0.1', 'out.port': u'3306', 'db.name': u'test', 'db.user': u'test', }) assert span.get_tag('sql.query') is None finally: unpatch() # assert we finish unpatched conn = mysql.connector.connect(**MYSQL_CONFIG) assert not Pin.get_from(conn) conn.close() patch()
def test_pin_config(self): # ensure `Pin` has a configuration object that can be modified obj = self.Obj() Pin.override(obj, service='metrics') pin = Pin.get_from(obj) assert pin._config is not None pin._config['distributed_tracing'] = True assert pin._config['distributed_tracing'] is True
def test_pin(self): # ensure a Pin can be attached to an instance obj = self.Obj() pin = Pin(service='metrics') pin.onto(obj) got = Pin.get_from(obj) assert got.service == pin.service assert got is pin
def test_pin_does_not_override_global(self): # ensure that when a `Pin` is created from a class, the specific # instance doesn't override the global one class A(object): pass Pin.override(A, service='metrics') global_pin = Pin.get_from(A) global_pin._config['distributed_tracing'] = True a = A() pin = Pin.get_from(a) assert pin is not None assert pin._config['distributed_tracing'] is True pin._config['distributed_tracing'] = False assert global_pin._config['distributed_tracing'] is True assert pin._config['distributed_tracing'] is False
def test_cant_pin_with_slots(self): # ensure a Pin can't be attached if the __slots__ is defined class Obj(object): __slots__ = ['value'] obj = Obj() obj.value = 1 Pin(service='metrics').onto(obj) got = Pin.get_from(obj) assert got is None
def test_pin_does_not_override_global_with_new_instance(self): # ensure that when a `Pin` is created from a class, the specific # instance doesn't override the global one, even if only the # `onto()` API has been used class A(object): pass pin = Pin(service='metrics') pin.onto(A) global_pin = Pin.get_from(A) global_pin._config['distributed_tracing'] = True a = A() pin = Pin.get_from(a) assert pin is not None assert pin._config['distributed_tracing'] is True pin._config['distributed_tracing'] = False assert global_pin._config['distributed_tracing'] is True assert pin._config['distributed_tracing'] is False
def test_blueprint_register(self): """ When we register a ``flask.Blueprint`` to a ``flask.Flask`` When no ``Pin`` is attached to the ``Blueprint`` We attach the pin from the ``flask.Flask`` app When a ``Pin`` is manually added to the ``Blueprint`` We do not use the ``flask.Flask`` app ``Pin`` """ bp = flask.Blueprint('pinned', __name__) Pin(service='flask-bp', tracer=self.tracer).onto(bp) # DEV: This is more common than calling ``flask.Blueprint.register`` directly self.app.register_blueprint(bp) pin = Pin.get_from(bp) self.assertEqual(pin.service, 'flask-bp') bp = flask.Blueprint('not-pinned', __name__) self.app.register_blueprint(bp) pin = Pin.get_from(bp) self.assertEqual(pin.service, 'flask')
def _client_channel_interceptor(wrapped, instance, args, kwargs): channel = wrapped(*args, **kwargs) pin = Pin.get_from(constants.GRPC_PIN_MODULE_CLIENT) if not pin or not pin.enabled(): return channel (host, port) = _parse_target_from_arguments(args, kwargs) interceptor_function = create_client_interceptor(pin, host, port) return grpc.intercept_channel(channel, interceptor_function)
def _unpatch_server(): if not getattr(constants.GRPC_PIN_MODULE_SERVER, '__opentelemetry_patch', False): return setattr(constants.GRPC_PIN_MODULE_SERVER, '__opentelemetry_patch', False) pin = Pin.get_from(constants.GRPC_PIN_MODULE_SERVER) if pin: pin.remove_from(constants.GRPC_PIN_MODULE_SERVER) _u(grpc, 'server')