Exemplo n.º 1
0
    def test_b3_extract(self):
        tracer = get_dummy_tracer()
        tracer.configure(http_propagator=B3HTTPPropagator)

        headers = {
            "x-b3-traceid": "4d2",
            "x-b3-spanid": "162e",
            "x-b3-sampled": "1",
        }

        propagator = HTTPPropagator()
        context = propagator.extract(headers)
        tracer.context_provider.activate(context)

        with tracer.trace("local_root_span") as span:
            assert span.trace_id == 1234
            assert span.parent_id == 5678
            assert span.context.sampling_priority == AUTO_KEEP

        headers = {
            "x-b3-traceid": "4d2",
            "x-b3-spanid": "162e",
            "x-b3-sampled": "0",
        }

        propagator = HTTPPropagator()
        context = propagator.extract(headers)
        tracer.context_provider.activate(context)

        with tracer.trace("local_root_span") as span:
            assert span.context.sampling_priority == AUTO_REJECT
Exemplo n.º 2
0
def test_extract_bad_values(trace_id, parent_span_id, sampling_priority,
                            dd_origin):
    headers = dict()
    wsgi_headers = dict()

    if trace_id is not NOT_SET:
        headers[HTTP_HEADER_TRACE_ID] = trace_id
        wsgi_headers[get_wsgi_header(HTTP_HEADER_TRACE_ID)] = trace_id
    if parent_span_id is not NOT_SET:
        headers[HTTP_HEADER_PARENT_ID] = parent_span_id
        wsgi_headers[get_wsgi_header(HTTP_HEADER_PARENT_ID)] = parent_span_id
    if sampling_priority is not NOT_SET:
        headers[HTTP_HEADER_SAMPLING_PRIORITY] = sampling_priority
        wsgi_headers[get_wsgi_header(
            HTTP_HEADER_SAMPLING_PRIORITY)] = sampling_priority
    if dd_origin is not NOT_SET:
        headers[HTTP_HEADER_ORIGIN] = dd_origin
        wsgi_headers[get_wsgi_header(HTTP_HEADER_ORIGIN)] = dd_origin

    # x-datadog-*headers
    context = HTTPPropagator.extract(headers)
    assert context.trace_id is None
    assert context.span_id is None
    assert context.sampling_priority is None
    assert context.dd_origin is None

    # HTTP_X_DATADOG_* headers
    context = HTTPPropagator.extract(wsgi_headers)
    assert context.trace_id is None
    assert context.span_id is None
    assert context.sampling_priority is None
    assert context.dd_origin is None
Exemplo n.º 3
0
    def extract(carrier):
        # type: (Dict[str, str]) -> SpanContext
        """Extract a span context from a carrier.

        :class:`ddtrace.propagation.http.HTTPPropagator` is used to extract
        ddtracer supported fields into a `ddtrace.Context` context which is
        combined with new logic to extract the baggage which is returned in an
        OpenTracing compatible span context.

        :param carrier: carrier to extract from.

        :return: extracted span context.
        """
        if not isinstance(carrier, dict):
            raise InvalidCarrierException(
                "propagator expects carrier to be a dict")

        ddspan_ctx = DDHTTPPropagator.extract(carrier)

        # if the dd propagator fails then it will return a new empty span
        # context (with trace_id=None), we however want to raise an exception
        # if this occurs.
        if not ddspan_ctx.trace_id:
            raise SpanContextCorruptedException(
                "failed to extract span context")

        baggage = {}
        for key in carrier:
            if key.startswith(HTTP_BAGGAGE_PREFIX):
                baggage[key[HTTP_BAGGAGE_PREFIX_LEN:]] = carrier[key]

        return SpanContext(ddcontext=ddspan_ctx, baggage=baggage)
Exemplo n.º 4
0
    def process_request(self, req, resp):
        if self._distributed_tracing:
            # Falcon uppercases all header names.
            headers = dict((k.lower(), v) for k, v in iteritems(req.headers))
            propagator = HTTPPropagator()
            context = propagator.extract(headers)
            # Only activate the new context if there was a trace id extracted
            if context.trace_id:
                self.tracer.context_provider.activate(context)

        span = self.tracer.trace(
            "falcon.request",
            service=self.service,
            span_type=httpx.TYPE,
        )

        # Configure trace search sample rate
        if config.falcon.event_sample_rate is not None:
            span.set_tag(EVENT_SAMPLE_RATE_KEY,
                         config.falcon.event_sample_rate)

        span.set_tag(httpx.METHOD, req.method)
        span.set_tag(httpx.URL, req.url)

        # Note: any request header set after this line will not be stored in the span
        store_request_headers(req.headers, span, config.falcon)
Exemplo n.º 5
0
    def process_request(self, req, resp):
        if self._distributed_tracing:
            # Falcon uppercases all header names.
            headers = dict((k.lower(), v) for k, v in iteritems(req.headers))
            propagator = HTTPPropagator()
            context = propagator.extract(headers)
            # Only activate the new context if there was a trace id extracted
            if context.trace_id:
                self.tracer.context_provider.activate(context)

        span = self.tracer.trace(
            'falcon.request',
            service=self.service,
            span_type=SpanTypes.WEB,
        )

        # set analytics sample rate with global config enabled
        span.set_tag(
            ANALYTICS_SAMPLE_RATE_KEY,
            config.falcon.get_analytics_sample_rate(use_global_config=True)
        )

        span.set_tag(httpx.METHOD, req.method)
        span.set_tag(httpx.URL, req.url)
        if config.falcon.trace_query_string:
            span.set_tag(httpx.QUERY_STRING, req.query_string)

        # Note: any request header set after this line will not be stored in the span
        store_request_headers(req.headers, span, config.falcon)
Exemplo n.º 6
0
    def test_WSGI_extract(self):
        """Ensure we support the WSGI formatted headers as well."""
        tracer = DummyTracer()

        headers = {
            "HTTP_X_DATADOG_TRACE_ID": "1234",
            "HTTP_X_DATADOG_PARENT_ID": "5678",
            "HTTP_X_DATADOG_SAMPLING_PRIORITY": "1",
            "HTTP_X_DATADOG_ORIGIN": "synthetics",
            "HTTP_X_DATADOG_TAGS": "_dd.p.test=value,any=tag",
        }

        context = HTTPPropagator.extract(headers)
        tracer.context_provider.activate(context)

        with tracer.trace("local_root_span") as span:
            assert span.trace_id == 1234
            assert span.parent_id == 5678
            assert span.context.sampling_priority == 1
            assert span.context.dd_origin == "synthetics"
            assert span.context._meta == {
                "_dd.origin": "synthetics",
                "_dd.p.test": "value",
                "any": "tag",
            }
Exemplo n.º 7
0
    def process_request(self, req, resp):
        if self._distributed_tracing:
            # Falcon uppercases all header names.
            headers = dict((k.lower(), v) for k, v in iteritems(req.headers))
            context = HTTPPropagator.extract(headers)
            # Only activate the new context if there was a trace id extracted
            if context.trace_id:
                self.tracer.context_provider.activate(context)

        span = self.tracer.trace(
            "falcon.request",
            service=self.service,
            span_type=SpanTypes.WEB,
        )
        span.set_tag(SPAN_MEASURED_KEY)

        # set analytics sample rate with global config enabled
        span.set_tag(
            ANALYTICS_SAMPLE_RATE_KEY,
            config.falcon.get_analytics_sample_rate(use_global_config=True))

        trace_utils.set_http_meta(span,
                                  config.falcon,
                                  method=req.method,
                                  url=req.url,
                                  query=req.query_string,
                                  request_headers=req.headers)
Exemplo n.º 8
0
def patch_handle_request(wrapped, instance, args, kwargs):
    """Wrapper for Sanic.handle_request"""
    request = kwargs.get("request", args[0])
    write_callback = kwargs.get("write_callback", args[1])
    stream_callback = kwargs.get("stream_callback", args[2])

    resource = "{} {}".format(request.method, request.path)

    headers = request.headers.copy()

    if config.sanic.distributed_tracing:
        propagator = HTTPPropagator()
        context = propagator.extract(headers)
        if context.trace_id:
            ddtrace.tracer.context_provider.activate(context)

    span = ddtrace.tracer.trace(
        "sanic.request", service=config.sanic.service, resource=resource, span_type=SpanTypes.WEB
    )
    sample_rate = config.sanic.get_analytics_sample_rate(use_global_config=True)
    if sample_rate is not None:
        span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, sample_rate)

    tags = _extract_tags_from_request(request=request)
    span.set_tags(tags)

    store_request_headers(headers, span, config.sanic)

    if write_callback is not None:
        write_callback = _wrap_response_callback(span, write_callback)
    if stream_callback is not None:
        stream_callback = _wrap_response_callback(span, stream_callback)

    return wrapped(request, write_callback, stream_callback, **kwargs)
Exemplo n.º 9
0
    async def __call__(self, scope, receive, send):
        if scope["type"] != "http":
            return await self.app(scope, receive, send)

        headers = _extract_headers(scope)

        if self.integration_config.distributed_tracing:
            propagator = HTTPPropagator()
            context = propagator.extract(headers)
            if context.trace_id:
                self.tracer.context_provider.activate(context)

        resource = "{} {}".format(scope["method"], scope["path"])

        span = self.tracer.trace(
            name=self.integration_config.get("request_span_name",
                                             "asgi.request"),
            service=trace_utils.int_service(None, self.integration_config),
            resource=resource,
            span_type=SpanTypes.HTTP,
        )

        if self.span_modifier:
            self.span_modifier(span, scope)

        sample_rate = self.integration_config.get_analytics_sample_rate(
            use_global_config=True)
        if sample_rate is not None:
            span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, sample_rate)

        tags = _extract_tags_from_scope(scope, self.integration_config)
        span.set_tags(tags)

        store_request_headers(headers, span, self.integration_config)

        async def wrapped_send(message):
            if span and message.get("type") == "http.response.start":
                if "status" in message:
                    status_code = message["status"]
                    span.set_tag(http.STATUS_CODE, status_code)
                    if 500 <= int(status_code) < 600:
                        span.error = 1

                if "headers" in message:
                    store_response_headers(message["headers"], span,
                                           self.integration_config)

            return await send(message)

        try:
            return await self.app(scope, receive, wrapped_send)
        except Exception as exc:
            (exc_type, exc_val, exc_tb) = sys.exc_info()
            span.set_exc_info(exc_type, exc_val, exc_tb)
            self.handle_exception_span(exc, span)
            reraise(exc_type, exc_val, exc_tb)
        finally:
            span.finish()
Exemplo n.º 10
0
    async def __call__(self, scope: Scope, receive: Receive,
                       send: Send) -> None:
        if scope["type"] != "http":
            await self.app(scope, receive, send)
            return

        request = Request(scope=scope, receive=receive)

        if self._distributed_tracing:
            propagator = HTTPPropagator()
            context = propagator.extract(request.headers)
            if context.trace_id:
                self.tracer.context_provider.activate(context)

        resource = "%s %s" % (request.method, request.url.path)
        span = self.tracer.trace(
            name="asgi.request",
            service=self.service,
            resource=resource,
            span_type=http_tags.TYPE,
        )

        span.set_tag(
            ANALYTICS_SAMPLE_RATE_KEY,
            config.asgi.get_analytics_sample_rate(use_global_config=True),
        )
        span.set_tag(http_tags.METHOD, request.method)
        span.set_tag(http_tags.URL, str(request.url))
        if config.asgi.trace_query_string:
            span.set_tag(http_tags.QUERY_STRING, request.url.query)

        # NOTE: any request header set in the future will not be stored in the span.
        store_request_headers(request.headers, span, config.asgi)

        async def send_with_tracing(message: Message) -> None:
            span = self.tracer.current_span()

            if not span:
                # Unexpected.
                await send(message)
                return

            if message["type"] == "http.response.start":
                status_code: int = message["status"]
                response_headers = Headers(raw=message["headers"])
                store_response_headers(response_headers, span, config.asgi)
                span.set_tag(http_tags.STATUS_CODE, str(status_code))

            await send(message)

        try:
            await self.app(scope, receive, send_with_tracing)
        except BaseException as exc:
            span.set_traceback()
            raise exc from None
        finally:
            span.finish()
Exemplo n.º 11
0
def activate_distributed_headers(tracer, int_config, request_headers=None):
    """
    Helper for activating a distributed trace headers' context if enabled in integration config.
    """
    int_config = int_config or {}

    if int_config.get("distributed_tracing_enabled", False):
        context = HTTPPropagator.extract(request_headers)
        # Only need to activate the new context if something was propagated
        if context.trace_id:
            tracer.context_provider.activate(context)
Exemplo n.º 12
0
async def patch_handle_request(wrapped, instance, args, kwargs):
    """Wrapper for Sanic.handle_request"""
    request = kwargs.get("request", args[0])
    write_callback = kwargs.get("write_callback", args[1])
    stream_callback = kwargs.get("stream_callback", args[2])

    if request.scheme not in ("http", "https"):
        return await wrapped(request, write_callback, stream_callback,
                             **kwargs)

    resource = "{} {}".format(request.method, _get_path(request))

    headers = request.headers.copy()

    if config.sanic.distributed_tracing:
        propagator = HTTPPropagator()
        context = propagator.extract(headers)
        if context.trace_id:
            ddtrace.tracer.context_provider.activate(context)

    span = ddtrace.tracer.trace(
        "sanic.request",
        service=trace_utils.int_service(None, config.sanic),
        resource=resource,
        span_type=SpanTypes.WEB,
    )
    sample_rate = config.sanic.get_analytics_sample_rate(
        use_global_config=True)
    if sample_rate is not None:
        span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, sample_rate)

    method = request.method
    url = "{scheme}://{host}{path}".format(scheme=request.scheme,
                                           host=request.host,
                                           path=request.path)
    query_string = request.query_string
    if isinstance(query_string, bytes):
        query_string = query_string.decode()
    trace_utils.set_http_meta(span,
                              config.sanic,
                              method=method,
                              url=url,
                              query=query_string,
                              request_headers=headers)

    if write_callback is not None:
        write_callback = _wrap_response_callback(span, write_callback)
    if stream_callback is not None:
        stream_callback = _wrap_response_callback(span, stream_callback)

    return await wrapped(request, write_callback, stream_callback, **kwargs)
Exemplo n.º 13
0
    def process_request(self, req, resp):
        if self._distributed_tracing:
            # Falcon uppercases all header names.
            headers = dict((k.lower(), v) for k, v in iteritems(req.headers))
            propagator = HTTPPropagator()
            context = propagator.extract(headers)
            self.tracer.context_provider.activate(context)

        span = self.tracer.trace(
            "falcon.request",
            service=self.service,
            span_type=httpx.TYPE,
        )

        span.set_tag(httpx.METHOD, req.method)
        span.set_tag(httpx.URL, req.url)
Exemplo n.º 14
0
    def process_request(self, req, resp):
        if self._distributed_tracing:
            # Falcon uppercases all header names.
            headers = dict((k.lower(), v) for k, v in iteritems(req.headers))
            propagator = HTTPPropagator()
            context = propagator.extract(headers)
            self.tracer.context_provider.activate(context)

        span = self.tracer.trace(
            "falcon.request",
            service=self.service,
            span_type=httpx.TYPE,
        )

        span.set_tag(httpx.METHOD, req.method)
        span.set_tag(httpx.URL, req.url)
Exemplo n.º 15
0
    def test_extract(self):
        tracer = get_dummy_tracer()

        headers = {
            HTTP_HEADER_TRACE_ID: '1234',
            HTTP_HEADER_PARENT_ID: '5678',
            HTTP_HEADER_SAMPLING_PRIORITY: '1',
        }

        propagator = HTTPPropagator()
        context = propagator.extract(headers)
        tracer.context_provider.activate(context)

        with tracer.trace("local_root_span") as span:
            eq_(span.trace_id, 1234)
            eq_(span.parent_id, 5678)
Exemplo n.º 16
0
    def test_extract(self):
        tracer = get_dummy_tracer()

        headers = {
            HTTP_HEADER_TRACE_ID: '1234',
            HTTP_HEADER_PARENT_ID: '5678',
            HTTP_HEADER_SAMPLING_PRIORITY: '1',
        }

        propagator = HTTPPropagator()
        context = propagator.extract(headers)
        tracer.context_provider.activate(context)

        with tracer.trace("local_root_span") as span:
            eq_(span.trace_id, 1234)
            eq_(span.parent_id, 5678)
Exemplo n.º 17
0
    def test_extract(self):
        tracer = get_dummy_tracer()

        headers = {
            "x-datadog-trace-id": "1234",
            "x-datadog-parent-id": "5678",
            "x-datadog-sampling-priority": "1",
        }

        propagator = HTTPPropagator()
        context = propagator.extract(headers)
        tracer.context_provider.activate(context)

        with tracer.trace("local_root_span") as span:
            eq_(span.trace_id, 1234)
            eq_(span.parent_id, 5678)
            eq_(span.context.sampling_priority, 1)
Exemplo n.º 18
0
    def test_extract(self):
        tracer = DummyTracer()

        headers = {
            "x-datadog-trace-id": "1234",
            "x-datadog-parent-id": "5678",
            "x-datadog-sampling-priority": "1",
            "x-datadog-origin": "synthetics",
        }

        context = HTTPPropagator.extract(headers)
        tracer.context_provider.activate(context)

        with tracer.trace("local_root_span") as span:
            assert span.trace_id == 1234
            assert span.parent_id == 5678
            assert span.context.sampling_priority == 1
            assert span.context.dd_origin == "synthetics"
Exemplo n.º 19
0
def activate_distributed_headers(tracer,
                                 int_config=None,
                                 request_headers=None,
                                 override=None):
    # type: (Tracer, Optional[IntegrationConfig], Optional[Dict[str, str]], Optional[bool]) -> None
    """
    Helper for activating a distributed trace headers' context if enabled in integration config.
    int_config will be used to check if distributed trace headers context will be activated, but
    override will override whatever value is set in int_config if passed any value other than None.
    """
    if override is False:
        return None

    if override or (int_config and distributed_tracing_enabled(int_config)):
        context = HTTPPropagator.extract(request_headers)
        # Only need to activate the new context if something was propagated
        if context.trace_id:
            tracer.context_provider.activate(context)
Exemplo n.º 20
0
    def test_WSGI_extract(self):
        """Ensure we support the WSGI formatted headers as well."""
        tracer = get_dummy_tracer()

        headers = {
            "HTTP_X_DATADOG_TRACE_ID": "1234",
            "HTTP_X_DATADOG_PARENT_ID": "5678",
            "HTTP_X_DATADOG_SAMPLING_PRIORITY": "1",
        }

        propagator = HTTPPropagator()
        context = propagator.extract(headers)
        tracer.context_provider.activate(context)

        with tracer.trace("local_root_span") as span:
            eq_(span.trace_id, 1234)
            eq_(span.parent_id, 5678)
            eq_(span.context.sampling_priority, 1)
Exemplo n.º 21
0
    def test_extract(self):
        tracer = get_dummy_tracer()

        headers = {
            'x-datadog-trace-id': '1234',
            'x-datadog-parent-id': '5678',
            'x-datadog-sampling-priority': '1',
            'x-datadog-origin': 'synthetics',
        }

        propagator = HTTPPropagator()
        context = propagator.extract(headers)
        tracer.context_provider.activate(context)

        with tracer.trace('local_root_span') as span:
            assert span.trace_id == 1234
            assert span.parent_id == 5678
            assert span.context.sampling_priority == 1
            assert span.context._dd_origin == 'synthetics'
Exemplo n.º 22
0
    def test_WSGI_extract(self):
        """Ensure we support the WSGI formatted headers as well."""
        tracer = get_dummy_tracer()

        headers = {
            'HTTP_X_DATADOG_TRACE_ID': '1234',
            'HTTP_X_DATADOG_PARENT_ID': '5678',
            'HTTP_X_DATADOG_SAMPLING_PRIORITY': '1',
            'HTTP_X_DATADOG_ORIGIN': 'synthetics',
        }

        propagator = HTTPPropagator()
        context = propagator.extract(headers)
        tracer.context_provider.activate(context)

        with tracer.trace('local_root_span') as span:
            assert span.trace_id == 1234
            assert span.parent_id == 5678
            assert span.context.sampling_priority == 1
            assert span.context._dd_origin == 'synthetics'
Exemplo n.º 23
0
    def test_extract_invalid_tags(self):
        # Malformed tags do not fail to extract the rest of the context
        tracer = DummyTracer()

        headers = {
            "x-datadog-trace-id": "1234",
            "x-datadog-parent-id": "5678",
            "x-datadog-sampling-priority": "1",
            "x-datadog-origin": "synthetics",
            "x-datadog-tags": "malformed=,=tags,",
        }

        context = HTTPPropagator.extract(headers)
        tracer.context_provider.activate(context)

        with tracer.trace("local_root_span") as span:
            assert span.trace_id == 1234
            assert span.parent_id == 5678
            assert span.context.sampling_priority == 1
            assert span.context.dd_origin == "synthetics"
            assert span.context._meta == {"_dd.origin": "synthetics"}
Exemplo n.º 24
0
    async def __call__(self, scope, receive, send):
        if scope["type"] != "http":
            return await self.app(scope, receive, send)

        headers = _extract_headers(scope)

        if self.integration_config.distributed_tracing:
            context = HTTPPropagator.extract(headers)
            if context.trace_id:
                self.tracer.context_provider.activate(context)

        resource = "{} {}".format(scope["method"], scope["path"])

        span = self.tracer.trace(
            name=self.integration_config.get("request_span_name",
                                             "asgi.request"),
            service=trace_utils.int_service(None, self.integration_config),
            resource=resource,
            span_type=SpanTypes.WEB,
        )

        if self.span_modifier:
            self.span_modifier(span, scope)

        sample_rate = self.integration_config.get_analytics_sample_rate(
            use_global_config=True)
        if sample_rate is not None:
            span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, sample_rate)

        method = scope.get("method")
        server = scope.get("server")
        if server and len(server) == 2:
            port = server[1]
            server_host = server[0] + (":" + str(port) if port is not None
                                       and port != 80 else "")
            full_path = scope.get("root_path", "") + scope.get("path", "")
            url = scope.get("scheme", "http") + "://" + server_host + full_path
        else:
            url = None

        if self.integration_config.trace_query_string:
            query_string = scope.get("query_string")
            if len(query_string) > 0:
                query_string = bytes_to_str(query_string)
        else:
            query_string = None

        trace_utils.set_http_meta(span,
                                  self.integration_config,
                                  method=method,
                                  url=url,
                                  query=query_string,
                                  request_headers=headers)

        tags = _extract_versions_from_scope(scope, self.integration_config)
        span.set_tags(tags)

        async def wrapped_send(message):
            if span and message.get(
                    "type") == "http.response.start" and "status" in message:
                status_code = message["status"]
            else:
                status_code = None

            if "headers" in message:
                response_headers = message["headers"]
            else:
                response_headers = None

            trace_utils.set_http_meta(span,
                                      self.integration_config,
                                      status_code=status_code,
                                      response_headers=response_headers)

            return await send(message)

        try:
            return await self.app(scope, receive, wrapped_send)
        except Exception as exc:
            (exc_type, exc_val, exc_tb) = sys.exc_info()
            span.set_exc_info(exc_type, exc_val, exc_tb)
            self.handle_exception_span(exc, span)
            reraise(exc_type, exc_val, exc_tb)
        finally:
            span.finish()
Exemplo n.º 25
0
class HTTPPropagator(Propagator):
    """OpenTracing compatible HTTP_HEADER and TEXT_MAP format propagator.

    `HTTPPropagator` provides compatibility by using existing OpenTracing
    compatible methods from the ddtracer along with new logic supporting the
    outstanding OpenTracing-defined functionality.
    """

    __slots__ = ['_dd_propagator']

    def __init__(self):
        self._dd_propagator = DDHTTPPropagator()

    def inject(self, span_context, carrier):
        """Inject a span context into a carrier.

        *span_context* is injected into the carrier by first using an
        :class:`ddtrace.propagation.http.HTTPPropagator` to inject the ddtracer
        specific fields.

        Then the baggage is injected into *carrier*.

        :param span_context: span context to inject.

        :param carrier: carrier to inject into.
        """
        if not isinstance(carrier, dict):
            raise InvalidCarrierException(
                'propagator expects carrier to be a dict')

        self._dd_propagator.inject(span_context._dd_context, carrier)

        # Add the baggage
        if span_context.baggage is not None:
            for key in span_context.baggage:
                carrier[HTTP_BAGGAGE_PREFIX + key] = span_context.baggage[key]

    def extract(self, carrier):
        """Extract a span context from a carrier.

        :class:`ddtrace.propagation.http.HTTPPropagator` is used to extract
        ddtracer supported fields into a `ddtrace.Context` context which is
        combined with new logic to extract the baggage which is returned in an
        OpenTracing compatible span context.

        :param carrier: carrier to extract from.

        :return: extracted span context.
        """
        if not isinstance(carrier, dict):
            raise InvalidCarrierException(
                'propagator expects carrier to be a dict')

        ddspan_ctx = self._dd_propagator.extract(carrier)

        # if the dd propagator fails then it will return a new empty span
        # context (with trace_id=None), we however want to raise an exception
        # if this occurs.
        if not ddspan_ctx.trace_id:
            raise SpanContextCorruptedException(
                'failed to extract span context')

        baggage = {}
        for key in carrier:
            if key.startswith(HTTP_BAGGAGE_PREFIX):
                baggage[key[HTTP_BAGGAGE_PREFIX_LEN:]] = carrier[key]

        return SpanContext(ddcontext=ddspan_ctx, baggage=baggage)
Exemplo n.º 26
0
    async def __call__(self, scope: Scope, receive: Receive,
                       send: Send) -> None:
        scope["ddtrace_asgi.tracer"] = self.tracer

        if scope["type"] != "http":
            await self.app(scope, receive, send)
            return

        request = Request(scope=scope, receive=receive)
        try:
            request_headers = request.headers
            method = request.method
            url = request.url
        except KeyError:
            # ASGI message is invalid - most likely missing the 'headers' or 'method'
            # fields.
            await self.app(scope, receive, send)
            return

        # Make sure we don't use potentially unsafe request attributes after this point.
        del request

        if self._distributed_tracing:
            propagator = HTTPPropagator()
            context = propagator.extract(request_headers)
            if context.trace_id:
                self.tracer.context_provider.activate(context)

        resource = "%s %s" % (method, url.path)
        span = self.tracer.trace(
            name="asgi.request",
            service=self.service,
            resource=resource,
            span_type=http_tags.TYPE,
        )

        span.set_tag(
            ANALYTICS_SAMPLE_RATE_KEY,
            config.asgi.get_analytics_sample_rate(use_global_config=True),
        )
        span.set_tag(http_tags.METHOD, method)
        span.set_tag(http_tags.URL, str(url))
        if config.asgi.get("trace_query_string"):
            span.set_tag(http_tags.QUERY_STRING, url.query)

        span.set_tags(self.tags)

        # NOTE: any request header set in the future will not be stored in the span.
        store_request_headers(request_headers, span, config.asgi)

        async def send_with_tracing(message: Message) -> None:
            span = self.tracer.current_span()

            if span and message.get("type") == "http.response.start":
                if "status" in message:
                    status_code: int = message["status"]
                    span.set_tag(http_tags.STATUS_CODE, str(status_code))
                if "headers" in message:
                    response_headers = Headers(raw=message["headers"])
                    store_response_headers(response_headers, span, config.asgi)

            await send(message)

        try:
            await self.app(scope, receive, send_with_tracing)
        except BaseException as exc:
            span.set_traceback()
            raise exc from None
        finally:
            span.finish()