def test_http_status_to_status_code(self): for status_code, expected in ( (HTTPStatus.OK, StatusCode.UNSET), (HTTPStatus.ACCEPTED, StatusCode.UNSET), (HTTPStatus.IM_USED, StatusCode.UNSET), (HTTPStatus.MULTIPLE_CHOICES, StatusCode.UNSET), (HTTPStatus.BAD_REQUEST, StatusCode.ERROR), (HTTPStatus.UNAUTHORIZED, StatusCode.ERROR), (HTTPStatus.FORBIDDEN, StatusCode.ERROR), (HTTPStatus.NOT_FOUND, StatusCode.ERROR), ( HTTPStatus.UNPROCESSABLE_ENTITY, StatusCode.ERROR, ), ( HTTPStatus.TOO_MANY_REQUESTS, StatusCode.ERROR, ), (HTTPStatus.NOT_IMPLEMENTED, StatusCode.ERROR), (HTTPStatus.SERVICE_UNAVAILABLE, StatusCode.ERROR), ( HTTPStatus.GATEWAY_TIMEOUT, StatusCode.ERROR, ), ( HTTPStatus.HTTP_VERSION_NOT_SUPPORTED, StatusCode.ERROR, ), (600, StatusCode.ERROR), (99, StatusCode.ERROR), ): with self.subTest(status_code=status_code): actual = http_status_to_status_code(int(status_code)) self.assertEqual(actual, expected, status_code)
def _finish_span(tracer, handler, error=None): status_code = handler.get_status() reason = getattr(handler, "_reason") finish_args = (None, None, None) ctx = getattr(handler, _HANDLER_CONTEXT_KEY, None) if error: if isinstance(error, tornado.web.HTTPError): status_code = error.status_code if not ctx and status_code == 404: ctx = _start_span(tracer, handler, _time_ns()) if status_code != 404: finish_args = ( type(error), error, getattr(error, "__traceback__", None), ) status_code = 500 reason = None if not ctx: return if ctx.span.is_recording(): ctx.span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, status_code) ctx.span.set_status( Status( status_code=http_status_to_status_code(status_code), description=reason, )) ctx.activation.__exit__(*finish_args) # pylint: disable=E1101 context.detach(ctx.token) delattr(handler, _HANDLER_CONTEXT_KEY)
def _apply_response(span: Span, response: urllib3.response.HTTPResponse) -> None: if not span.is_recording(): return span.set_attribute("http.status_code", response.status) span.set_attribute("http.status_text", response.reason) span.set_status(Status(http_status_to_status_code(response.status)))
def process_response(self, req, resp, resource, req_succeeded=None): # pylint:disable=R0201 span = req.env.get(_ENVIRON_SPAN_KEY) if not span or not span.is_recording(): return status = resp.status reason = None if resource is None: status = "404" reason = "NotFound" exc_type, exc, _ = sys.exc_info() if exc_type and not req_succeeded: if "HTTPNotFound" in exc_type.__name__: status = "404" reason = "NotFound" else: status = "500" reason = "{}: {}".format(exc_type.__name__, exc) status = status.split(" ")[0] try: status_code = int(status) except ValueError: pass finally: span.set_attribute("http.status_code", status_code) span.set_status( Status( status_code=http_status_to_status_code(status_code), description=reason, ))
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") or context.get_value( _SUPPRESS_REQUESTS_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) labels = {} labels["http.method"] = method labels["http.url"] = url with get_tracer(__name__, __version__, tracer_provider).start_as_current_span( span_name, kind=SpanKind.CLIENT) as span: exception = None if span.is_recording(): span.set_attribute("http.method", method) span.set_attribute("http.url", url) headers = get_or_create_headers() inject(type(headers).__setitem__, headers) token = context.attach( context.set_value(_SUPPRESS_REQUESTS_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("http.status_code", result.status_code) span.set_attribute("http.status_text", result.reason) span.set_status( Status(http_status_to_status_code(result.status_code))) labels["http.status_code"] = str(result.status_code) if result.raw and result.raw.version: labels["http.flavor"] = (str(result.raw.version)[:1] + "." + str(result.raw.version)[:-1]) if span_callback is not None: span_callback(span, 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
def test_http_status_to_status_code_redirect(self): for status_code, expected in ( (HTTPStatus.MULTIPLE_CHOICES, StatusCode.ERROR), (HTTPStatus.MOVED_PERMANENTLY, StatusCode.ERROR), (HTTPStatus.TEMPORARY_REDIRECT, StatusCode.ERROR), (HTTPStatus.PERMANENT_REDIRECT, StatusCode.ERROR), ): with self.subTest(status_code=status_code): actual = http_status_to_status_code(int(status_code), allow_redirect=False) self.assertEqual(actual, expected, status_code)
async def on_request_end( unused_session: aiohttp.ClientSession, trace_config_ctx: types.SimpleNamespace, params: aiohttp.TraceRequestEndParams, ): if trace_config_ctx.span is None: return if trace_config_ctx.span.is_recording(): trace_config_ctx.span.set_status( Status(http_status_to_status_code(int( params.response.status)))) trace_config_ctx.span.set_attribute( SpanAttributes.HTTP_STATUS_CODE, params.response.status) _end_trace(trace_config_ctx)
def set_status_code(span, status_code): """Adds HTTP response attributes to span using the status_code argument.""" if not span.is_recording(): return try: status_code = int(status_code) except ValueError: span.set_status( Status( StatusCode.ERROR, "Non-integer HTTP status: " + repr(status_code), )) else: span.set_attribute("http.status_code", status_code) span.set_status(Status(http_status_to_status_code(status_code)))
def _finish_span(tracer, handler, error=None): status_code = handler.get_status() reason = getattr(handler, "_reason") finish_args = (None, None, None) ctx = getattr(handler, _HANDLER_CONTEXT_KEY, None) if error: if isinstance(error, tornado.web.HTTPError): status_code = error.status_code if not ctx and status_code == 404: ctx = _start_span(tracer, handler, _time_ns()) else: status_code = 500 reason = None if status_code >= 500: finish_args = ( type(error), error, getattr(error, "__traceback__", None), ) if not ctx: return if ctx.span.is_recording(): ctx.span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, status_code) otel_status_code = http_status_to_status_code(status_code, server_span=True) otel_status_description = None if otel_status_code is StatusCode.ERROR: otel_status_description = reason ctx.span.set_status( Status( status_code=otel_status_code, description=otel_status_description, )) if ctx.span.is_recording() and ctx.span.kind == trace.SpanKind.SERVER: custom_attributes = _collect_custom_response_headers_attributes( handler._headers) if len(custom_attributes) > 0: ctx.span.set_attributes(custom_attributes) ctx.activation.__exit__(*finish_args) # pylint: disable=E1101 if ctx.token: context.detach(ctx.token) delattr(handler, _HANDLER_CONTEXT_KEY)
def add_response_attributes(span, start_response_status, response_headers): # pylint: disable=unused-argument """Adds HTTP response attributes to span using the arguments passed to a PEP3333-conforming start_response callable.""" if not span.is_recording(): return status_code, _ = start_response_status.split(" ", 1) try: status_code = int(status_code) except ValueError: span.set_status( Status( StatusCode.ERROR, "Non-integer HTTP status: " + repr(status_code), )) else: span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, status_code) span.set_status(Status(http_status_to_status_code(status_code)))
def process_response(self, req, resp, resource, req_succeeded=None): # pylint:disable=R0201 span = req.env.get(_ENVIRON_SPAN_KEY) if not span or not span.is_recording(): return status = resp.status reason = None if resource is None: status = "404" reason = "NotFound" else: if _ENVIRON_EXC in req.env: exc = req.env[_ENVIRON_EXC] exc_type = type(exc) else: exc_type, exc = None, None if exc_type and not req_succeeded: if "HTTPNotFound" in exc_type.__name__: status = "404" reason = "NotFound" else: status = "500" reason = f"{exc_type.__name__}: {exc}" status = status.split(" ")[0] try: status_code = int(status) span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, status_code) span.set_status( Status( status_code=http_status_to_status_code(status_code, server_span=True), description=reason, )) except ValueError: pass propagator = get_global_response_propagator() if propagator: propagator.inject(resp, setter=_response_propagation_setter) if self._response_hook: self._response_hook(span, req, resp)
def _finish_tracing_callback(future, span): status_code = None description = None exc = future.exception() if span.is_recording() and exc: if isinstance(exc, HTTPError): status_code = exc.code description = "{}: {}".format(type(exc).__name__, exc) else: status_code = future.result().code if status_code is not None: span.set_attribute("http.status_code", status_code) span.set_status( Status( status_code=http_status_to_status_code(status_code), description=description, )) span.end()
def _finish_tracing_callback(future, span, response_hook): status_code = None description = None exc = future.exception() if span.is_recording() and exc: if isinstance(exc, HTTPError): status_code = exc.code description = "{}: {}".format(type(exc).__name__, exc) else: status_code = future.result().code if status_code is not None: span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, status_code) span.set_status( Status( status_code=http_status_to_status_code(status_code), description=description, )) if response_hook: response_hook(span, future) span.end()
def _instrumented_open_call(_, request, call_wrapped, get_or_create_headers): # pylint: disable=too-many-locals if context.get_value("suppress_instrumentation") or context.get_value( _SUPPRESS_URLLIB_INSTRUMENTATION_KEY): return call_wrapped() method = request.get_method().upper() url = request.full_url 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) recorder = URLLibInstrumentor().metric_recorder labels = { "http.method": method, "http.url": url, } with get_tracer(__name__, __version__, tracer_provider).start_as_current_span( span_name, kind=SpanKind.CLIENT) as span: exception = None with recorder.record_client_duration(labels): if span.is_recording(): span.set_attribute("http.method", method) span.set_attribute("http.url", url) headers = get_or_create_headers() propagators.inject(type(headers).__setitem__, headers) token = context.attach( context.set_value(_SUPPRESS_URLLIB_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["http.status_code"] = str(code_) if span.is_recording(): span.set_attribute("http.status_code", code_) span.set_attribute("http.status_text", result.reason) span.set_status( Status(http_status_to_status_code(code_))) ver_ = str(getattr(result, "version", "")) if ver_: labels["http.flavor"] = "{}.{}".format( ver_[:1], ver_[:-1]) if span_callback is not None: span_callback(span, result) if exception is not None: raise exception.with_traceback(exception.__traceback__) return result
def test_http_status_to_status_code_none(self): for status_code, expected in ((None, StatusCode.UNSET),): with self.subTest(status_code=status_code): actual = http_status_to_status_code(status_code) self.assertEqual(actual, expected, status_code)
def _apply_response(span: Span, response: urllib3.response.HTTPResponse): if not span.is_recording(): return span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, response.status) span.set_status(Status(http_status_to_status_code(response.status)))
def _apply_status_code(span: Span, status_code: int) -> None: if not span.is_recording(): return span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, status_code) span.set_status(Status(http_status_to_status_code(status_code)))
def _instrumented_open_call( _, request, call_wrapped, get_or_create_headers ): # pylint: disable=too-many-locals if context.get_value("suppress_instrumentation") or context.get_value( _SUPPRESS_HTTP_INSTRUMENTATION_KEY ): return call_wrapped() method = request.get_method().upper() url = request.full_url 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) labels = { SpanAttributes.HTTP_METHOD: method, SpanAttributes.HTTP_URL: 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, "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] = "{}.{}".format( ver_[:1], ver_[:-1] ) if span_callback is not None: span_callback(span, 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) 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
def process_response( self, req, resp, resource, req_succeeded=None ): # pylint:disable=R0201,R0912 span = req.env.get(_ENVIRON_SPAN_KEY) if not span or not span.is_recording(): return status = resp.status reason = None if resource is None: status = "404" reason = "NotFound" else: if _ENVIRON_EXC in req.env: exc = req.env[_ENVIRON_EXC] exc_type = type(exc) else: exc_type, exc = None, None if exc_type and not req_succeeded: if "HTTPNotFound" in exc_type.__name__: status = "404" reason = "NotFound" else: status = "500" reason = f"{exc_type.__name__}: {exc}" status = status.split(" ")[0] try: status_code = int(status) span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, status_code) span.set_status( Status( status_code=http_status_to_status_code( status_code, server_span=True ), description=reason, ) ) # Falcon 1 does not support response headers. So # send an empty dict. response_headers = {} if _falcon_version > 1: response_headers = resp.headers if span.is_recording() and span.kind == trace.SpanKind.SERVER: custom_attributes = ( otel_wsgi.collect_custom_response_headers_attributes( response_headers.items() ) ) if len(custom_attributes) > 0: span.set_attributes(custom_attributes) except ValueError: pass propagator = get_global_response_propagator() if propagator: propagator.inject(resp, setter=_response_propagation_setter) if self._response_hook: self._response_hook(span, req, resp)