Beispiel #1
0
 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)
Beispiel #3
0
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)))
Beispiel #4
0
    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,
                ))
Beispiel #5
0
    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
Beispiel #7
0
    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)
Beispiel #9
0
    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)))
Beispiel #11
0
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)
Beispiel #12
0
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)))
Beispiel #13
0
    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)
Beispiel #14
0
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
Beispiel #17
0
 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)
Beispiel #18
0
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)))
Beispiel #19
0
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)))
Beispiel #20
0
    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
Beispiel #22
0
    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)