コード例 #1
0
 def test_http_user_agent_attribute(self):
     self.environ["HTTP_USER_AGENT"] = "test-useragent"
     expected = {"http.user_agent": "test-useragent"}
     self.assertGreaterEqual(
         otel_wsgi.collect_request_attributes(self.environ).items(),
         expected.items(),
     )
コード例 #2
0
def _before_request():
    if _excluded_urls.url_disabled(flask.request.url):
        return

    environ = flask.request.environ
    span_name = flask.request.endpoint or otel_wsgi.get_default_span_name(
        environ)
    token = context.attach(
        propagators.extract(otel_wsgi.get_header_from_environ, environ))

    tracer = trace.get_tracer(__name__, __version__)

    attributes = otel_wsgi.collect_request_attributes(environ)
    if flask.request.url_rule:
        # For 404 that result from no route found, etc, we
        # don't have a url_rule.
        attributes["http.route"] = flask.request.url_rule.rule
    span = tracer.start_span(
        span_name,
        kind=trace.SpanKind.SERVER,
        attributes=attributes,
        start_time=environ.get(_ENVIRON_STARTTIME_KEY),
    )
    activation = tracer.use_span(span, end_on_exit=True)
    activation.__enter__()
    environ[_ENVIRON_ACTIVATION_KEY] = activation
    environ[_ENVIRON_SPAN_KEY] = span
    environ[_ENVIRON_TOKEN] = token
コード例 #3
0
    def _before_request():
        if _excluded_urls.url_disabled(flask.request.url):
            return

        flask_request_environ = flask.request.environ
        span_name = name_callback()
        token = context.attach(
            extract(flask_request_environ, getter=otel_wsgi.wsgi_getter))

        tracer = trace.get_tracer(__name__, __version__)

        span = tracer.start_span(
            span_name,
            kind=trace.SpanKind.SERVER,
            start_time=flask_request_environ.get(_ENVIRON_STARTTIME_KEY),
        )
        if span.is_recording():
            attributes = otel_wsgi.collect_request_attributes(
                flask_request_environ)
            if flask.request.url_rule:
                # For 404 that result from no route found, etc, we
                # don't have a url_rule.
                attributes["http.route"] = flask.request.url_rule.rule
            for key, value in attributes.items():
                span.set_attribute(key, value)

        activation = trace.use_span(span, end_on_exit=True)
        activation.__enter__()  # pylint: disable=E1101
        flask_request_environ[_ENVIRON_ACTIVATION_KEY] = activation
        flask_request_environ[_ENVIRON_SPAN_KEY] = span
        flask_request_environ[_ENVIRON_TOKEN] = token
コード例 #4
0
    def process_view(self, request, view_func, view_args, view_kwargs):  # pylint: disable=unused-argument
        # 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

        # environ = {
        #     key.lower().replace('_', '-').replace("http-", "", 1): value
        #     for key, value in request.META.items()
        # }
        if self._excluded_urls.url_disabled(request.build_absolute_uri("?")):
            return

        environ = request.META

        token = attach(extract(get_header_from_environ, environ))

        tracer = get_tracer(__name__, __version__)

        attributes = collect_request_attributes(environ)

        span = tracer.start_span(
            view_func.__name__,
            kind=SpanKind.SERVER,
            attributes=attributes,
            start_time=environ.get(
                "opentelemetry-instrumentor-django.starttime_key"),
        )

        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
コード例 #5
0
 def test_request_attributes_pathless(self):
     self.environ["RAW_URI"] = ""
     expected = {SpanAttributes.HTTP_TARGET: ""}
     self.assertGreaterEqual(
         otel_wsgi.collect_request_attributes(self.environ).items(),
         expected.items(),
     )
コード例 #6
0
 def test_request_attributes_pathless(self):
     self.environ["RAW_URI"] = ""
     expected = {"http.target": ""}
     self.assertGreaterEqual(
         otel_wsgi.collect_request_attributes(self.environ).items(),
         expected.items(),
     )
コード例 #7
0
    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

        environ = request.META

        token = attach(extract(get_header_from_environ, environ))

        tracer = get_tracer(__name__, __version__)

        attributes = collect_request_attributes(environ)
        for attr in self._traced_request_attrs:
            value = getattr(request, attr, None)
            if value is not None:
                attributes[attr] = str(value)

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

        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
コード例 #8
0
ファイル: app.py プロジェクト: paulosman/python-otel-service
def server_request():
    with tracer.start_as_current_span(
            "server_request",
            context=propagators.extract(DictGetter(), request.headers),
            kind=trace.SpanKind.SERVER,
            attributes=collect_request_attributes(request.environ)):
        print(request.args.get("param"))
        return "served"
コード例 #9
0
def server_request():
    with tracer.start_as_current_span(
            "server_request",
            parent=propagators.extract(lambda dict_, key: dict_.get(key, []),
                                       request.headers)["current-span"],
            kind=trace.SpanKind.SERVER,
            attributes=collect_request_attributes(request.environ),
    ):
        print(request.args.get("param"))
        return "served"
コード例 #10
0
 def test_credential_removal(self):
     self.environ["HTTP_HOST"] = "username:[email protected]"
     self.environ["PATH_INFO"] = "/status/200"
     expected = {
         SpanAttributes.HTTP_URL: "http://httpbin.com/status/200",
         SpanAttributes.NET_HOST_PORT: 80,
     }
     self.assertGreaterEqual(
         otel_wsgi.collect_request_attributes(self.environ).items(),
         expected.items(),
     )
コード例 #11
0
    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
        # update_name later but that API is "highly discouraged" so
        # we better avoid it.
        wrapped_app_environ[_ENVIRON_STARTTIME_KEY] = _time_ns()
        start = default_timer()
        attributes = otel_wsgi.collect_request_attributes(wrapped_app_environ)
        active_requests_count_attrs = (
            otel_wsgi._parse_active_request_count_attrs(attributes))
        duration_attrs = otel_wsgi._parse_duration_attrs(attributes)
        active_requests_counter.add(1, active_requests_count_attrs)

        def _start_response(status, response_headers, *args, **kwargs):
            if flask.request and (
                    excluded_urls is None
                    or not excluded_urls.url_disabled(flask.request.url)):
                span = flask.request.environ.get(_ENVIRON_SPAN_KEY)

                propagator = get_global_response_propagator()
                if propagator:
                    propagator.inject(
                        response_headers,
                        setter=otel_wsgi.default_response_propagation_setter,
                    )

                if span:
                    otel_wsgi.add_response_attributes(span, status,
                                                      response_headers)
                    status_code = otel_wsgi._parse_status_code(status)
                    if status_code is not None:
                        duration_attrs[
                            SpanAttributes.HTTP_STATUS_CODE] = status_code
                    if (span.is_recording()
                            and span.kind == trace.SpanKind.SERVER):
                        custom_attributes = otel_wsgi.collect_custom_response_headers_attributes(
                            response_headers)
                        if len(custom_attributes) > 0:
                            span.set_attributes(custom_attributes)
                else:
                    _logger.warning(
                        "Flask environ's OpenTelemetry span "
                        "missing at _start_response(%s)",
                        status,
                    )
                if response_hook is not None:
                    response_hook(span, status, response_headers)
            return start_response(status, response_headers, *args, **kwargs)

        result = wsgi_app(wrapped_app_environ, _start_response)
        duration = max(round((default_timer() - start) * 1000), 0)
        duration_histogram.record(duration, duration_attrs)
        active_requests_counter.add(-1, active_requests_count_attrs)
        return result
コード例 #12
0
def _before_traversal(event):
    request = event.request
    request_environ = request.environ
    span_name = otel_wsgi.get_default_span_name(request_environ)

    enabled = request_environ.get(_ENVIRON_ENABLED_KEY)
    if enabled is None:
        _logger.warning(
            "Opentelemetry pyramid tween 'opentelemetry.instrumentation.pyramid.trace_tween_factory'"
            "was not called. Make sure that the tween is included in 'pyramid.tweens' if"
            "the tween list was created manually")
        return

    if not enabled:
        # Tracing not enabled, return
        return

    start_time = request_environ.get(_ENVIRON_STARTTIME_KEY)
    tracer = trace.get_tracer(__name__, __version__)

    if request.matched_route:
        span_name = request.matched_route.pattern
    else:
        span_name = otel_wsgi.get_default_span_name(request_environ)

    span, token = _start_internal_or_server_span(
        tracer=tracer,
        span_name=span_name,
        start_time=start_time,
        context_carrier=request_environ,
        context_getter=otel_wsgi.wsgi_getter,
    )

    if span.is_recording():
        attributes = otel_wsgi.collect_request_attributes(request_environ)
        if request.matched_route:
            attributes[
                SpanAttributes.HTTP_ROUTE] = request.matched_route.pattern
        for key, value in attributes.items():
            span.set_attribute(key, value)
        if span.kind == trace.SpanKind.SERVER:
            custom_attributes = (
                otel_wsgi.collect_custom_request_headers_attributes(
                    request_environ))
            if len(custom_attributes) > 0:
                span.set_attributes(custom_attributes)

    activation = trace.use_span(span, end_on_exit=True)
    activation.__enter__()  # pylint: disable=E1101
    request_environ[_ENVIRON_ACTIVATION_KEY] = activation
    request_environ[_ENVIRON_SPAN_KEY] = span
    if token:
        request_environ[_ENVIRON_TOKEN] = token
コード例 #13
0
 def test_request_attributes_with_conflicting_nonstandard_port(self):
     self.environ[
         "HTTP_HOST"] += ":8080"  # Note that we do not correct SERVER_PORT
     expected = {
         SpanAttributes.HTTP_HOST: "127.0.0.1:8080",
         SpanAttributes.HTTP_URL: "http://127.0.0.1:8080/",
         SpanAttributes.NET_HOST_PORT: 80,
     }
     self.assertGreaterEqual(
         otel_wsgi.collect_request_attributes(self.environ).items(),
         expected.items(),
     )
コード例 #14
0
 def test_request_attributes_with_full_request_uri(self):
     self.environ["HTTP_HOST"] = "127.0.0.1:8080"
     self.environ["REQUEST_METHOD"] = "CONNECT"
     self.environ[
         "REQUEST_URI"] = "127.0.0.1:8080"  # Might happen in a CONNECT request
     expected = {
         SpanAttributes.HTTP_HOST: "127.0.0.1:8080",
         SpanAttributes.HTTP_TARGET: "127.0.0.1:8080",
     }
     self.assertGreaterEqual(
         otel_wsgi.collect_request_attributes(self.environ).items(),
         expected.items(),
     )
コード例 #15
0
    def __call__(self, env, start_response):
        # pylint: disable=E1101
        if self._otel_excluded_urls.url_disabled(env.get("PATH_INFO", "/")):
            return super().__call__(env, start_response)

        start_time = _time_ns()

        span, token = _start_internal_or_server_span(
            tracer=self._otel_tracer,
            span_name=otel_wsgi.get_default_span_name(env),
            start_time=start_time,
            context_carrier=env,
            context_getter=otel_wsgi.wsgi_getter,
        )

        if span.is_recording():
            attributes = otel_wsgi.collect_request_attributes(env)
            for key, value in attributes.items():
                span.set_attribute(key, value)
            if span.is_recording() and span.kind == trace.SpanKind.SERVER:
                custom_attributes = (
                    otel_wsgi.collect_custom_request_headers_attributes(env)
                )
                if len(custom_attributes) > 0:
                    span.set_attributes(custom_attributes)

        activation = trace.use_span(span, end_on_exit=True)
        activation.__enter__()
        env[_ENVIRON_SPAN_KEY] = span
        env[_ENVIRON_ACTIVATION_KEY] = activation

        def _start_response(status, response_headers, *args, **kwargs):
            response = start_response(
                status, response_headers, *args, **kwargs
            )
            activation.__exit__(None, None, None)
            if token is not None:
                context.detach(token)
            return response

        try:
            return super().__call__(env, _start_response)
        except Exception as exc:
            activation.__exit__(
                type(exc),
                exc,
                getattr(exc, "__traceback__", None),
            )
            if token is not None:
                context.detach(token)
            raise
コード例 #16
0
    def test_request_attributes(self):
        self.environ["QUERY_STRING"] = "foo=bar"

        attrs = otel_wsgi.collect_request_attributes(self.environ)
        self.assertDictEqual(
            attrs,
            {
                "http.method": "GET",
                "http.host": "127.0.0.1",
                "http.url": "http://127.0.0.1/?foo=bar",
                "net.host.port": 80,
                "http.scheme": "http",
                "http.server_name": "127.0.0.1",
                "http.flavor": "1.0",
            },
        )
コード例 #17
0
    def test_request_attributes(self):
        self.environ["QUERY_STRING"] = "foo=bar"

        attrs = otel_wsgi.collect_request_attributes(self.environ)
        self.assertDictEqual(
            attrs,
            {
                SpanAttributes.HTTP_METHOD: "GET",
                SpanAttributes.HTTP_HOST: "127.0.0.1",
                SpanAttributes.HTTP_URL: "http://127.0.0.1/?foo=bar",
                SpanAttributes.NET_HOST_PORT: 80,
                SpanAttributes.HTTP_SCHEME: "http",
                SpanAttributes.HTTP_SERVER_NAME: "127.0.0.1",
                SpanAttributes.HTTP_FLAVOR: "1.0",
            },
        )
コード例 #18
0
def _before_traversal(event):
    request = event.request
    request_environ = request.environ
    span_name = otel_wsgi.get_default_span_name(request_environ)

    enabled = request_environ.get(_ENVIRON_ENABLED_KEY)
    if enabled is None:
        _logger.warning(
            "Opentelemetry pyramid tween 'opentelemetry.instrumentation.pyramid.trace_tween_factory'"
            "was not called. Make sure that the tween is included in 'pyramid.tweens' if"
            "the tween list was created manually")
        return

    if not enabled:
        # Tracing not enabled, return
        return

    start_time = request_environ.get(_ENVIRON_STARTTIME_KEY)

    token = context.attach(
        propagators.extract(otel_wsgi.carrier_getter, request_environ))
    tracer = trace.get_tracer(__name__, __version__)

    if request.matched_route:
        span_name = request.matched_route.pattern
    else:
        span_name = otel_wsgi.get_default_span_name(request_environ)

    span = tracer.start_span(
        span_name,
        kind=trace.SpanKind.SERVER,
        start_time=start_time,
    )

    if span.is_recording():
        attributes = otel_wsgi.collect_request_attributes(request_environ)
        if request.matched_route:
            attributes["http.route"] = request.matched_route.pattern
        for key, value in attributes.items():
            span.set_attribute(key, value)

    activation = tracer.use_span(span, end_on_exit=True)
    activation.__enter__()
    request_environ[_ENVIRON_ACTIVATION_KEY] = activation
    request_environ[_ENVIRON_SPAN_KEY] = span
    request_environ[_ENVIRON_TOKEN] = token
コード例 #19
0
    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(request_meta, getter=wsgi_getter))

        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 = 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
        request.META[self._environ_token] = token

        if _DjangoMiddleware._otel_request_hook:
            _DjangoMiddleware._otel_request_hook(  # pylint: disable=not-callable
                span, request
            )
コード例 #20
0
    def validate_url(self, expected_url, raw=False, has_host=True):
        parts = urlsplit(expected_url)
        expected = {
            "http.scheme": parts.scheme,
            "host.port": parts.port or (80 if parts.scheme == "http" else 443),
            "http.server_name": parts.hostname,  # Not true in the general case, but for all tests.
        }
        if raw:
            expected["http.target"] = expected_url.split(parts.netloc, 1)[1]
        else:
            expected["http.url"] = expected_url
        if has_host:
            expected["http.host"] = parts.hostname

        attrs = otel_wsgi.collect_request_attributes(self.environ)
        self.assertGreaterEqual(
            attrs.items(), expected.items(), expected_url + " expected."
        )
コード例 #21
0
    def _before_request():
        if excluded_urls and excluded_urls.url_disabled(flask.request.url):
            return
        flask_request_environ = flask.request.environ
        span_name = get_default_span_name()

        token = ctx = span_kind = None

        if trace.get_current_span() is trace.INVALID_SPAN:
            ctx = extract(flask_request_environ, getter=otel_wsgi.wsgi_getter)
            token = context.attach(ctx)
            span_kind = trace.SpanKind.SERVER
        else:
            ctx = context.get_current()
            span_kind = trace.SpanKind.INTERNAL

        span = tracer.start_span(
            span_name,
            ctx,
            kind=span_kind,
            start_time=flask_request_environ.get(_ENVIRON_STARTTIME_KEY),
        )

        if request_hook:
            request_hook(span, flask_request_environ)

        if span.is_recording():
            attributes = otel_wsgi.collect_request_attributes(
                flask_request_environ)
            if flask.request.url_rule:
                # For 404 that result from no route found, etc, we
                # don't have a url_rule.
                attributes[
                    SpanAttributes.HTTP_ROUTE] = flask.request.url_rule.rule
            for key, value in attributes.items():
                span.set_attribute(key, value)

        activation = trace.use_span(span, end_on_exit=True)
        activation.__enter__()  # pylint: disable=E1101
        flask_request_environ[_ENVIRON_ACTIVATION_KEY] = activation
        flask_request_environ[_ENVIRON_SPAN_KEY] = span
        flask_request_environ[_ENVIRON_TOKEN] = token
コード例 #22
0
    def __call__(self, env, start_response):
        # pylint: disable=E1101
        if _excluded_urls.url_disabled(env.get("PATH_INFO", "/")):
            return super().__call__(env, start_response)

        start_time = _time_ns()

        token = context.attach(extract(env, getter=otel_wsgi.wsgi_getter))
        span = self._tracer.start_span(
            otel_wsgi.get_default_span_name(env),
            kind=trace.SpanKind.SERVER,
            start_time=start_time,
        )
        if span.is_recording():
            attributes = otel_wsgi.collect_request_attributes(env)
            for key, value in attributes.items():
                span.set_attribute(key, value)

        activation = trace.use_span(span, end_on_exit=True)
        activation.__enter__()
        env[_ENVIRON_SPAN_KEY] = span
        env[_ENVIRON_ACTIVATION_KEY] = activation

        def _start_response(status, response_headers, *args, **kwargs):
            otel_wsgi.add_response_attributes(span, status, response_headers)
            response = start_response(status, response_headers, *args,
                                      **kwargs)
            activation.__exit__(None, None, None)
            context.detach(token)
            return response

        try:
            return super().__call__(env, _start_response)
        except Exception as exc:
            activation.__exit__(
                type(exc),
                exc,
                getattr(exc, "__traceback__", None),
            )
            context.detach(token)
            raise
コード例 #23
0
    def validate_url(self, expected_url, raw=False, has_host=True):
        parts = urlsplit(expected_url)
        expected = {
            SpanAttributes.HTTP_SCHEME: parts.scheme,
            SpanAttributes.NET_HOST_PORT: parts.port
            or (80 if parts.scheme == "http" else 443),
            SpanAttributes.HTTP_SERVER_NAME: parts.hostname,  # Not true in the general case, but for all tests.
        }
        if raw:
            expected[SpanAttributes.HTTP_TARGET] = expected_url.split(
                parts.netloc, 1
            )[1]
        else:
            expected[SpanAttributes.HTTP_URL] = expected_url
        if has_host:
            expected[SpanAttributes.HTTP_HOST] = parts.hostname

        attrs = otel_wsgi.collect_request_attributes(self.environ)
        self.assertGreaterEqual(
            attrs.items(), expected.items(), expected_url + " expected."
        )
コード例 #24
0
    def _before_request():
        if excluded_urls and excluded_urls.url_disabled(flask.request.url):
            return
        flask_request_environ = flask.request.environ
        span_name = get_default_span_name()

        span, token = _start_internal_or_server_span(
            tracer=tracer,
            span_name=span_name,
            start_time=flask_request_environ.get(_ENVIRON_STARTTIME_KEY),
            context_carrier=flask_request_environ,
            context_getter=otel_wsgi.wsgi_getter,
        )

        if request_hook:
            request_hook(span, flask_request_environ)

        if span.is_recording():
            attributes = otel_wsgi.collect_request_attributes(
                flask_request_environ)
            if flask.request.url_rule:
                # For 404 that result from no route found, etc, we
                # don't have a url_rule.
                attributes[
                    SpanAttributes.HTTP_ROUTE] = flask.request.url_rule.rule
            for key, value in attributes.items():
                span.set_attribute(key, value)
            if span.is_recording() and span.kind == trace.SpanKind.SERVER:
                custom_attributes = (
                    otel_wsgi.collect_custom_request_headers_attributes(
                        flask_request_environ))
                if len(custom_attributes) > 0:
                    span.set_attributes(custom_attributes)

        activation = trace.use_span(span, end_on_exit=True)
        activation.__enter__()  # pylint: disable=E1101
        flask_request_environ[_ENVIRON_ACTIVATION_KEY] = activation
        flask_request_environ[_ENVIRON_SPAN_KEY] = span
        flask_request_environ[_ENVIRON_TOKEN] = token
コード例 #25
0
    def __call__(self, env, start_response):
        if _excluded_urls.url_disabled(env.get("PATH_INFO", "/")):
            return super().__call__(env, start_response)

        start_time = time_ns()

        token = context.attach(
            propagators.extract(otel_wsgi.get_header_from_environ, env))
        attributes = otel_wsgi.collect_request_attributes(env)
        span = self._tracer.start_span(
            otel_wsgi.get_default_span_name(env),
            kind=trace.SpanKind.SERVER,
            attributes=attributes,
            start_time=start_time,
        )
        activation = self._tracer.use_span(span, end_on_exit=True)
        activation.__enter__()
        env[_ENVIRON_SPAN_KEY] = span
        env[_ENVIRON_ACTIVATION_KEY] = activation

        def _start_response(status, response_headers, *args, **kwargs):
            otel_wsgi.add_response_attributes(span, status, response_headers)
            response = start_response(status, response_headers, *args,
                                      **kwargs)
            activation.__exit__(None, None, None)
            context.detach(token)
            return response

        try:
            return super().__call__(env, _start_response)
        except Exception as exc:
            activation.__exit__(
                type(exc),
                exc,
                getattr(exc, "__traceback__", None),
            )
            context.detach(token)
            raise
コード例 #26
0
def _before_request():
    if _excluded_urls.url_disabled(flask.request.url):
        return

    environ = flask.request.environ
    span_name = None
    try:
        span_name = flask.request.url_rule.rule
    except AttributeError:
        pass
    if span_name is None:
        span_name = otel_wsgi.get_default_span_name(environ)
    token = context.attach(
        propagators.extract(otel_wsgi.carrier_getter, environ)
    )

    tracer = trace.get_tracer(__name__, __version__)

    span = tracer.start_span(
        span_name,
        kind=trace.SpanKind.SERVER,
        start_time=environ.get(_ENVIRON_STARTTIME_KEY),
    )
    if span.is_recording():
        attributes = otel_wsgi.collect_request_attributes(environ)
        if flask.request.url_rule:
            # For 404 that result from no route found, etc, we
            # don't have a url_rule.
            attributes["http.route"] = flask.request.url_rule.rule
        for key, value in attributes.items():
            span.set_attribute(key, value)

    activation = tracer.use_span(span, end_on_exit=True)
    activation.__enter__()
    environ[_ENVIRON_ACTIVATION_KEY] = activation
    environ[_ENVIRON_SPAN_KEY] = span
    environ[_ENVIRON_TOKEN] = token