async def on_request_start( unused_session: aiohttp.ClientSession, trace_config_ctx: types.SimpleNamespace, params: aiohttp.TraceRequestStartParams, ): if context_api.get_value(_SUPPRESS_INSTRUMENTATION_KEY): trace_config_ctx.span = None return http_method = params.method.upper() request_span_name = f"HTTP {http_method}" request_url = (remove_url_credentials( trace_config_ctx.url_filter(params.url)) if callable( trace_config_ctx.url_filter) else remove_url_credentials( str(params.url))) span_attributes = { SpanAttributes.HTTP_METHOD: http_method, SpanAttributes.HTTP_URL: request_url, } trace_config_ctx.span = trace_config_ctx.tracer.start_span( request_span_name, kind=SpanKind.CLIENT, attributes=span_attributes) if callable(request_hook): request_hook(trace_config_ctx.span, params) trace_config_ctx.token = context_api.attach( trace.set_span_in_context(trace_config_ctx.span)) inject(params.headers)
def _instrumented_open_call(_, request, call_wrapped, get_or_create_headers): # pylint: disable=too-many-locals if context.get_value( _SUPPRESS_INSTRUMENTATION_KEY) or context.get_value( _SUPPRESS_HTTP_INSTRUMENTATION_KEY): return call_wrapped() method = request.get_method().upper() url = request.full_url span_name = f"HTTP {method}".strip() url = remove_url_credentials(url) labels = { SpanAttributes.HTTP_METHOD: method, SpanAttributes.HTTP_URL: url, } with tracer.start_as_current_span(span_name, kind=SpanKind.CLIENT, attributes=labels) as span: exception = None if callable(request_hook): request_hook(span, request) headers = get_or_create_headers() inject(headers) token = context.attach( context.set_value(_SUPPRESS_HTTP_INSTRUMENTATION_KEY, True)) try: result = call_wrapped() # *** PROCEED except Exception as exc: # pylint: disable=W0703 exception = exc result = getattr(exc, "file", None) finally: context.detach(token) if result is not None: code_ = result.getcode() labels[SpanAttributes.HTTP_STATUS_CODE] = str(code_) if span.is_recording(): span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, code_) span.set_status(Status(http_status_to_status_code(code_))) ver_ = str(getattr(result, "version", "")) if ver_: labels[ SpanAttributes.HTTP_FLAVOR] = f"{ver_[:1]}.{ver_[:-1]}" if callable(response_hook): response_hook(span, request, result) if exception is not None: raise exception.with_traceback(exception.__traceback__) return result
def _instrumented_requests_call( method: str, url: str, call_wrapped, get_or_create_headers ): if context.get_value( _SUPPRESS_INSTRUMENTATION_KEY ) or context.get_value(_SUPPRESS_HTTP_INSTRUMENTATION_KEY): return call_wrapped() # See # https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#http-client method = method.upper() span_name = "" if name_callback is not None: span_name = name_callback(method, url) if not span_name or not isinstance(span_name, str): span_name = get_default_span_name(method) url = remove_url_credentials(url) with tracer.start_as_current_span( span_name, kind=SpanKind.CLIENT ) as span: exception = None if span.is_recording(): span.set_attribute(SpanAttributes.HTTP_METHOD, method) span.set_attribute(SpanAttributes.HTTP_URL, url) headers = get_or_create_headers() inject(headers) token = context.attach( context.set_value(_SUPPRESS_HTTP_INSTRUMENTATION_KEY, True) ) try: result = call_wrapped() # *** PROCEED except Exception as exc: # pylint: disable=W0703 exception = exc result = getattr(exc, "response", None) finally: context.detach(token) if isinstance(result, Response): if span.is_recording(): span.set_attribute( SpanAttributes.HTTP_STATUS_CODE, result.status_code ) span.set_status( Status(http_status_to_status_code(result.status_code)) ) if span_callback is not None: span_callback(span, result) if exception is not None: raise exception.with_traceback(exception.__traceback__) return result
async def on_request_start( unused_session: aiohttp.ClientSession, trace_config_ctx: types.SimpleNamespace, params: aiohttp.TraceRequestStartParams, ): if context_api.get_value(_SUPPRESS_INSTRUMENTATION_KEY): trace_config_ctx.span = None return http_method = params.method.upper() if trace_config_ctx.span_name is None: request_span_name = "HTTP {}".format(http_method) elif callable(trace_config_ctx.span_name): request_span_name = str(trace_config_ctx.span_name(params)) else: request_span_name = str(trace_config_ctx.span_name) trace_config_ctx.span = trace_config_ctx.tracer.start_span( request_span_name, kind=SpanKind.CLIENT, ) if trace_config_ctx.span.is_recording(): attributes = { SpanAttributes.HTTP_METHOD: http_method, SpanAttributes.HTTP_URL: remove_url_credentials(trace_config_ctx.url_filter(params.url)) if callable(trace_config_ctx.url_filter) else remove_url_credentials(str(params.url)), } for key, value in attributes.items(): trace_config_ctx.span.set_attribute(key, value) trace_config_ctx.token = context_api.attach( trace.set_span_in_context(trace_config_ctx.span)) inject(params.headers)
def collect_request_attributes(environ): """Collects HTTP request attributes from the PEP3333-conforming WSGI environ and returns a dictionary to be used as span creation attributes.""" result = { SpanAttributes.HTTP_METHOD: environ.get("REQUEST_METHOD"), SpanAttributes.HTTP_SERVER_NAME: environ.get("SERVER_NAME"), SpanAttributes.HTTP_SCHEME: environ.get("wsgi.url_scheme"), } host_port = environ.get("SERVER_PORT") if host_port is not None and not host_port == "": result.update({SpanAttributes.NET_HOST_PORT: int(host_port)}) setifnotnone(result, SpanAttributes.HTTP_HOST, environ.get("HTTP_HOST")) target = environ.get("RAW_URI") if target is None: # Note: `"" or None is None` target = environ.get("REQUEST_URI") if target is not None: result[SpanAttributes.HTTP_TARGET] = target else: result[SpanAttributes.HTTP_URL] = remove_url_credentials( wsgiref_util.request_uri(environ) ) remote_addr = environ.get("REMOTE_ADDR") if remote_addr: result[SpanAttributes.NET_PEER_IP] = remote_addr remote_host = environ.get("REMOTE_HOST") if remote_host and remote_host != remote_addr: result[SpanAttributes.NET_PEER_NAME] = remote_host user_agent = environ.get("HTTP_USER_AGENT") if user_agent is not None and len(user_agent) > 0: result[SpanAttributes.HTTP_USER_AGENT] = user_agent setifnotnone( result, SpanAttributes.NET_PEER_PORT, environ.get("REMOTE_PORT") ) flavor = environ.get("SERVER_PROTOCOL", "") if flavor.upper().startswith(_HTTP_VERSION_PREFIX): flavor = flavor[len(_HTTP_VERSION_PREFIX) :] if flavor: result[SpanAttributes.HTTP_FLAVOR] = flavor return result
def fetch_async(tracer, request_hook, response_hook, func, _, args, kwargs): start_time = _time_ns() # Return immediately if no args were provided (error) # or original_request is set (meaning we are in a redirect step). if len(args) == 0 or hasattr(args[0], "original_request"): return func(*args, **kwargs) # Force the creation of a HTTPRequest object if needed, # so we can inject the context into the headers. args, kwargs = _normalize_request(args, kwargs) request = args[0] span = tracer.start_span( request.method, kind=trace.SpanKind.CLIENT, start_time=start_time, ) if request_hook: request_hook(span, request) if span.is_recording(): attributes = { SpanAttributes.HTTP_URL: remove_url_credentials(request.url), SpanAttributes.HTTP_METHOD: request.method, } for key, value in attributes.items(): span.set_attribute(key, value) with trace.use_span(span): inject(request.headers) future = func(*args, **kwargs) future.add_done_callback( functools.partial( _finish_tracing_callback, span=span, response_hook=response_hook, ) ) return future
def collect_request_attributes(scope): """Collects HTTP request attributes from the ASGI scope and returns a dictionary to be used as span creation attributes.""" server_host, port, http_url = get_host_port_url_tuple(scope) query_string = scope.get("query_string") if query_string and http_url: if isinstance(query_string, bytes): query_string = query_string.decode("utf8") http_url += "?" + urllib.parse.unquote(query_string) result = { SpanAttributes.HTTP_SCHEME: scope.get("scheme"), SpanAttributes.HTTP_HOST: server_host, SpanAttributes.NET_HOST_PORT: port, SpanAttributes.HTTP_FLAVOR: scope.get("http_version"), SpanAttributes.HTTP_TARGET: scope.get("path"), SpanAttributes.HTTP_URL: remove_url_credentials(http_url), } http_method = scope.get("method") if http_method: result[SpanAttributes.HTTP_METHOD] = http_method http_host_value_list = asgi_getter.get(scope, "host") if http_host_value_list: result[SpanAttributes.HTTP_SERVER_NAME] = ",".join( http_host_value_list ) http_user_agent = asgi_getter.get(scope, "user-agent") if http_user_agent: result[SpanAttributes.HTTP_USER_AGENT] = http_user_agent[0] if "client" in scope and scope["client"] is not None: result[SpanAttributes.NET_PEER_IP] = scope.get("client")[0] result[SpanAttributes.NET_PEER_PORT] = scope.get("client")[1] # remove None values result = {k: v for k, v in result.items() if v is not None} return result
def _instrumented_requests_call(method: str, url: str, call_wrapped, get_or_create_headers): if context.get_value( _SUPPRESS_INSTRUMENTATION_KEY) or context.get_value( _SUPPRESS_HTTP_INSTRUMENTATION_KEY): return call_wrapped() # See # https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#http-client method = method.upper() span_name = "" if name_callback is not None: span_name = name_callback(method, url) if not span_name or not isinstance(span_name, str): span_name = get_default_span_name(method) url = remove_url_credentials(url) span_attributes = { SpanAttributes.HTTP_METHOD: method, SpanAttributes.HTTP_URL: url, } metric_labels = { SpanAttributes.HTTP_METHOD: method, } try: parsed_url = urlparse(url) metric_labels[SpanAttributes.HTTP_SCHEME] = parsed_url.scheme if parsed_url.hostname: metric_labels[SpanAttributes.HTTP_HOST] = parsed_url.hostname metric_labels[ SpanAttributes.NET_PEER_NAME] = parsed_url.hostname if parsed_url.port: metric_labels[SpanAttributes.NET_PEER_PORT] = parsed_url.port except ValueError: pass with tracer.start_as_current_span( span_name, kind=SpanKind.CLIENT, attributes=span_attributes ) as span, set_ip_on_next_http_connection(span): exception = None headers = get_or_create_headers() inject(headers) token = context.attach( context.set_value(_SUPPRESS_HTTP_INSTRUMENTATION_KEY, True)) start_time = default_timer() try: result = call_wrapped() # *** PROCEED except Exception as exc: # pylint: disable=W0703 exception = exc result = getattr(exc, "response", None) finally: elapsed_time = max( round((default_timer() - start_time) * 1000), 0) context.detach(token) if isinstance(result, Response): if span.is_recording(): span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, result.status_code) span.set_status( Status(http_status_to_status_code(result.status_code))) metric_labels[ SpanAttributes.HTTP_STATUS_CODE] = result.status_code if result.raw is not None: version = getattr(result.raw, "version", None) if version: metric_labels[SpanAttributes.HTTP_FLAVOR] = ( "1.1" if version == 11 else "1.0") if span_callback is not None: span_callback(span, result) duration_histogram.record(elapsed_time, attributes=metric_labels) if exception is not None: raise exception.with_traceback(exception.__traceback__) return result