예제 #1
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):
     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()
예제 #3
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)
예제 #4
0
 def setUp(self):
     super().setUp()
     self.env_patch = patch.dict(
         "os.environ",
         {"OTEL_PYTHON_STARLETTE_EXCLUDED_URLS": "/exclude/123,healthzz"},
     )
     self.env_patch.start()
     self.exclude_patch = patch(
         "opentelemetry.instrumentation.starlette._excluded_urls",
         get_excluded_urls("STARLETTE"),
     )
     self.exclude_patch.start()
     self._instrumentor = otel_starlette.StarletteInstrumentor()
     self._app = self._create_app()
     self._client = TestClient(self._app)
예제 #5
0
 def setUp(self):
     super().setUp()
     self.env_patch = patch.dict(
         "os.environ",
         {"OTEL_PYTHON_FASTAPI_EXCLUDED_URLS": "/exclude/123,healthzz"},
     )
     self.env_patch.start()
     self.exclude_patch = patch(
         "opentelemetry.instrumentation.fastapi._excluded_urls",
         get_excluded_urls("FASTAPI"),
     )
     self.exclude_patch.start()
     self._instrumentor = otel_fastapi.FastAPIInstrumentor()
     self._app = self._create_app()
     self._client = TestClient(self._app)
    def setUp(self):
        super().setUp()

        self.env_patch = mock.patch.dict(
            "os.environ",
            {
                "OTEL_PYTHON_REQUESTS_EXCLUDED_URLS":
                "http://localhost/env_excluded_arg/123,env_excluded_noarg"
            },
        )
        self.env_patch.start()

        self.exclude_patch = mock.patch(
            "opentelemetry.instrumentation.requests._excluded_urls_from_env",
            get_excluded_urls("REQUESTS"),
        )
        self.exclude_patch.start()

        RequestsInstrumentor().instrument()
        httpretty.enable()
        httpretty.register_uri(httpretty.GET, self.URL, body="Hello!")
    def setUp(self):
        super().setUp()

        self.app = Flask(__name__)

        FlaskInstrumentor().instrument_app(self.app)

        self._common_initialization()

        self.env_patch = patch.dict(
            "os.environ",
            {
                "OTEL_PYTHON_FLASK_EXCLUDED_URLS": "http://localhost/excluded_arg/123,excluded_noarg"
            },
        )
        self.env_patch.start()
        self.exclude_patch = patch(
            "opentelemetry.instrumentation.flask._excluded_urls",
            get_excluded_urls("FLASK"),
        )
        self.exclude_patch.start()
    def setUp(self):
        super().setUp()
        config = Configurator()
        PyramidInstrumentor().instrument_config(config)

        self.config = config

        self._common_initialization(self.config)

        self.env_patch = patch.dict(
            "os.environ",
            {
                "OTEL_PYTHON_PYRAMID_EXCLUDED_URLS": "http://localhost/excluded_arg/123,excluded_noarg"
            },
        )
        self.env_patch.start()
        self.exclude_patch = patch(
            "opentelemetry.instrumentation.pyramid.callbacks._excluded_urls",
            get_excluded_urls("PYRAMID"),
        )
        self.exclude_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()
import logging
import typing
from typing import Collection

import fastapi
from starlette.routing import Match

from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware
from opentelemetry.instrumentation.asgi.package import _instruments
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
from opentelemetry.semconv.trace import SpanAttributes
from opentelemetry.trace import Span
from opentelemetry.util.http import get_excluded_urls, parse_excluded_urls

_excluded_urls_from_env = get_excluded_urls("FASTAPI")
_logger = logging.getLogger(__name__)

_ServerRequestHookT = typing.Optional[typing.Callable[[Span, dict], None]]
_ClientRequestHookT = typing.Optional[typing.Callable[[Span, dict], None]]
_ClientResponseHookT = typing.Optional[typing.Callable[[Span, dict], None]]


class FastAPIInstrumentor(BaseInstrumentor):
    """An instrumentor for FastAPI

    See `BaseInstrumentor`
    """

    _original_fastapi = None
---
"""
import typing
from typing import Collection

from starlette import applications
from starlette.routing import Match

from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware
from opentelemetry.instrumentation.asgi.package import _instruments
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
from opentelemetry.semconv.trace import SpanAttributes
from opentelemetry.trace import Span
from opentelemetry.util.http import get_excluded_urls

_excluded_urls = get_excluded_urls("STARLETTE")

_ServerRequestHookT = typing.Optional[typing.Callable[[Span, dict], None]]
_ClientRequestHookT = typing.Optional[typing.Callable[[Span, dict], None]]
_ClientResponseHookT = typing.Optional[typing.Callable[[Span, dict], None]]


class StarletteInstrumentor(BaseInstrumentor):
    """An instrumentor for starlette

    See `BaseInstrumentor`
    """

    _original_starlette = None

    @staticmethod
    http_status_to_status_code,
)
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):
    def test_config_from_generic_env(self):
        exclude_list = get_excluded_urls("DJANGO")

        self.assertTrue(exclude_list.url_disabled("/excluded_arg/123"))
        self.assertTrue(exclude_list.url_disabled("/excluded_noarg"))
        self.assertFalse(exclude_list.url_disabled("/excluded_arg/125"))
    def test_config_from_instrumentation_env_empty(self):
        exclude_list = get_excluded_urls("DJANGO")

        self.assertFalse(exclude_list.url_disabled("/excluded_arg/123"))
        self.assertFalse(exclude_list.url_disabled("/excluded_noarg"))
        self.assertFalse(exclude_list.url_disabled("/excluded_arg/125"))
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
예제 #16
0
    unwrap,
)
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
    http_status_to_status_code,
)
from opentelemetry.metrics import Histogram, get_meter
from opentelemetry.propagate import inject
from opentelemetry.semconv.trace import SpanAttributes
from opentelemetry.trace import SpanKind, Tracer, get_tracer
from opentelemetry.trace.span import Span
from opentelemetry.trace.status import Status
from opentelemetry.util.http import (
    get_excluded_urls,
    parse_excluded_urls,
    remove_url_credentials,
)
from opentelemetry.util.http.httplib import set_ip_on_next_http_connection

_excluded_urls_from_env = get_excluded_urls("REQUESTS")


# pylint: disable=unused-argument
# pylint: disable=R0915
def _instrument(
    tracer: Tracer,
    duration_histogram: Histogram,
    span_callback: Optional[Callable[[Span, Response], str]] = None,
    name_callback: Optional[Callable[[str, str], str]] = None,
    excluded_urls: Iterable[str] = None,
):
    """Enables tracing of all requests calls that go through
    :code:`requests.session.Session.request` (this includes
    :code:`requests.get`, etc.)."""
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
from opentelemetry.semconv.trace import SpanAttributes
from opentelemetry.util._time import _time_ns
from opentelemetry.util.http import get_excluded_urls

TWEEN_NAME = "opentelemetry.instrumentation.pyramid.trace_tween_factory"
SETTING_TRACE_ENABLED = "opentelemetry-pyramid.trace_enabled"

_ENVIRON_STARTTIME_KEY = "opentelemetry-pyramid.starttime_key"
_ENVIRON_SPAN_KEY = "opentelemetry-pyramid.span_key"
_ENVIRON_ACTIVATION_KEY = "opentelemetry-pyramid.activation_key"
_ENVIRON_ENABLED_KEY = "opentelemetry-pyramid.tracing_enabled_key"
_ENVIRON_TOKEN = "opentelemetry-pyramid.token"

_logger = getLogger(__name__)

_excluded_urls = get_excluded_urls("PYRAMID")


def includeme(config):
    config.add_settings({SETTING_TRACE_ENABLED: True})

    config.add_subscriber(_before_traversal, BeforeTraversal)
    _insert_tween(config)


def _insert_tween(config):
    settings = config.get_settings()
    tweens = settings.get("pyramid.tweens")
    # If the list is empty, pyramid does not consider the tweens have been
    # set explicitly. And if our tween is already there, nothing to do
    if not tweens or not tweens.strip():
예제 #20
0
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
from opentelemetry.instrumentation.propagators import (
    get_global_response_propagator, )
from opentelemetry.propagate import extract
from opentelemetry.semconv.trace import SpanAttributes
from opentelemetry.util._time import _time_ns
from opentelemetry.util.http import get_excluded_urls, parse_excluded_urls

_logger = getLogger(__name__)

_ENVIRON_STARTTIME_KEY = "opentelemetry-flask.starttime_key"
_ENVIRON_SPAN_KEY = "opentelemetry-flask.span_key"
_ENVIRON_ACTIVATION_KEY = "opentelemetry-flask.activation_key"
_ENVIRON_TOKEN = "opentelemetry-flask.token"

_excluded_urls_from_env = get_excluded_urls("FLASK")


def get_default_span_name():
    span_name = ""
    try:
        span_name = flask.request.url_rule.rule
    except AttributeError:
        span_name = otel_wsgi.get_default_span_name(flask.request.environ)
    return span_name


def _rewrapped_app(wsgi_app, response_hook=None, excluded_urls=None):
    def _wrapped_app(wrapped_app_environ, start_response):
        # We want to measure the time for route matching, etc.
        # In theory, we could start the span here and use
예제 #21
0
from opentelemetry.instrumentation.flask.version import __version__
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
from opentelemetry.instrumentation.propagators import (
    get_global_response_propagator, )
from opentelemetry.propagate import extract
from opentelemetry.util._time import _time_ns
from opentelemetry.util.http import get_excluded_urls

_logger = getLogger(__name__)

_ENVIRON_STARTTIME_KEY = "opentelemetry-flask.starttime_key"
_ENVIRON_SPAN_KEY = "opentelemetry-flask.span_key"
_ENVIRON_ACTIVATION_KEY = "opentelemetry-flask.activation_key"
_ENVIRON_TOKEN = "opentelemetry-flask.token"

_excluded_urls = get_excluded_urls("FLASK")


def get_default_span_name():
    span_name = ""
    try:
        span_name = flask.request.url_rule.rule
    except AttributeError:
        span_name = otel_wsgi.get_default_span_name(flask.request.environ)
    return span_name


def _rewrapped_app(wsgi_app):
    def _wrapped_app(wrapped_app_environ, start_response):
        # We want to measure the time for route matching, etc.
        # In theory, we could start the span here and use
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import fastapi
from starlette.routing import Match

from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
from opentelemetry.util.http import get_excluded_urls

_excluded_urls = get_excluded_urls("FASTAPI")


class FastAPIInstrumentor(BaseInstrumentor):
    """An instrumentor for FastAPI

    See `BaseInstrumentor`
    """

    _original_fastapi = None

    @staticmethod
    def instrument_app(app: fastapi.FastAPI):
        """Instrument an uninstrumented FastAPI application.
        """
        if not getattr(app, "is_instrumented_by_opentelemetry", False):