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)
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)
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()
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()
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)