Ejemplo n.º 1
0
def _wrap_send(func, instance, args, kwargs):
    """Trace the `Session.send` instance method"""
    # TODO[manu]: we already offer a way to provide the Global Tracer
    # and is ddtrace.tracer; it's used only inside our tests and can
    # be easily changed by providing a TracingTestCase that sets common
    # tracing functionalities.
    tracer = getattr(instance, "datadog_tracer", ddtrace.tracer)

    # skip if tracing is not enabled
    if not tracer.enabled:
        return func(*args, **kwargs)

    request = kwargs.get("request") or args[0]
    if not request:
        return func(*args, **kwargs)

    # sanitize url of query
    parsed_uri = parse.urlparse(request.url)
    hostname = parsed_uri.hostname
    if parsed_uri.port:
        hostname = "{}:{}".format(hostname, parsed_uri.port)
    sanitized_url = parse.urlunparse((
        parsed_uri.scheme,
        parsed_uri.netloc,
        parsed_uri.path,
        parsed_uri.params,
        None,  # drop parsed_uri.query
        parsed_uri.fragment,
    ))

    with tracer.trace("requests.request", span_type=SpanTypes.HTTP) as span:
        span.set_tag(SPAN_MEASURED_KEY)
        # update the span service name before doing any action
        span.service = _extract_service_name(instance, span, hostname=hostname)

        # Configure trace search sample rate
        # DEV: analytics enabled on per-session basis
        cfg = config.get_from(instance)
        analytics_enabled = cfg.get("analytics_enabled")
        if analytics_enabled:
            span.set_tag(ANALYTICS_SAMPLE_RATE_KEY,
                         cfg.get("analytics_sample_rate", True))

        # propagate distributed tracing headers
        if cfg.get("distributed_tracing"):
            propagator = HTTPPropagator()
            propagator.inject(span.context, request.headers)

        # Storing request headers in the span
        store_request_headers(request.headers, span, config.requests)

        response = None
        try:
            response = func(*args, **kwargs)

            # Storing response headers in the span. Note that response.headers is not a dict, but an iterable
            # requests custom structure, that we convert to a dict
            if hasattr(response, "headers"):
                store_response_headers(dict(response.headers), span,
                                       config.requests)
            return response
        finally:
            try:
                span.set_tag(http.METHOD, request.method.upper())
                span.set_tag(http.URL, sanitized_url)
                if config.requests.trace_query_string:
                    span.set_tag(http.QUERY_STRING, parsed_uri.query)
                if response is not None:
                    span.set_tag(http.STATUS_CODE, response.status_code)
                    # `span.error` must be an integer
                    span.error = int(500 <= response.status_code)
                    # Storing response headers in the span.
                    # Note that response.headers is not a dict, but an iterable
                    # requests custom structure, that we convert to a dict
                    response_headers = dict(getattr(response, "headers", {}))
                    store_response_headers(response_headers, span,
                                           config.requests)
            except Exception:
                log.debug("requests: error adding tags", exc_info=True)
Ejemplo n.º 2
0
def _wrap_send(func, instance, args, kwargs):
    """Trace the `Session.send` instance method"""
    # TODO[manu]: we already offer a way to provide the Global Tracer
    # and is ddtrace.tracer; it's used only inside our tests and can
    # be easily changed by providing a TracingTestCase that sets common
    # tracing functionalities.
    tracer = getattr(instance, 'datadog_tracer', ddtrace.tracer)

    # skip if tracing is not enabled
    if not tracer.enabled:
        return func(*args, **kwargs)

    request = kwargs.get('request') or args[0]
    if not request:
        return func(*args, **kwargs)

    # sanitize url of query
    parsed_uri = parse.urlparse(request.url)
    hostname = parsed_uri.hostname
    if parsed_uri.port:
        hostname = '{}:{}'.format(hostname, parsed_uri.port)
    sanitized_url = parse.urlunparse((
        parsed_uri.scheme,
        parsed_uri.netloc,
        parsed_uri.path,
        parsed_uri.params,
        None,  # drop parsed_uri.query
        parsed_uri.fragment
    ))

    with tracer.trace('requests.request', span_type=http.TYPE) as span:
        # update the span service name before doing any action
        span.service = _extract_service_name(instance, span, hostname=hostname)

        # propagate distributed tracing headers
        if config.get_from(instance).get('distributed_tracing'):
            propagator = HTTPPropagator()
            propagator.inject(span.context, request.headers)

        # Storing request headers in the span
        store_request_headers(request.headers, span, config.requests)

        response = None
        try:
            response = func(*args, **kwargs)

            # Storing response headers in the span. Note that response.headers is not a dict, but an iterable
            # requests custom structure, that we convert to a dict
            if hasattr(response, 'headers'):
                store_response_headers(dict(response.headers), span, config.requests)
            return response
        finally:
            try:
                span.set_tag(http.METHOD, request.method.upper())
                span.set_tag(http.URL, sanitized_url)
                if response is not None:
                    span.set_tag(http.STATUS_CODE, response.status_code)
                    # `span.error` must be an integer
                    span.error = int(500 <= response.status_code)
                    # Storing response headers in the span.
                    # Note that response.headers is not a dict, but an iterable
                    # requests custom structure, that we convert to a dict
                    response_headers = dict(getattr(response, 'headers', {}))
                    store_response_headers(response_headers, span, config.requests)
            except Exception:
                log.debug("requests: error adding tags", exc_info=True)
Ejemplo n.º 3
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.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

        trace_utils.set_http_meta(span,
                                  self.integration_config,
                                  method=method,
                                  url=url)

        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" and "status" in message:
                status_code = message["status"]
            else:
                status_code = None
            trace_utils.set_http_meta(span,
                                      self.integration_config,
                                      status_code=status_code)

            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()
Ejemplo n.º 4
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()
Ejemplo n.º 5
0
 def test_it_does_not_break_if_headers_are_not_a_dict(
         self, span, integration_config):
     store_request_headers(list(), span, integration_config)
     store_response_headers(list(), span, integration_config)
Ejemplo n.º 6
0
 def test_it_does_not_break_if_no_headers(self, span, integration_config):
     store_request_headers(None, span, integration_config)
     store_response_headers(None, span, integration_config)