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
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'
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'
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_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 '
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'
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'
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')
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) # 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()
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)
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)
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 oteltrace.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, 'opentelemetry_tracer', oteltrace.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) # 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)