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)
示例#2
0
    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 setUp(self):
     super(TestRedisPatch, self).setUp()
     patch()
     r = redis.Redis(port=self.TEST_PORT)
     r.flushall()
     Pin.override(r, service=self.TEST_SERVICE, tracer=self.tracer)
     self.r = r
示例#4
0
    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
示例#5
0
    def test_fetch_no_analytics(self):
        """ Confirm fetch* methods do not have analytics sample rate metric """
        with self.override_config(
                'dbapi2',
                dict(analytics_enabled=True)
        ):
            cursor = self.cursor
            cursor.rowcount = 0
            cursor.fetchone.return_value = '__result__'
            pin = Pin('pin_name', tracer=self.tracer)
            traced_cursor = FetchTracedCursor(cursor, pin)
            assert '__result__' == traced_cursor.fetchone('arg_1', kwarg1='kwarg1')

            span = self.tracer.writer.pop()[0]
            self.assertIsNone(span.get_metric(ANALYTICS_SAMPLE_RATE_KEY))

            cursor = self.cursor
            cursor.rowcount = 0
            cursor.fetchall.return_value = '__result__'
            pin = Pin('pin_name', tracer=self.tracer)
            traced_cursor = FetchTracedCursor(cursor, pin)
            assert '__result__' == traced_cursor.fetchall('arg_1', kwarg1='kwarg1')

            span = self.tracer.writer.pop()[0]
            self.assertIsNone(span.get_metric(ANALYTICS_SAMPLE_RATE_KEY))

            cursor = self.cursor
            cursor.rowcount = 0
            cursor.fetchmany.return_value = '__result__'
            pin = Pin('pin_name', tracer=self.tracer)
            traced_cursor = FetchTracedCursor(cursor, pin)
            assert '__result__' == traced_cursor.fetchmany('arg_1', kwarg1='kwarg1')

            span = self.tracer.writer.pop()[0]
            self.assertIsNone(span.get_metric(ANALYTICS_SAMPLE_RATE_KEY))
示例#6
0
    def test_cursor_override(self):
        """Test overriding the tracer with our own."""
        conn, cur = self.test_conn

        Pin.override(cur, tracer=self.test_tracer)

        with conn:
            cur.execute(
                "INSERT INTO {} (a, b) VALUES (1, 'aa');".format(TEST_TABLE))
            cur.execute('SELECT * FROM {};'.format(TEST_TABLE))

        spans = self.test_tracer.writer.pop()
        assert len(spans) == 2

        # check all the metadata
        assert spans[0].service == 'vertica'
        assert spans[0].span_type == 'sql'
        assert spans[0].name == 'vertica.query'
        assert spans[0].get_metric('db.rowcount') == -1
        query = "INSERT INTO test_table (a, b) VALUES (1, 'aa');"
        assert spans[0].resource == query
        assert spans[0].get_tag('out.host') == '127.0.0.1'
        assert spans[0].get_tag('out.port') == '5433'

        assert spans[1].resource == 'SELECT * FROM test_table;'
示例#7
0
    def test_configuration_routine(self):
        """Ensure that the integration routines can be configured."""
        routine_config = dict(patch={
            'vertica_python.vertica.connection.Connection':
            dict(routines=dict(cursor=dict(
                operation_name='get_cursor',
                trace_enabled=True,
            ), ), ),
        }, )

        # Make a copy of the vertica config first before we merge our settings over
        # DEV: First argument gets merged into the second
        copy = deepmerge(config.vertica, dict())
        overrides = deepmerge(routine_config, copy)
        with self.override_config('vertica', overrides):
            patch()
            import vertica_python

            test_tracer = get_dummy_tracer()

            conn = vertica_python.connect(**VERTICA_CONFIG)
            Pin.override(conn, service='mycustomservice', tracer=test_tracer)
            conn.cursor()  # should be traced now
            conn.close()
        spans = test_tracer.writer.pop()
        assert len(spans) == 1
        assert spans[0].name == 'get_cursor'
        assert spans[0].service == 'mycustomservice'
示例#8
0
    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 = 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_route_success(self):
        """ Tests request was a success with the expected span tags """
        response = molten_client()
        spans = self.tracer.writer.pop()
        self.assertEqual(response.status_code, 200)
        # TestResponse from TestClient is wrapper around Response so we must
        # access data property
        self.assertEqual(response.data, '"Hello 24 year old named Jim!"')
        span = spans[0]
        self.assertEqual(span.service, 'molten')
        self.assertEqual(span.name, 'molten.request')
        self.assertEqual(span.resource, 'GET /hello/{name}/{age}')
        self.assertEqual(span.get_tag('http.method'), 'GET')
        self.assertEqual(span.get_tag(http.URL),
                         'http://127.0.0.1:8000/hello/Jim/24')
        self.assertEqual(span.get_tag('http.status_code'), '200')
        assert http.QUERY_STRING not in span.meta

        # See test_resources below for specifics of this difference
        if MOLTEN_VERSION >= (0, 7, 2):
            self.assertEqual(len(spans), 18)
        else:
            self.assertEqual(len(spans), 16)

        # test override of service name
        Pin.override(molten, service=self.TEST_SERVICE)
        response = molten_client()
        spans = self.tracer.writer.pop()
        self.assertEqual(spans[0].service, 'molten-patch')
示例#11
0
    def test_blueprint_request_pin_override(self):
        """
        When making a request to a Blueprint's endpoint
            When we attach a ``Pin`` to the Blueprint
                We create the expected spans
        """
        bp = flask.Blueprint('bp', __name__)
        Pin.override(bp, service='flask-bp', tracer=self.tracer)

        @bp.route('/')
        def test():
            return 'test'

        self.app.register_blueprint(bp)

        # Request the endpoint
        self.client.get('/')

        # Only extract the span we care about
        # DEV: Making a request creates a bunch of lifecycle spans,
        #   ignore them, we test them elsewhere
        span = self.find_span_by_name(self.get_spans(), 'bp.test')
        self.assertEqual(span.service, 'flask-bp')
        self.assertEqual(span.name, 'bp.test')
        self.assertEqual(span.resource, '/')
        self.assertEqual(span.meta, dict())
 def make_client(self, mock_socket_values, **kwargs):
     tracer = get_dummy_tracer()
     Pin.override(pymemcache, tracer=tracer)
     self.client = pymemcache.client.base.Client((TEST_HOST, TEST_PORT),
                                                 **kwargs)
     self.client.sock = MockSocket(list(mock_socket_values))
     return self.client
    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 setUp(self):
     super(TestRedisPatch, self).setUp()
     patch()
     r = self._get_test_client()
     r.flushall()
     Pin.override(r, service=self.TEST_SERVICE, tracer=self.tracer)
     self.r = r
示例#15
0
 def setUp(self):
     patch()
     # prevent cache effects when using Template('code...')
     jinja2.environment._spontaneous_environments.clear()
     # provide a dummy tracer
     self.tracer = get_dummy_tracer()
     Pin.override(jinja2.environment.Environment, tracer=self.tracer)
    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
示例#17
0
    def test_execute_metadata(self):
        """Metadata related to an `execute` call should be captured."""
        conn, cur = self.test_conn

        Pin.override(cur, tracer=self.test_tracer)

        with conn:
            cur.execute(
                "INSERT INTO {} (a, b) VALUES (1, 'aa');".format(TEST_TABLE))
            cur.execute('SELECT * FROM {};'.format(TEST_TABLE))

        spans = self.test_tracer.writer.pop()
        assert len(spans) == 2

        # check all the metadata
        assert spans[0].service == 'vertica'
        assert spans[0].span_type == 'sql'
        assert spans[0].name == 'vertica.query'
        assert spans[0].get_metric('db.rowcount') == -1
        query = "INSERT INTO test_table (a, b) VALUES (1, 'aa');"
        assert spans[0].resource == query
        assert spans[0].get_tag('out.host') == '127.0.0.1'
        assert spans[0].get_tag('out.port') == '5433'
        assert spans[0].get_tag('db.name') == 'docker'
        assert spans[0].get_tag('db.user') == 'dbadmin'

        assert spans[1].resource == 'SELECT * FROM test_table;'
    def _traced_session(self):
        tracer = get_dummy_tracer()
        # pin the global Cluster to test if they will conflict
        Pin(service='not-%s' % self.TEST_SERVICE).onto(Cluster)
        self.cluster = Cluster(port=CASSANDRA_CONFIG['port'])

        Pin(service=self.TEST_SERVICE, tracer=tracer).onto(self.cluster)
        return self.cluster.connect(self.TEST_KEYSPACE), tracer
示例#19
0
    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 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)
示例#21
0
    def get_tracer_and_connect(self):
        tracer = get_dummy_tracer()
        # Set a connect-level service, to check that we properly override it
        Pin(service='not-%s' % self.TEST_SERVICE).onto(mongoengine.connect)
        client = mongoengine.connect(port=MONGO_CONFIG['port'])
        Pin(service=self.TEST_SERVICE, tracer=tracer).onto(client)

        return tracer
 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 patch_conn(conn):

    tags = {t: getattr(conn, a) for t, a in CONN_ATTR_BY_TAG.items() if getattr(conn, a, '') != ''}
    pin = Pin(service='mysql', app='mysql', app_type=AppTypes.db, tags=tags)

    # grab the metadata from the conn
    wrapped = TracedConnection(conn, pin=pin)
    pin.onto(wrapped)
    return wrapped
    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_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 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()
示例#27
0
    def setUp(self):
        super(CeleryBaseTestCase, self).setUp()

        # instrument Celery and create an app with Broker and Result backends
        patch()
        self.pin = Pin(service='celery-unittest', tracer=self.tracer)
        self.app = Celery('celery.test_app',
                          broker=BROKER_URL,
                          backend=BACKEND_URL)
        # override pins to use our Dummy Tracer
        Pin.override(self.app, tracer=self.tracer)
    def test_service_name_override(self):
        client = self.make_client(
            [b'STORED\r\n', b'VALUE key 0 5\r\nvalue\r\nEND\r\n'])
        Pin.override(client, service='testsvcname')
        client.set(b'key', b'value', noreply=False)
        result = client.get(b'key')
        assert _str(result) == 'value'

        spans = self.get_spans()
        self.assertEqual(spans[0].service, 'testsvcname')
        self.assertEqual(spans[1].service, 'testsvcname')
    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
示例#30
0
    def setUp(self):
        super(TestKombuPatch, self).setUp()

        conn = kombu.Connection('amqp://*****:*****@127.0.0.1:{p}//'.format(p=self.TEST_PORT))
        conn.connect()
        producer = conn.Producer()
        Pin.override(producer, service=self.TEST_SERVICE, tracer=self.tracer)

        self.conn = conn
        self.producer = producer

        patch()