Beispiel #1
0
    def __init__(self, *args, **kwargs):
        otel_opts = kwargs.pop("_otel_opts", {})

        # inject trace middleware
        middlewares = kwargs.pop("middleware", [])
        tracer_provider = otel_opts.pop("tracer_provider", None)
        if not isinstance(middlewares, (list, tuple)):
            middlewares = [middlewares]

        self._otel_tracer = trace.get_tracer(
            __name__, __version__, tracer_provider
        )

        trace_middleware = _TraceMiddleware(
            self._otel_tracer,
            otel_opts.pop(
                "traced_request_attributes", get_traced_request_attrs("FALCON")
            ),
            otel_opts.pop("request_hook", None),
            otel_opts.pop("response_hook", None),
        )
        middlewares.insert(0, trace_middleware)
        kwargs["middleware"] = middlewares

        self._otel_excluded_urls = get_excluded_urls("FALCON")
        super().__init__(*args, **kwargs)
 def setUp(self):
     TornadoInstrumentor().instrument(
         server_request_hook=getattr(self, "server_request_hook", None),
         client_request_hook=getattr(self, "client_request_hook", None),
         client_response_hook=getattr(self, "client_response_hook", None),
     )
     super().setUp()
     # pylint: disable=protected-access
     self.env_patch = patch.dict(
         "os.environ",
         {
             "OTEL_PYTHON_TORNADO_EXCLUDED_URLS": "healthz,ping",
             "OTEL_PYTHON_TORNADO_TRACED_REQUEST_ATTRS": "uri,full_url,query",
         },
     )
     self.env_patch.start()
     self.exclude_patch = patch(
         "opentelemetry.instrumentation.tornado._excluded_urls",
         get_excluded_urls("TORNADO"),
     )
     self.traced_patch = patch(
         "opentelemetry.instrumentation.tornado._traced_request_attrs",
         get_traced_request_attrs("TORNADO"),
     )
     self.exclude_patch.start()
     self.traced_patch.start()
Beispiel #3
0
 def setUp(self):
     super().setUp()
     FalconInstrumentor().instrument(
         request_hook=getattr(self, "request_hook", None),
         response_hook=getattr(self, "response_hook", None),
     )
     self.app = make_app()
     # pylint: disable=protected-access
     self.env_patch = patch.dict(
         "os.environ",
         {
             "OTEL_PYTHON_FALCON_EXCLUDED_URLS": "ping",
             "OTEL_PYTHON_FALCON_TRACED_REQUEST_ATTRS": "query_string",
         },
     )
     self.env_patch.start()
     self.exclude_patch = patch(
         "opentelemetry.instrumentation.falcon._excluded_urls",
         get_excluded_urls("FALCON"),
     )
     middleware = self.app._middleware[0][  # pylint:disable=W0212
         0
     ].__self__
     self.traced_patch = patch.object(
         middleware,
         "_traced_request_attrs",
         get_traced_request_attrs("FALCON"),
     )
     self.exclude_patch.start()
     self.traced_patch.start()
 def setUp(self):
     super().setUp()
     setup_test_environment()
     _django_instrumentor.instrument()
     self.env_patch = patch.dict(
         "os.environ",
         {
             "OTEL_PYTHON_DJANGO_EXCLUDED_URLS": "http://testserver/excluded_arg/123,excluded_noarg",
             "OTEL_PYTHON_DJANGO_TRACED_REQUEST_ATTRS": "path_info,content_type,non_existing_variable",
         },
     )
     self.env_patch.start()
     self.exclude_patch = patch(
         "opentelemetry.instrumentation.django.middleware._DjangoMiddleware._excluded_urls",
         get_excluded_urls("DJANGO"),
     )
     self.traced_patch = patch(
         "opentelemetry.instrumentation.django.middleware._DjangoMiddleware._traced_request_attrs",
         get_traced_request_attrs("DJANGO"),
     )
     self.exclude_patch.start()
     self.traced_patch.start()
)
from opentelemetry.propagate import extract
from opentelemetry.trace.status import Status
from opentelemetry.util._time import _time_ns
from opentelemetry.util.http import get_excluded_urls, get_traced_request_attrs

_logger = getLogger(__name__)

_ENVIRON_STARTTIME_KEY = "opentelemetry-falcon.starttime_key"
_ENVIRON_SPAN_KEY = "opentelemetry-falcon.span_key"
_ENVIRON_ACTIVATION_KEY = "opentelemetry-falcon.activation_key"
_ENVIRON_TOKEN = "opentelemetry-falcon.token"
_ENVIRON_EXC = "opentelemetry-falcon.exc"

_excluded_urls = get_excluded_urls("FALCON")
_traced_request_attrs = get_traced_request_attrs("FALCON")
_response_propagation_setter = FuncSetter(falcon.api.Response.append_header)


class FalconInstrumentor(BaseInstrumentor):
    # pylint: disable=protected-access,attribute-defined-outside-init
    """An instrumentor for falcon.API

    See `BaseInstrumentor`
    """
    def _instrument(self, **kwargs):
        self._original_falcon_api = falcon.API
        falcon.API = partial(_InstrumentedFalconAPI, **kwargs)

    def _uninstrument(self, **kwargs):
        falcon.API = self._original_falcon_api
class _DjangoMiddleware(MiddlewareMixin):
    """Django Middleware for OpenTelemetry"""

    _environ_activation_key = (
        "opentelemetry-instrumentor-django.activation_key")
    _environ_token = "opentelemetry-instrumentor-django.token"
    _environ_span_key = "opentelemetry-instrumentor-django.span_key"
    _environ_exception_key = "opentelemetry-instrumentor-django.exception_key"

    _traced_request_attrs = get_traced_request_attrs("DJANGO")
    _excluded_urls = get_excluded_urls("DJANGO")

    @staticmethod
    def _get_span_name(request):
        try:
            if getattr(request, "resolver_match"):
                match = request.resolver_match
            else:
                match = resolve(request.path)

            if hasattr(match, "route"):
                return match.route

            # Instead of using `view_name`, better to use `_func_name` as some applications can use similar
            # view names in different modules
            if hasattr(match, "_func_name"):
                return match._func_name  # pylint: disable=protected-access

            # Fallback for safety as `_func_name` private field
            return match.view_name

        except Resolver404:
            return "HTTP {}".format(request.method)

    def process_request(self, request):
        # request.META is a dictionary containing all available HTTP headers
        # Read more about request.META here:
        # https://docs.djangoproject.com/en/3.0/ref/request-response/#django.http.HttpRequest.META

        if self._excluded_urls.url_disabled(request.build_absolute_uri("?")):
            return

        # pylint:disable=W0212
        request._otel_start_time = time()

        request_meta = request.META

        token = attach(extract(carrier_getter, request_meta))

        tracer = get_tracer(__name__, __version__)

        span = tracer.start_span(
            self._get_span_name(request),
            kind=SpanKind.SERVER,
            start_time=request_meta.get(
                "opentelemetry-instrumentor-django.starttime_key"),
        )

        attributes = collect_request_attributes(request_meta)

        if span.is_recording():
            attributes = extract_attributes_from_object(
                request, self._traced_request_attrs, attributes)
            for key, value in attributes.items():
                span.set_attribute(key, value)

        activation = tracer.use_span(span, end_on_exit=True)
        activation.__enter__()

        request.META[self._environ_activation_key] = activation
        request.META[self._environ_span_key] = span
        request.META[self._environ_token] = token

    # pylint: disable=unused-argument
    def process_view(self, request, view_func, *args, **kwargs):
        # Process view is executed before the view function, here we get the
        # route template from request.resolver_match.  It is not set yet in process_request
        if self._excluded_urls.url_disabled(request.build_absolute_uri("?")):
            return

        if (self._environ_activation_key in request.META.keys()
                and self._environ_span_key in request.META.keys()):
            span = request.META[self._environ_span_key]

            if span.is_recording():
                match = getattr(request, "resolver_match")
                if match:
                    route = getattr(match, "route")
                    if route:
                        span.set_attribute("http.route", route)

    def process_exception(self, request, exception):
        if self._excluded_urls.url_disabled(request.build_absolute_uri("?")):
            return

        if self._environ_activation_key in request.META.keys():
            request.META[self._environ_exception_key] = exception

    def process_response(self, request, response):
        if self._excluded_urls.url_disabled(request.build_absolute_uri("?")):
            return response

        if (self._environ_activation_key in request.META.keys()
                and self._environ_span_key in request.META.keys()):
            add_response_attributes(
                request.META[self._environ_span_key],
                "{} {}".format(response.status_code, response.reason_phrase),
                response,
            )

            request.META.pop(self._environ_span_key)

            exception = request.META.pop(self._environ_exception_key, None)
            if exception:
                request.META[self._environ_activation_key].__exit__(
                    type(exception),
                    exception,
                    getattr(exception, "__traceback__", None),
                )
            else:
                request.META[self._environ_activation_key].__exit__(
                    None, None, None)
            request.META.pop(self._environ_activation_key)

        if self._environ_token in request.META.keys():
            detach(request.environ.get(self._environ_token))
            request.META.pop(self._environ_token)

        return response
Beispiel #7
0
)
from opentelemetry.propagate import extract
from opentelemetry.semconv.trace import SpanAttributes
from opentelemetry.trace.status import Status, StatusCode
from opentelemetry.util._time import _time_ns
from opentelemetry.util.http import get_excluded_urls, get_traced_request_attrs

from .client import fetch_async  # pylint: disable=E0401

_logger = getLogger(__name__)
_TraceContext = namedtuple("TraceContext", ["activation", "span", "token"])
_HANDLER_CONTEXT_KEY = "_otel_trace_context_key"
_OTEL_PATCHED_KEY = "_otel_patched_key"

_excluded_urls = get_excluded_urls("TORNADO")
_traced_request_attrs = get_traced_request_attrs("TORNADO")

response_propagation_setter = FuncSetter(tornado.web.RequestHandler.add_header)


class TornadoInstrumentor(BaseInstrumentor):
    patched_handlers = []
    original_handler_new = None

    def instrumentation_dependencies(self) -> Collection[str]:
        return _instruments

    def _instrument(self, **kwargs):
        """
        _instrument patches tornado.web.RequestHandler and tornado.httpclient.AsyncHTTPClient classes
        to automatically instrument requests both received and sent by Tornado.
class _DjangoMiddleware(MiddlewareMixin):
    """Django Middleware for OpenTelemetry"""

    _environ_activation_key = (
        "opentelemetry-instrumentor-django.activation_key"
    )
    _environ_token = "opentelemetry-instrumentor-django.token"
    _environ_span_key = "opentelemetry-instrumentor-django.span_key"
    _environ_exception_key = "opentelemetry-instrumentor-django.exception_key"

    _traced_request_attrs = get_traced_request_attrs("DJANGO")
    _excluded_urls = get_excluded_urls("DJANGO")
    _tracer = None

    _otel_request_hook: Callable[[Span, HttpRequest], None] = None
    _otel_response_hook: Callable[
        [Span, HttpRequest, HttpResponse], None
    ] = None

    @staticmethod
    def _get_span_name(request):
        try:
            if getattr(request, "resolver_match"):
                match = request.resolver_match
            else:
                match = resolve(request.path)

            if hasattr(match, "route"):
                return match.route

            # Instead of using `view_name`, better to use `_func_name` as some applications can use similar
            # view names in different modules
            if hasattr(match, "_func_name"):
                return match._func_name  # pylint: disable=protected-access

            # Fallback for safety as `_func_name` private field
            return match.view_name

        except Resolver404:
            return f"HTTP {request.method}"

    def process_request(self, request):
        # request.META is a dictionary containing all available HTTP headers
        # Read more about request.META here:
        # https://docs.djangoproject.com/en/3.0/ref/request-response/#django.http.HttpRequest.META

        if self._excluded_urls.url_disabled(request.build_absolute_uri("?")):
            return

        is_asgi_request = _is_asgi_request(request)
        if not _is_asgi_supported and is_asgi_request:
            return

        # pylint:disable=W0212
        request._otel_start_time = time()

        request_meta = request.META

        if is_asgi_request:
            carrier = request.scope
            carrier_getter = asgi_getter
            collect_request_attributes = asgi_collect_request_attributes
        else:
            carrier = request_meta
            carrier_getter = wsgi_getter
            collect_request_attributes = wsgi_collect_request_attributes

        token = context = None
        span_kind = SpanKind.INTERNAL
        if get_current_span() is INVALID_SPAN:
            context = extract(carrier, getter=carrier_getter)
            token = attach(context)
            span_kind = SpanKind.SERVER
        span = self._tracer.start_span(
            self._get_span_name(request),
            context,
            kind=span_kind,
            start_time=request_meta.get(
                "opentelemetry-instrumentor-django.starttime_key"
            ),
        )

        attributes = collect_request_attributes(carrier)

        if span.is_recording():
            attributes = extract_attributes_from_object(
                request, self._traced_request_attrs, attributes
            )
            if is_asgi_request:
                # ASGI requests include extra attributes in request.scope.headers.
                attributes = extract_attributes_from_object(
                    types.SimpleNamespace(
                        **{
                            name.decode("latin1"): value.decode("latin1")
                            for name, value in request.scope.get("headers", [])
                        }
                    ),
                    self._traced_request_attrs,
                    attributes,
                )

            for key, value in attributes.items():
                span.set_attribute(key, value)

        activation = use_span(span, end_on_exit=True)
        activation.__enter__()  # pylint: disable=E1101

        request.META[self._environ_activation_key] = activation
        request.META[self._environ_span_key] = span
        if token:
            request.META[self._environ_token] = token

        if _DjangoMiddleware._otel_request_hook:
            _DjangoMiddleware._otel_request_hook(  # pylint: disable=not-callable
                span, request
            )

    # pylint: disable=unused-argument
    def process_view(self, request, view_func, *args, **kwargs):
        # Process view is executed before the view function, here we get the
        # route template from request.resolver_match.  It is not set yet in process_request
        if self._excluded_urls.url_disabled(request.build_absolute_uri("?")):
            return

        if (
            self._environ_activation_key in request.META.keys()
            and self._environ_span_key in request.META.keys()
        ):
            span = request.META[self._environ_span_key]

            if span.is_recording():
                match = getattr(request, "resolver_match", None)
                if match:
                    route = getattr(match, "route", None)
                    if route:
                        span.set_attribute(SpanAttributes.HTTP_ROUTE, route)

    def process_exception(self, request, exception):
        if self._excluded_urls.url_disabled(request.build_absolute_uri("?")):
            return

        if self._environ_activation_key in request.META.keys():
            request.META[self._environ_exception_key] = exception

    def process_response(self, request, response):
        if self._excluded_urls.url_disabled(request.build_absolute_uri("?")):
            return response

        is_asgi_request = _is_asgi_request(request)
        if not _is_asgi_supported and is_asgi_request:
            return response

        activation = request.META.pop(self._environ_activation_key, None)
        span = request.META.pop(self._environ_span_key, None)

        if activation and span:
            if is_asgi_request:
                set_status_code(span, response.status_code)
            else:
                add_response_attributes(
                    span,
                    f"{response.status_code} {response.reason_phrase}",
                    response,
                )

            propagator = get_global_response_propagator()
            if propagator:
                propagator.inject(response)

            # record any exceptions raised while processing the request
            exception = request.META.pop(self._environ_exception_key, None)
            if _DjangoMiddleware._otel_response_hook:
                _DjangoMiddleware._otel_response_hook(  # pylint: disable=not-callable
                    span, request, response
                )

            if exception:
                activation.__exit__(
                    type(exception),
                    exception,
                    getattr(exception, "__traceback__", None),
                )
            else:
                activation.__exit__(None, None, None)

        if request.META.get(self._environ_token, None) is not None:
            detach(request.META.get(self._environ_token))
            request.META.pop(self._environ_token)

        return response