def _handle_response_or_error(span, response_or_error):
    # response_of_error should be a grpc.Future and so we expect to have
    # exception() and traceback() methods if a computation has resulted in
    # an exception being raised
    if (
        not callable(getattr(response_or_error, 'exception', None)) and
        not callable(getattr(response_or_error, 'traceback', None))
    ):
        return

    exception = response_or_error.exception()
    traceback = response_or_error.traceback()

    # pull out status code from gRPC response to use both for `grpc.status.code`
    # tag and the error type tag if the response is an exception
    status_code = to_unicode(response_or_error.code())

    if exception is not None and traceback is not None:
        if isinstance(exception, grpc.RpcError):
            # handle internal gRPC exceptions separately to get status code and
            # details as tags properly
            exc_val = to_unicode(response_or_error.details())
            span.set_tag(errors.ERROR_MSG, exc_val)
            span.set_tag(errors.ERROR_TYPE, status_code)
            span.set_tag(errors.ERROR_STACK, traceback)
        else:
            exc_type = type(exception)
            span.set_exc_info(exc_type, exception, traceback)
            status_code = to_unicode(response_or_error.code())

    span.set_tag(constants.GRPC_STATUS_CODE_KEY, status_code)
def _handle_server_exception(server_context, span):
    if server_context is not None and \
       hasattr(server_context, '_state') and \
       server_context._state is not None:
        code = to_unicode(server_context._state.code)
        details = to_unicode(server_context._state.details)
        span.error = 1
        span.set_tag(errors.ERROR_MSG, details)
        span.set_tag(errors.ERROR_TYPE, code)
 def test_to_unicode_bytearray_double_decode(self):
     #  Calling `compat.to_unicode` with an already decoded `bytearray`
     # This represents the double-decode issue, which can cause a `UnicodeEncodeError`
     #   `bytearray('\xc3\xbf').decode('utf-8').decode('utf-8')`
     res = to_unicode(bytearray(b'\xc3\xbf').decode('utf-8'))
     assert type(res) == unicode
     assert res == u'ÿ'
Ejemplo n.º 4
0
    def test_analytics_global_off_integration_default(self):
        """
        When making a request
            When an integration trace search is not set and sample rate is set and globally trace search is disabled
                We expect the root span to not include tag
        """
        # setup our test app
        @self.app.route('/hi/<name>')
        def hi(name):
            return 'hi %s' % name

        self._trace_app(self.tracer)

        with self.override_global_config(dict(analytics_enabled=False)):
            resp = self.app.get('/hi/dougie')
            assert resp.status_int == 200
            assert compat.to_unicode(resp.body) == u'hi dougie'

        root = self.get_root_span()
        self.assertIsNone(root.get_metric(ANALYTICS_SAMPLE_RATE_KEY))

        for span in self.spans:
            if span == root:
                continue
            self.assertIsNone(span.get_metric(ANALYTICS_SAMPLE_RATE_KEY))
Ejemplo n.º 5
0
    def test_analytics_global_off_integration_on(self):
        """
        When making a request
            When an integration trace search is enabled and sample rate is set and globally trace search is disabled
                We expect the root span to have the appropriate tag
        """
        # setup our test app
        @self.app.route('/hi/<name>')
        def hi(name):
            return 'hi %s' % name

        self._trace_app(self.tracer)

        with self.override_global_config(dict(analytics_enabled=False)):
            with self.override_config(
                    'bottle',
                    dict(analytics_enabled=True, analytics_sample_rate=0.5)):
                resp = self.app.get('/hi/dougie')
                assert resp.status_int == 200
                assert compat.to_unicode(resp.body) == u'hi dougie'

        root = self.get_root_span()
        root.assert_matches(
            name='bottle.request',
            metrics={
                ANALYTICS_SAMPLE_RATE_KEY: 0.5,
            },
        )

        for span in self.spans:
            if span == root:
                continue
            self.assertIsNone(span.get_metric(ANALYTICS_SAMPLE_RATE_KEY))
 def test_to_unicode_unicode_double_decode(self):
     # Calling `compat.to_unicode` on a unicode decoded string
     # This represents the double-decode issue, which can cause a `UnicodeEncodeError`
     #   `'\xc3\xbf'.decode('utf-8').decode('utf-8')`
     res = to_unicode(b'\xc3\xbf'.decode('utf-8'))
     assert type(res) == unicode
     assert res == u'ÿ'
Ejemplo n.º 7
0
    def test_200(self, query_string=''):
        if query_string:
            fqs = '?' + query_string
        else:
            fqs = ''

        # setup our test app
        @self.app.route('/hi/<name>')
        def hi(name):
            return 'hi %s' % name

        self._trace_app(self.tracer)

        # make a request
        resp = self.app.get('/hi/dougie' + fqs)
        assert resp.status_int == 200
        assert compat.to_unicode(resp.body) == u'hi dougie'
        # validate it's traced
        spans = self.tracer.writer.pop()
        assert len(spans) == 1
        s = spans[0]
        assert s.name == 'bottle.request'
        assert s.service == 'bottle-app'
        assert s.span_type == 'web'
        assert s.resource == 'GET /hi/<name>'
        assert s.get_tag('http.status_code') == '200'
        assert s.get_tag('http.method') == 'GET'
        assert s.get_tag(http.URL) == 'http://localhost:80/hi/dougie'
        if oteltrace.config.bottle.trace_query_string:
            assert s.get_tag(http.QUERY_STRING) == query_string
        else:
            assert http.QUERY_STRING not in s.meta

        services = self.tracer.writer.pop_services()
        assert services == {}
    def test_not_distributed(self):
        # setup our test app
        @self.app.route('/hi/<name>')
        def hi(name):
            return 'hi %s' % name

        self._trace_app_not_distributed(self.tracer)

        # make a request
        headers = {'x-datadog-trace-id': '123', 'x-datadog-parent-id': '456'}
        resp = self.app.get('/hi/dougie', headers=headers)
        assert resp.status_int == 200
        assert compat.to_unicode(resp.body) == u'hi dougie'

        # validate it's traced
        spans = self.tracer.writer.pop()
        assert len(spans) == 1
        s = spans[0]
        assert s.name == 'bottle.request'
        assert s.service == 'bottle-app'
        assert s.resource == 'GET /hi/<name>'
        assert s.get_tag('http.status_code') == '200'
        assert s.get_tag('http.method') == 'GET'
        # check distributed headers
        assert 123 != s.trace_id
        assert 456 != s.parent_id
def test_redis_legacy():
    # ensure the old interface isn't broken, but doesn't trace
    tracer = get_dummy_tracer()
    TracedRedisCache = get_traced_redis(tracer, 'foo')
    r = TracedRedisCache(port=REDIS_CONFIG['port'])
    r.set('a', 'b')
    got = r.get('a')
    assert compat.to_unicode(got) == 'b'
    assert not tracer.writer.pop()
Ejemplo n.º 10
0
    def test_render(self):
        # render
        t = Template('Hello ${name}!')
        self.assertEqual(t.render(name='mako'), 'Hello mako!')

        spans = self.tracer.writer.pop()
        self.assertEqual(len(spans), 1)

        self.assertEqual(spans[0].service, 'mako')
        self.assertEqual(spans[0].span_type, 'template')
        self.assertEqual(spans[0].get_tag('mako.template_name'), '<memory>')
        self.assertEqual(spans[0].name, 'mako.template.render')
        self.assertEqual(spans[0].resource, '<memory>')

        # render_unicode
        t = Template('Hello ${name}!')
        self.assertEqual(t.render_unicode(name='mako'),
                         to_unicode('Hello mako!'))
        spans = self.tracer.writer.pop()
        self.assertEqual(len(spans), 1)
        self.assertEqual(spans[0].service, 'mako')
        self.assertEqual(spans[0].span_type, 'template')
        self.assertEqual(spans[0].get_tag('mako.template_name'), '<memory>')
        self.assertEqual(spans[0].name, 'mako.template.render_unicode')
        self.assertEqual(spans[0].resource, '<memory>')

        # render_context
        t = Template('Hello ${name}!')
        buf = StringIO()
        c = Context(buf, name='mako')
        t.render_context(c)
        self.assertEqual(buf.getvalue(), 'Hello mako!')
        spans = self.tracer.writer.pop()
        self.assertEqual(len(spans), 1)
        self.assertEqual(spans[0].service, 'mako')
        self.assertEqual(spans[0].span_type, 'template')
        self.assertEqual(spans[0].get_tag('mako.template_name'), '<memory>')
        self.assertEqual(spans[0].name, 'mako.template.render_context')
        self.assertEqual(spans[0].resource, '<memory>')
Ejemplo n.º 11
0
    def test_200(self):
        # setup our test app
        @self.app.route('/hi/<name>')
        def hi(name):
            return 'hi %s' % name

        self._trace_app(self.tracer)

        # make a request
        resp = self.app.get('/hi/dougie')
        assert resp.status_int == 200
        assert compat.to_unicode(resp.body) == u'hi dougie'
        # validate it's traced
        spans = self.tracer.writer.pop()
        assert len(spans) == 1
        s = spans[0]
        assert s.name == 'bottle.request'
        assert s.service == 'bottle-app'
        assert s.resource == 'GET /hi/<name>'
        assert s.get_tag('http.status_code') == '200'
        assert s.get_tag('http.method') == 'GET'

        services = self.tracer.writer.pop_services()
        assert services == {}
 def test_to_unicode_unicode_encoded(self):
     # Calling `compat.to_unicode` on a unicode encoded string
     res = to_unicode(b'\xc3\xbf')
     assert type(res) == unicode
     assert res == u'ÿ'
 def test_to_unicode_unicode_string(self):
     # Calling `compat.to_unicode` on a unicode string
     res = to_unicode(u'ÿ')
     assert type(res) == unicode
     assert res == u'ÿ'
 def test_to_unicode_bytearray(self):
     # Calling `compat.to_unicode` with a `bytearray` containing unicode
     res = to_unicode(bytearray(b'\xc3\xbf'))
     assert type(res) == unicode
     assert res == u'ÿ'
 def test_to_unicode_non_string(self):
     #  Calling `compat.to_unicode` on non-string types
     assert to_unicode(1) == u'1'
     assert to_unicode(True) == u'True'
     assert to_unicode(None) == u'None'
     assert to_unicode(dict(key='value')) == u'{\'key\': \'value\'}'
Ejemplo n.º 16
0
def traced_wsgi_app(pin, wrapped, instance, args, kwargs):
    """
    Wrapper for flask.app.Flask.wsgi_app

    This wrapper is the starting point for all requests.
    """
    # DEV: This is safe before this is the args for a WSGI handler
    #   https://www.python.org/dev/peps/pep-3333/
    environ, start_response = args

    # Create a werkzeug request from the `environ` to make interacting with it easier
    # DEV: This executes before a request context is created
    request = werkzeug.Request(environ)

    # Configure distributed tracing
    if config.flask.get('distributed_tracing_enabled', False):
        propagator = HTTPPropagator()
        context = propagator.extract(request.headers)
        # Only need to activate the new context if something was propagated
        if context.trace_id:
            pin.tracer.context_provider.activate(context)

    # Default resource is method and path:
    #   GET /
    #   POST /save
    # We will override this below in `traced_dispatch_request` when we have a `RequestContext` and possibly a url rule
    resource = u'{} {}'.format(request.method, request.path)
    with pin.tracer.trace('flask.request',
                          service=pin.service,
                          resource=resource,
                          span_type=http.TYPE) as s:
        # set analytics sample rate with global config enabled
        s.set_tag(
            ANALYTICS_SAMPLE_RATE_KEY,
            config.flask.get_analytics_sample_rate(use_global_config=True))

        s.set_tag(FLASK_VERSION, flask_version_str)

        # Wrap the `start_response` handler to extract response code
        # DEV: We tried using `Flask.finalize_request`, which seemed to work, but gave us hell during tests
        # DEV: The downside to using `start_response` is we do not have a `Flask.Response` object here,
        #   only `status_code`, and `headers` to work with
        #   On the bright side, this works in all versions of Flask (or any WSGI app actually)
        def _wrap_start_response(func):
            def traced_start_response(status_code, headers):
                code, _, _ = status_code.partition(' ')
                try:
                    code = int(code)
                except ValueError:
                    pass

                # Override root span resource name to be `<method> 404` for 404 requests
                # DEV: We do this because we want to make it easier to see all unknown requests together
                #      Also, we do this to reduce the cardinality on unknown urls
                # DEV: If we have an endpoint or url rule tag, then we don't need to do this,
                #      we still want `GET /product/<int:product_id>` grouped together,
                #      even if it is a 404
                if not s.get_tag(FLASK_ENDPOINT) and not s.get_tag(
                        FLASK_URL_RULE):
                    s.resource = u'{} {}'.format(request.method, code)

                s.set_tag(http.STATUS_CODE, code)
                if 500 <= code < 600:
                    s.error = 1
                elif code in config.flask.get('extra_error_codes', set()):
                    s.error = 1
                return func(status_code, headers)

            return traced_start_response

        start_response = _wrap_start_response(start_response)

        # DEV: We set response status code in `_wrap_start_response`
        # DEV: Use `request.base_url` and not `request.url` to keep from leaking any query string parameters
        s.set_tag(http.URL, request.base_url)
        s.set_tag(http.METHOD, request.method)
        if config.flask.trace_query_string:
            s.set_tag(http.QUERY_STRING,
                      compat.to_unicode(request.query_string))

        return wrapped(environ, start_response)
 def test_to_unicode_string(self):
     # Calling `compat.to_unicode` on a non-unicode string
     res = to_unicode(b'test')
     assert type(res) == unicode
     assert res == 'test'