Beispiel #1
0
 def test_no_whitelist(self, span, integration_config):
     """
     :type span: Span
     :type integration_config: IntegrationConfig
     """
     store_response_headers({"Content-Type": "some;value",}, span, integration_config)
     assert span.get_tag("http.response.headers.content-type") is None
Beispiel #2
0
 def test_whitelist_case_insensitive(self, span, integration_config):
     """
     :type span: Span
     :type integration_config: IntegrationConfig
     """
     integration_config.http.trace_headers("CoNtEnT-tYpE")
     store_response_headers({"cOnTeNt-TyPe": "some;value",}, span, integration_config)
     assert span.get_tag("http.response.headers.content-type") == "some;value"
Beispiel #3
0
 def test_numbers_in_headers_names_are_allowed(self, span, integration_config):
     """
     :type span: Span
     :type integration_config: IntegrationConfig
     """
     integration_config.http.trace_headers("Content-Type123")
     store_response_headers({"Content-Type123": "some;value",}, span, integration_config)
     assert span.get_tag("http.response.headers.content-type123") == "some;value"
Beispiel #4
0
 def test_value_not_trim_leading_trailing_spaced(self, span, integration_config):
     """
     :type span: Span
     :type integration_config: IntegrationConfig
     """
     integration_config.http.trace_headers("Content-Type")
     store_response_headers({"Content-Type": "   some;value   ",}, span, integration_config)
     assert span.get_tag("http.response.headers.content-type") == "   some;value   "
Beispiel #5
0
 def test_whitelist_exact(self, span, integration_config):
     """
     :type span: Span
     :type integration_config: IntegrationConfig
     """
     integration_config.http.trace_headers("content-type")
     store_response_headers({"Content-Type": "some;value",}, span, integration_config)
     assert span.get_tag("http.response.headers.content-type") == "some;value"
 def test_no_allowlist(self, span, integration_config):
     """
     :type span: Span
     :type integration_config: IntegrationConfig
     """
     store_response_headers({
         'Content-Type': 'some;value',
     }, span, integration_config)
     assert span.get_tag('http.response.headers.content-type') is None
Beispiel #7
0
 def test_non_allowed_chars_replaced(self, span, integration_config):
     """
     :type span: Span
     :type integration_config: IntegrationConfig
     """
     # See: https://docs.datadoghq.com/tagging/#defining-tags
     integration_config.http.trace_headers("C!#ontent-Type")
     store_response_headers({"C!#ontent-Type": "some;value",}, span, integration_config)
     assert span.get_tag("http.response.headers.c__ontent-type") == "some;value"
Beispiel #8
0
 def update_span(response):
     if isinstance(response, sanic.response.BaseHTTPResponse):
         status_code = response.status
         store_response_headers(response.headers, span, config.sanic)
     else:
         # invalid response causes ServerError exception which must be handled
         status_code = 500
     trace_utils.set_http_meta(span, config.sanic, status_code=status_code)
     span.finish()
Beispiel #9
0
        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 "headers" in message:
                    store_response_headers(message["headers"], span,
                                           config.asgi)

            return await send(message)
Beispiel #10
0
 def test_period_is_replaced_by_underscore(self, span, integration_config):
     """
     :type span: Span
     :type integration_config: IntegrationConfig
     """
     # Deviation from https://docs.datadoghq.com/tagging/#defining-tags in order to allow
     # consistent representation of headers having the period in the name.
     integration_config.http.trace_headers("api.token")
     store_response_headers({"api.token": "some;value",}, span, integration_config)
     assert span.get_tag("http.response.headers.api_token") == "some;value"
Beispiel #11
0
 def test_whitelist_case_insensitive(self, span, integration_config):
     """
     :type span: Span
     :type integration_config: IntegrationConfig
     """
     integration_config.http.trace_headers('CoNtEnT-tYpE')
     store_response_headers({
         'cOnTeNt-TyPe': 'some;value',
     }, span, integration_config)
     assert span.get_tag(
         'http.response.headers.content-type') == 'some;value'
Beispiel #12
0
 def test_whitelist_exact(self, span, integration_config):
     """
     :type span: Span
     :type integration_config: IntegrationConfig
     """
     integration_config.http.trace_headers('content-type')
     store_response_headers({
         'Content-Type': 'some;value',
     }, span, integration_config)
     assert span.get_tag(
         'http.response.headers.content-type') == 'some;value'
Beispiel #13
0
 def test_key_trim_leading_trailing_spaced(self, span, integration_config):
     """
     :type span: Span
     :type integration_config: IntegrationConfig
     """
     integration_config.http.trace_headers('Content-Type')
     store_response_headers({
         '   Content-Type   ': 'some;value',
     }, span, integration_config)
     assert span.get_tag(
         'http.response.headers.content-type') == 'some;value'
Beispiel #14
0
        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)
Beispiel #15
0
        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)
Beispiel #16
0
 def test_allowed_chars_not_replaced_in_tag_name(self, span,
                                                 integration_config):
     """
     :type span: Span
     :type integration_config: IntegrationConfig
     """
     # See: https://docs.datadoghq.com/tagging/#defining-tags
     integration_config.http.trace_headers('C0n_t:e/nt-Type')
     store_response_headers({
         'C0n_t:e/nt-Type': 'some;value',
     }, span, integration_config)
     assert span.get_tag(
         'http.response.headers.c0n_t:e/nt-type') == 'some;value'
Beispiel #17
0
 def update_span(response):
     error = 0
     if isinstance(response, sanic.response.BaseHTTPResponse):
         status_code = response.status
         if 500 <= response.status < 600:
             error = 1
         store_response_headers(response.headers, span, config.sanic)
     else:
         # invalid response causes ServerError exception which must be handled
         status_code = 500
         error = 1
     span.set_tag(http.STATUS_CODE, status_code)
     span.error = error
     span.finish()
Beispiel #18
0
 def test_store_multiple_response_headers_as_dict(self, span, integration_config):
     """
     :type span: Span
     :type integration_config: IntegrationConfig
     """
     integration_config.http.trace_headers(["Content-Type", "Max-Age"])
     store_response_headers(
         {"Content-Type": "some;value;content-type", "Max-Age": "some;value;max_age", "Other": "some;value;other",},
         span,
         integration_config,
     )
     assert span.get_tag("http.response.headers.content-type") == "some;value;content-type"
     assert span.get_tag("http.response.headers.max-age") == "some;value;max_age"
     assert None is span.get_tag("http.response.headers.other")
Beispiel #19
0
        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)
Beispiel #20
0
        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)
Beispiel #21
0
 def test_store_multiple_response_headers_as_dict(self, span,
                                                  integration_config):
     """
     :type span: Span
     :type integration_config: IntegrationConfig
     """
     integration_config.http.trace_headers(['Content-Type', 'Max-Age'])
     store_response_headers(
         {
             'Content-Type': 'some;value;content-type',
             'Max-Age': 'some;value;max_age',
             'Other': 'some;value;other',
         }, span, integration_config)
     assert span.get_tag(
         'http.response.headers.content-type') == 'some;value;content-type'
     assert span.get_tag(
         'http.response.headers.max-age') == 'some;value;max_age'
     assert None is span.get_tag('http.response.headers.other')
Beispiel #22
0
    def process_response(self, req, resp, resource, req_succeeded=None):
        # req_succeded is not a kwarg in the API, but we need that to support
        # Falcon 1.0 that doesn't provide this argument
        span = self.tracer.current_span()
        if not span:
            return  # unexpected

        status = httpx.normalize_status_code(resp.status)

        # Note: any response header set after this line will not be stored in the span
        store_response_headers(resp._headers, span, config.falcon)

        # FIXME[matt] falcon does not map errors or unmatched routes
        # to proper status codes, so we we have to try to infer them
        # here. See https://github.com/falconry/falcon/issues/606
        if resource is None:
            status = '404'
            span.resource = '%s 404' % req.method
            span.set_tag(httpx.STATUS_CODE, status)
            span.finish()
            return

        err_type = sys.exc_info()[0]
        if err_type is not None:
            if req_succeeded is None:
                # backward-compatibility with Falcon 1.0; any version
                # greater than 1.0 has req_succeded in [True, False]
                # TODO[manu]: drop the support at some point
                status = _detect_and_set_status_error(err_type, span)
            elif req_succeeded is False:
                # Falcon 1.1+ provides that argument that is set to False
                # if get an Exception (404 is still an exception)
                status = _detect_and_set_status_error(err_type, span)

        span.set_tag(httpx.STATUS_CODE, status)
        if 500 <= int(status) < 600:
            span.error = 1

        # Emit span hook for this response
        # DEV: Emit before closing so they can overwrite `span.resource` if they want
        config.falcon.hooks.emit('request', span, req, resp)

        # Close the span
        span.finish()
Beispiel #23
0
def traced_get_response(django, pin, func, instance, args, kwargs):
    """Trace django.core.handlers.base.BaseHandler.get_response() (or other implementations).

    This is the main entry point for requests.

    Django requests are handled by a Handler.get_response method (inherited from base.BaseHandler).
    This method invokes the middleware chain and returns the response generated by the chain.
    """

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

    try:
        request_headers = request.META

        if config.django.distributed_tracing_enabled:
            context = propagator.extract(request_headers)
            if context.trace_id:
                pin.tracer.context_provider.activate(context)

        # Determine the resolver and resource name for this request
        resolver = get_resolver(getattr(request, "urlconf", None))

        if django.VERSION < (1, 10, 0):
            error_type_404 = django.core.urlresolvers.Resolver404
        else:
            error_type_404 = django.urls.exceptions.Resolver404

        route = None
        resolver_match = None
        resource = request.method
        try:
            # Resolve the requested url and build resource name pieces
            resolver_match = resolver.resolve(request.path_info)
            handler, _, _ = resolver_match
            handler = func_name(handler)
            urlpattern = ""
            resource_format = None

            if config.django.use_handler_resource_format:
                resource_format = "{method} {handler}"
            else:
                # In Django >= 2.2.0 we can access the original route or regex pattern
                # TODO: Validate if `resolver.pattern.regex.pattern` is available on django<2.2
                if django.VERSION >= (2, 2, 0):
                    route = utils.get_django_2_route(resolver, resolver_match)
                    resource_format = "{method} {urlpattern}"
                else:
                    resource_format = "{method} {handler}"

                if route is not None:
                    urlpattern = route

            resource = resource_format.format(method=request.method, urlpattern=urlpattern, handler=handler)

        except error_type_404:
            # Normalize all 404 requests into a single resource name
            # DEV: This is for potential cardinality issues
            resource = "{0} 404".format(request.method)
        except Exception:
            log.debug(
                "Failed to resolve request path %r with path info %r",
                request,
                getattr(request, "path_info", "not-set"),
                exc_info=True,
            )
    except Exception:
        log.debug("Failed to trace django request %r", args, exc_info=True)
        return func(*args, **kwargs)
    else:
        with pin.tracer.trace(
            "django.request",
            resource=resource,
            service=trace_utils.int_service(pin, config.django),
            span_type=SpanTypes.HTTP,
        ) as span:
            analytics_sr = config.django.get_analytics_sample_rate(use_global_config=True)
            if analytics_sr is not None:
                span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, analytics_sr)

            if config.django.http.trace_query_string:
                span.set_tag(http.QUERY_STRING, request_headers["QUERY_STRING"])

            # Not a 404 request
            if resolver_match:
                span.set_tag("django.view", resolver_match.view_name)
                utils.set_tag_array(span, "django.namespace", resolver_match.namespaces)

                # Django >= 2.0.0
                if hasattr(resolver_match, "app_names"):
                    utils.set_tag_array(span, "django.app", resolver_match.app_names)

            if route:
                span.set_tag("http.route", route)

            # Set HTTP Request tags
            span.set_tag(http.URL, utils.get_request_uri(request))

            response = func(*args, **kwargs)

            # Note: this call must be done after the function call because
            # some attributes (like `user`) are added to the request through
            # the middleware chain
            _set_request_tags(django, span, request)

            if response:
                span.set_tag(http.STATUS_CODE, response.status_code)
                if 500 <= response.status_code < 600:
                    span.error = 1
                span.set_tag("django.response.class", func_name(response))
                if hasattr(response, "template_name"):
                    # template_name is a bit of a misnomer, as it could be any of:
                    # a list of strings, a tuple of strings, a single string, or an instance of Template
                    # for more detail, see:
                    # https://docs.djangoproject.com/en/3.0/ref/template-response/#django.template.response.SimpleTemplateResponse.template_name
                    template = response.template_name

                    if isinstance(template, six.string_types):
                        template_names = [template]
                    elif isinstance(
                        template,
                        (
                            list,
                            tuple,
                        ),
                    ):
                        template_names = template
                    elif hasattr(template, "template"):
                        # ^ checking by attribute here because
                        # django backend implementations don't have a common base
                        # `.template` is also the most consistent across django versions
                        template_names = [template.template.name]
                    else:
                        template_names = None

                    utils.set_tag_array(span, "django.response.template", template_names)

                headers = dict(response.items())
                store_response_headers(headers, span, config.django)

            return response
Beispiel #24
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)
Beispiel #25
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)
Beispiel #26
0
 def update_span(response):
     span.set_tag(http.STATUS_CODE, response.status)
     if 500 <= response.status < 600:
         span.error = 1
     store_response_headers(response.headers, span, config.sanic)
     span.finish()
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)
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)