We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Uninstrument Existing fastapi instances
Existing instances shouldn't continue to send any telemetry after the `uninstrument`.

_Originally posted by @srikanthccv in https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1199#discussion_r957094027_

`uninstrument` method of `FastAPIInstrumentor` restore the original `fastapi.FastAPI` class, but all the existing objects of the earlier instrumented class do not get uninstrumented, which is not the expected behaviour.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/__init__.py`
Content:
```
1 # Copyright The OpenTelemetry Authors
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 """
16 Usage
17 -----
18 
19 .. code-block:: python
20 
21     import fastapi
22     from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
23 
24     app = fastapi.FastAPI()
25 
26     @app.get("/foobar")
27     async def foobar():
28         return {"message": "hello world"}
29 
30     FastAPIInstrumentor.instrument_app(app)
31 
32 Configuration
33 -------------
34 
35 Exclude lists
36 *************
37 To exclude certain URLs from being tracked, set the environment variable ``OTEL_PYTHON_FASTAPI_EXCLUDED_URLS``
38 (or ``OTEL_PYTHON_EXCLUDED_URLS`` as fallback) with comma delimited regexes representing which URLs to exclude.
39 
40 For example,
41 
42 ::
43 
44     export OTEL_PYTHON_FASTAPI_EXCLUDED_URLS="client/.*/info,healthcheck"
45 
46 will exclude requests such as ``https://site/client/123/info`` and ``https://site/xyz/healthcheck``.
47 
48 You can also pass the comma delimited regexes to the ``instrument_app`` method directly:
49 
50 .. code-block:: python
51 
52     FastAPIInstrumentor.instrument_app(app, excluded_urls="client/.*/info,healthcheck")
53 
54 Request/Response hooks
55 **********************
56 
57 Utilize request/response hooks to execute custom logic to be performed before/after performing a request. The server request hook takes in a server span and ASGI
58 scope object for every incoming request. The client request hook is called with the internal span and an ASGI scope which is sent as a dictionary for when the method receive is called.
59 The client response hook is called with the internal span and an ASGI event which is sent as a dictionary for when the method send is called.
60 
61 .. code-block:: python
62 
63     def server_request_hook(span: Span, scope: dict):
64         if span and span.is_recording():
65             span.set_attribute("custom_user_attribute_from_request_hook", "some-value")
66 
67     def client_request_hook(span: Span, scope: dict):
68         if span and span.is_recording():
69             span.set_attribute("custom_user_attribute_from_client_request_hook", "some-value")
70 
71     def client_response_hook(span: Span, message: dict):
72         if span and span.is_recording():
73             span.set_attribute("custom_user_attribute_from_response_hook", "some-value")
74 
75    FastAPIInstrumentor().instrument(server_request_hook=server_request_hook, client_request_hook=client_request_hook, client_response_hook=client_response_hook)
76 
77 Capture HTTP request and response headers
78 *****************************************
79 You can configure the agent to capture predefined HTTP headers as span attributes, according to the `semantic convention <https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#http-request-and-response-headers>`_.
80 
81 Request headers
82 ***************
83 To capture predefined HTTP request headers as span attributes, set the environment variable ``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST``
84 to a comma-separated list of HTTP header names.
85 
86 For example,
87 
88 ::
89 
90     export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST="content-type,custom_request_header"
91 
92 will extract ``content-type`` and ``custom_request_header`` from request headers and add them as span attributes.
93 
94 It is recommended that you should give the correct names of the headers to be captured in the environment variable.
95 Request header names in fastapi are case insensitive. So, giving header name as ``CUStom-Header`` in environment variable will be able capture header with name ``custom-header``.
96 
97 The name of the added span attribute will follow the format ``http.request.header.<header_name>`` where ``<header_name>`` being the normalized HTTP header name (lowercase, with - characters replaced by _ ).
98 The value of the attribute will be single item list containing all the header values.
99 
100 Example of the added span attribute,
101 ``http.request.header.custom_request_header = ["<value1>,<value2>"]``
102 
103 Response headers
104 ****************
105 To capture predefined HTTP response headers as span attributes, set the environment variable ``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE``
106 to a comma-separated list of HTTP header names.
107 
108 For example,
109 
110 ::
111 
112     export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE="content-type,custom_response_header"
113 
114 will extract ``content-type`` and ``custom_response_header`` from response headers and add them as span attributes.
115 
116 It is recommended that you should give the correct names of the headers to be captured in the environment variable.
117 Response header names captured in fastapi are case insensitive. So, giving header name as ``CUStomHeader`` in environment variable will be able capture header with name ``customheader``.
118 
119 The name of the added span attribute will follow the format ``http.response.header.<header_name>`` where ``<header_name>`` being the normalized HTTP header name (lowercase, with - characters replaced by _ ).
120 The value of the attribute will be single item list containing all the header values.
121 
122 Example of the added span attribute,
123 ``http.response.header.custom_response_header = ["<value1>,<value2>"]``
124 
125 Note:
126     Environment variable names to capture http headers are still experimental, and thus are subject to change.
127 
128 API
129 ---
130 """
131 import logging
132 import typing
133 from typing import Collection
134 
135 import fastapi
136 from starlette.routing import Match
137 
138 from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware
139 from opentelemetry.instrumentation.asgi.package import _instruments
140 from opentelemetry.instrumentation.fastapi.version import __version__
141 from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
142 from opentelemetry.metrics import get_meter
143 from opentelemetry.semconv.trace import SpanAttributes
144 from opentelemetry.trace import Span
145 from opentelemetry.util.http import get_excluded_urls, parse_excluded_urls
146 
147 _excluded_urls_from_env = get_excluded_urls("FASTAPI")
148 _logger = logging.getLogger(__name__)
149 
150 _ServerRequestHookT = typing.Optional[typing.Callable[[Span, dict], None]]
151 _ClientRequestHookT = typing.Optional[typing.Callable[[Span, dict], None]]
152 _ClientResponseHookT = typing.Optional[typing.Callable[[Span, dict], None]]
153 
154 
155 class FastAPIInstrumentor(BaseInstrumentor):
156     """An instrumentor for FastAPI
157 
158     See `BaseInstrumentor`
159     """
160 
161     _original_fastapi = None
162 
163     @staticmethod
164     def instrument_app(
165         app: fastapi.FastAPI,
166         server_request_hook: _ServerRequestHookT = None,
167         client_request_hook: _ClientRequestHookT = None,
168         client_response_hook: _ClientResponseHookT = None,
169         tracer_provider=None,
170         meter_provider=None,
171         excluded_urls=None,
172     ):
173         """Instrument an uninstrumented FastAPI application."""
174         if not hasattr(app, "_is_instrumented_by_opentelemetry"):
175             app._is_instrumented_by_opentelemetry = False
176 
177         if not getattr(app, "_is_instrumented_by_opentelemetry", False):
178             if excluded_urls is None:
179                 excluded_urls = _excluded_urls_from_env
180             else:
181                 excluded_urls = parse_excluded_urls(excluded_urls)
182             meter = get_meter(__name__, __version__, meter_provider)
183 
184             app.add_middleware(
185                 OpenTelemetryMiddleware,
186                 excluded_urls=excluded_urls,
187                 default_span_details=_get_route_details,
188                 server_request_hook=server_request_hook,
189                 client_request_hook=client_request_hook,
190                 client_response_hook=client_response_hook,
191                 tracer_provider=tracer_provider,
192                 meter=meter,
193             )
194             app._is_instrumented_by_opentelemetry = True
195         else:
196             _logger.warning(
197                 "Attempting to instrument FastAPI app while already instrumented"
198             )
199 
200     @staticmethod
201     def uninstrument_app(app: fastapi.FastAPI):
202         app.user_middleware = [
203             x
204             for x in app.user_middleware
205             if x.cls is not OpenTelemetryMiddleware
206         ]
207         app.middleware_stack = app.build_middleware_stack()
208         app._is_instrumented_by_opentelemetry = False
209 
210     def instrumentation_dependencies(self) -> Collection[str]:
211         return _instruments
212 
213     def _instrument(self, **kwargs):
214         self._original_fastapi = fastapi.FastAPI
215         _InstrumentedFastAPI._tracer_provider = kwargs.get("tracer_provider")
216         _InstrumentedFastAPI._server_request_hook = kwargs.get(
217             "server_request_hook"
218         )
219         _InstrumentedFastAPI._client_request_hook = kwargs.get(
220             "client_request_hook"
221         )
222         _InstrumentedFastAPI._client_response_hook = kwargs.get(
223             "client_response_hook"
224         )
225         _excluded_urls = kwargs.get("excluded_urls")
226         _InstrumentedFastAPI._excluded_urls = (
227             _excluded_urls_from_env
228             if _excluded_urls is None
229             else parse_excluded_urls(_excluded_urls)
230         )
231         _InstrumentedFastAPI._meter_provider = kwargs.get("meter_provider")
232         fastapi.FastAPI = _InstrumentedFastAPI
233 
234     def _uninstrument(self, **kwargs):
235         fastapi.FastAPI = self._original_fastapi
236 
237 
238 class _InstrumentedFastAPI(fastapi.FastAPI):
239     _tracer_provider = None
240     _meter_provider = None
241     _excluded_urls = None
242     _server_request_hook: _ServerRequestHookT = None
243     _client_request_hook: _ClientRequestHookT = None
244     _client_response_hook: _ClientResponseHookT = None
245 
246     def __init__(self, *args, **kwargs):
247         super().__init__(*args, **kwargs)
248         meter = get_meter(
249             __name__, __version__, _InstrumentedFastAPI._meter_provider
250         )
251         self.add_middleware(
252             OpenTelemetryMiddleware,
253             excluded_urls=_InstrumentedFastAPI._excluded_urls,
254             default_span_details=_get_route_details,
255             server_request_hook=_InstrumentedFastAPI._server_request_hook,
256             client_request_hook=_InstrumentedFastAPI._client_request_hook,
257             client_response_hook=_InstrumentedFastAPI._client_response_hook,
258             tracer_provider=_InstrumentedFastAPI._tracer_provider,
259             meter=meter,
260         )
261         self._is_instrumented_by_opentelemetry = True
262 
263 
264 def _get_route_details(scope):
265     """Callback to retrieve the fastapi route being served.
266 
267     TODO: there is currently no way to retrieve http.route from
268     a starlette application from scope.
269 
270     See: https://github.com/encode/starlette/pull/804
271     """
272     app = scope["app"]
273     route = None
274     for starlette_route in app.routes:
275         match, _ = starlette_route.matches(scope)
276         if match == Match.FULL:
277             route = starlette_route.path
278             break
279         if match == Match.PARTIAL:
280             route = starlette_route.path
281     # method only exists for http, if websocket
282     # leave it blank.
283     span_name = route or scope.get("method", "")
284     attributes = {}
285     if route:
286         attributes[SpanAttributes.HTTP_ROUTE] = route
287     return span_name, attributes
288
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/__init__.py b/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/__init__.py
--- a/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/__init__.py
+++ b/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/__init__.py
@@ -192,6 +192,8 @@
                 meter=meter,
             )
             app._is_instrumented_by_opentelemetry = True
+            if app not in _InstrumentedFastAPI._instrumented_fastapi_apps:
+                _InstrumentedFastAPI._instrumented_fastapi_apps.add(app)
         else:
             _logger.warning(
                 "Attempting to instrument FastAPI app while already instrumented"
@@ -232,6 +234,9 @@
         fastapi.FastAPI = _InstrumentedFastAPI
 
     def _uninstrument(self, **kwargs):
+        for instance in _InstrumentedFastAPI._instrumented_fastapi_apps:
+            self.uninstrument_app(instance)
+        _InstrumentedFastAPI._instrumented_fastapi_apps.clear()
         fastapi.FastAPI = self._original_fastapi
 
 
@@ -242,6 +247,7 @@
     _server_request_hook: _ServerRequestHookT = None
     _client_request_hook: _ClientRequestHookT = None
     _client_response_hook: _ClientResponseHookT = None
+    _instrumented_fastapi_apps = set()
 
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
@@ -259,6 +265,11 @@
             meter=meter,
         )
         self._is_instrumented_by_opentelemetry = True
+        _InstrumentedFastAPI._instrumented_fastapi_apps.add(self)
+
+    def __del__(self):
+        if self in _InstrumentedFastAPI._instrumented_fastapi_apps:
+            _InstrumentedFastAPI._instrumented_fastapi_apps.remove(self)
 
 
 def _get_route_details(scope):

{"golden_diff": "diff --git a/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/__init__.py b/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/__init__.py\n--- a/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/__init__.py\n+++ b/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/__init__.py\n@@ -192,6 +192,8 @@\n                 meter=meter,\n             )\n             app._is_instrumented_by_opentelemetry = True\n+            if app not in _InstrumentedFastAPI._instrumented_fastapi_apps:\n+                _InstrumentedFastAPI._instrumented_fastapi_apps.add(app)\n         else:\n             _logger.warning(\n                 \"Attempting to instrument FastAPI app while already instrumented\"\n@@ -232,6 +234,9 @@\n         fastapi.FastAPI = _InstrumentedFastAPI\n \n     def _uninstrument(self, **kwargs):\n+        for instance in _InstrumentedFastAPI._instrumented_fastapi_apps:\n+            self.uninstrument_app(instance)\n+        _InstrumentedFastAPI._instrumented_fastapi_apps.clear()\n         fastapi.FastAPI = self._original_fastapi\n \n \n@@ -242,6 +247,7 @@\n     _server_request_hook: _ServerRequestHookT = None\n     _client_request_hook: _ClientRequestHookT = None\n     _client_response_hook: _ClientResponseHookT = None\n+    _instrumented_fastapi_apps = set()\n \n     def __init__(self, *args, **kwargs):\n         super().__init__(*args, **kwargs)\n@@ -259,6 +265,11 @@\n             meter=meter,\n         )\n         self._is_instrumented_by_opentelemetry = True\n+        _InstrumentedFastAPI._instrumented_fastapi_apps.add(self)\n+\n+    def __del__(self):\n+        if self in _InstrumentedFastAPI._instrumented_fastapi_apps:\n+            _InstrumentedFastAPI._instrumented_fastapi_apps.remove(self)\n \n \n def _get_route_details(scope):\n", "issue": "Uninstrument Existing fastapi instances\nExisting instances shouldn't continue to send any telemetry after the `uninstrument`.\r\n\r\n_Originally posted by @srikanthccv in https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1199#discussion_r957094027_\r\n\r\n`uninstrument` method of `FastAPIInstrumentor` restore the original `fastapi.FastAPI` class, but all the existing objects of the earlier instrumented class do not get uninstrumented, which is not the expected behaviour.\n", "before_files": [{"content": "# Copyright The OpenTelemetry Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nUsage\n-----\n\n.. code-block:: python\n\n    import fastapi\n    from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor\n\n    app = fastapi.FastAPI()\n\n    @app.get(\"/foobar\")\n    async def foobar():\n        return {\"message\": \"hello world\"}\n\n    FastAPIInstrumentor.instrument_app(app)\n\nConfiguration\n-------------\n\nExclude lists\n*************\nTo exclude certain URLs from being tracked, set the environment variable ``OTEL_PYTHON_FASTAPI_EXCLUDED_URLS``\n(or ``OTEL_PYTHON_EXCLUDED_URLS`` as fallback) with comma delimited regexes representing which URLs to exclude.\n\nFor example,\n\n::\n\n    export OTEL_PYTHON_FASTAPI_EXCLUDED_URLS=\"client/.*/info,healthcheck\"\n\nwill exclude requests such as ``https://site/client/123/info`` and ``https://site/xyz/healthcheck``.\n\nYou can also pass the comma delimited regexes to the ``instrument_app`` method directly:\n\n.. code-block:: python\n\n    FastAPIInstrumentor.instrument_app(app, excluded_urls=\"client/.*/info,healthcheck\")\n\nRequest/Response hooks\n**********************\n\nUtilize request/response hooks to execute custom logic to be performed before/after performing a request. The server request hook takes in a server span and ASGI\nscope object for every incoming request. The client request hook is called with the internal span and an ASGI scope which is sent as a dictionary for when the method receive is called.\nThe client response hook is called with the internal span and an ASGI event which is sent as a dictionary for when the method send is called.\n\n.. code-block:: python\n\n    def server_request_hook(span: Span, scope: dict):\n        if span and span.is_recording():\n            span.set_attribute(\"custom_user_attribute_from_request_hook\", \"some-value\")\n\n    def client_request_hook(span: Span, scope: dict):\n        if span and span.is_recording():\n            span.set_attribute(\"custom_user_attribute_from_client_request_hook\", \"some-value\")\n\n    def client_response_hook(span: Span, message: dict):\n        if span and span.is_recording():\n            span.set_attribute(\"custom_user_attribute_from_response_hook\", \"some-value\")\n\n   FastAPIInstrumentor().instrument(server_request_hook=server_request_hook, client_request_hook=client_request_hook, client_response_hook=client_response_hook)\n\nCapture HTTP request and response headers\n*****************************************\nYou can configure the agent to capture predefined HTTP headers as span attributes, according to the `semantic convention <https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#http-request-and-response-headers>`_.\n\nRequest headers\n***************\nTo capture predefined HTTP request headers as span attributes, set the environment variable ``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST``\nto a comma-separated list of HTTP header names.\n\nFor example,\n\n::\n\n    export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST=\"content-type,custom_request_header\"\n\nwill extract ``content-type`` and ``custom_request_header`` from request headers and add them as span attributes.\n\nIt is recommended that you should give the correct names of the headers to be captured in the environment variable.\nRequest header names in fastapi are case insensitive. So, giving header name as ``CUStom-Header`` in environment variable will be able capture header with name ``custom-header``.\n\nThe name of the added span attribute will follow the format ``http.request.header.<header_name>`` where ``<header_name>`` being the normalized HTTP header name (lowercase, with - characters replaced by _ ).\nThe value of the attribute will be single item list containing all the header values.\n\nExample of the added span attribute,\n``http.request.header.custom_request_header = [\"<value1>,<value2>\"]``\n\nResponse headers\n****************\nTo capture predefined HTTP response headers as span attributes, set the environment variable ``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE``\nto a comma-separated list of HTTP header names.\n\nFor example,\n\n::\n\n    export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE=\"content-type,custom_response_header\"\n\nwill extract ``content-type`` and ``custom_response_header`` from response headers and add them as span attributes.\n\nIt is recommended that you should give the correct names of the headers to be captured in the environment variable.\nResponse header names captured in fastapi are case insensitive. So, giving header name as ``CUStomHeader`` in environment variable will be able capture header with name ``customheader``.\n\nThe name of the added span attribute will follow the format ``http.response.header.<header_name>`` where ``<header_name>`` being the normalized HTTP header name (lowercase, with - characters replaced by _ ).\nThe value of the attribute will be single item list containing all the header values.\n\nExample of the added span attribute,\n``http.response.header.custom_response_header = [\"<value1>,<value2>\"]``\n\nNote:\n    Environment variable names to capture http headers are still experimental, and thus are subject to change.\n\nAPI\n---\n\"\"\"\nimport logging\nimport typing\nfrom typing import Collection\n\nimport fastapi\nfrom starlette.routing import Match\n\nfrom opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware\nfrom opentelemetry.instrumentation.asgi.package import _instruments\nfrom opentelemetry.instrumentation.fastapi.version import __version__\nfrom opentelemetry.instrumentation.instrumentor import BaseInstrumentor\nfrom opentelemetry.metrics import get_meter\nfrom opentelemetry.semconv.trace import SpanAttributes\nfrom opentelemetry.trace import Span\nfrom opentelemetry.util.http import get_excluded_urls, parse_excluded_urls\n\n_excluded_urls_from_env = get_excluded_urls(\"FASTAPI\")\n_logger = logging.getLogger(__name__)\n\n_ServerRequestHookT = typing.Optional[typing.Callable[[Span, dict], None]]\n_ClientRequestHookT = typing.Optional[typing.Callable[[Span, dict], None]]\n_ClientResponseHookT = typing.Optional[typing.Callable[[Span, dict], None]]\n\n\nclass FastAPIInstrumentor(BaseInstrumentor):\n    \"\"\"An instrumentor for FastAPI\n\n    See `BaseInstrumentor`\n    \"\"\"\n\n    _original_fastapi = None\n\n    @staticmethod\n    def instrument_app(\n        app: fastapi.FastAPI,\n        server_request_hook: _ServerRequestHookT = None,\n        client_request_hook: _ClientRequestHookT = None,\n        client_response_hook: _ClientResponseHookT = None,\n        tracer_provider=None,\n        meter_provider=None,\n        excluded_urls=None,\n    ):\n        \"\"\"Instrument an uninstrumented FastAPI application.\"\"\"\n        if not hasattr(app, \"_is_instrumented_by_opentelemetry\"):\n            app._is_instrumented_by_opentelemetry = False\n\n        if not getattr(app, \"_is_instrumented_by_opentelemetry\", False):\n            if excluded_urls is None:\n                excluded_urls = _excluded_urls_from_env\n            else:\n                excluded_urls = parse_excluded_urls(excluded_urls)\n            meter = get_meter(__name__, __version__, meter_provider)\n\n            app.add_middleware(\n                OpenTelemetryMiddleware,\n                excluded_urls=excluded_urls,\n                default_span_details=_get_route_details,\n                server_request_hook=server_request_hook,\n                client_request_hook=client_request_hook,\n                client_response_hook=client_response_hook,\n                tracer_provider=tracer_provider,\n                meter=meter,\n            )\n            app._is_instrumented_by_opentelemetry = True\n        else:\n            _logger.warning(\n                \"Attempting to instrument FastAPI app while already instrumented\"\n            )\n\n    @staticmethod\n    def uninstrument_app(app: fastapi.FastAPI):\n        app.user_middleware = [\n            x\n            for x in app.user_middleware\n            if x.cls is not OpenTelemetryMiddleware\n        ]\n        app.middleware_stack = app.build_middleware_stack()\n        app._is_instrumented_by_opentelemetry = False\n\n    def instrumentation_dependencies(self) -> Collection[str]:\n        return _instruments\n\n    def _instrument(self, **kwargs):\n        self._original_fastapi = fastapi.FastAPI\n        _InstrumentedFastAPI._tracer_provider = kwargs.get(\"tracer_provider\")\n        _InstrumentedFastAPI._server_request_hook = kwargs.get(\n            \"server_request_hook\"\n        )\n        _InstrumentedFastAPI._client_request_hook = kwargs.get(\n            \"client_request_hook\"\n        )\n        _InstrumentedFastAPI._client_response_hook = kwargs.get(\n            \"client_response_hook\"\n        )\n        _excluded_urls = kwargs.get(\"excluded_urls\")\n        _InstrumentedFastAPI._excluded_urls = (\n            _excluded_urls_from_env\n            if _excluded_urls is None\n            else parse_excluded_urls(_excluded_urls)\n        )\n        _InstrumentedFastAPI._meter_provider = kwargs.get(\"meter_provider\")\n        fastapi.FastAPI = _InstrumentedFastAPI\n\n    def _uninstrument(self, **kwargs):\n        fastapi.FastAPI = self._original_fastapi\n\n\nclass _InstrumentedFastAPI(fastapi.FastAPI):\n    _tracer_provider = None\n    _meter_provider = None\n    _excluded_urls = None\n    _server_request_hook: _ServerRequestHookT = None\n    _client_request_hook: _ClientRequestHookT = None\n    _client_response_hook: _ClientResponseHookT = None\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        meter = get_meter(\n            __name__, __version__, _InstrumentedFastAPI._meter_provider\n        )\n        self.add_middleware(\n            OpenTelemetryMiddleware,\n            excluded_urls=_InstrumentedFastAPI._excluded_urls,\n            default_span_details=_get_route_details,\n            server_request_hook=_InstrumentedFastAPI._server_request_hook,\n            client_request_hook=_InstrumentedFastAPI._client_request_hook,\n            client_response_hook=_InstrumentedFastAPI._client_response_hook,\n            tracer_provider=_InstrumentedFastAPI._tracer_provider,\n            meter=meter,\n        )\n        self._is_instrumented_by_opentelemetry = True\n\n\ndef _get_route_details(scope):\n    \"\"\"Callback to retrieve the fastapi route being served.\n\n    TODO: there is currently no way to retrieve http.route from\n    a starlette application from scope.\n\n    See: https://github.com/encode/starlette/pull/804\n    \"\"\"\n    app = scope[\"app\"]\n    route = None\n    for starlette_route in app.routes:\n        match, _ = starlette_route.matches(scope)\n        if match == Match.FULL:\n            route = starlette_route.path\n            break\n        if match == Match.PARTIAL:\n            route = starlette_route.path\n    # method only exists for http, if websocket\n    # leave it blank.\n    span_name = route or scope.get(\"method\", \"\")\n    attributes = {}\n    if route:\n        attributes[SpanAttributes.HTTP_ROUTE] = route\n    return span_name, attributes\n", "path": "instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/__init__.py"}], "after_files": [{"content": "# Copyright The OpenTelemetry Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nUsage\n-----\n\n.. code-block:: python\n\n    import fastapi\n    from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor\n\n    app = fastapi.FastAPI()\n\n    @app.get(\"/foobar\")\n    async def foobar():\n        return {\"message\": \"hello world\"}\n\n    FastAPIInstrumentor.instrument_app(app)\n\nConfiguration\n-------------\n\nExclude lists\n*************\nTo exclude certain URLs from being tracked, set the environment variable ``OTEL_PYTHON_FASTAPI_EXCLUDED_URLS``\n(or ``OTEL_PYTHON_EXCLUDED_URLS`` as fallback) with comma delimited regexes representing which URLs to exclude.\n\nFor example,\n\n::\n\n    export OTEL_PYTHON_FASTAPI_EXCLUDED_URLS=\"client/.*/info,healthcheck\"\n\nwill exclude requests such as ``https://site/client/123/info`` and ``https://site/xyz/healthcheck``.\n\nYou can also pass the comma delimited regexes to the ``instrument_app`` method directly:\n\n.. code-block:: python\n\n    FastAPIInstrumentor.instrument_app(app, excluded_urls=\"client/.*/info,healthcheck\")\n\nRequest/Response hooks\n**********************\n\nUtilize request/response hooks to execute custom logic to be performed before/after performing a request. The server request hook takes in a server span and ASGI\nscope object for every incoming request. The client request hook is called with the internal span and an ASGI scope which is sent as a dictionary for when the method receive is called.\nThe client response hook is called with the internal span and an ASGI event which is sent as a dictionary for when the method send is called.\n\n.. code-block:: python\n\n    def server_request_hook(span: Span, scope: dict):\n        if span and span.is_recording():\n            span.set_attribute(\"custom_user_attribute_from_request_hook\", \"some-value\")\n\n    def client_request_hook(span: Span, scope: dict):\n        if span and span.is_recording():\n            span.set_attribute(\"custom_user_attribute_from_client_request_hook\", \"some-value\")\n\n    def client_response_hook(span: Span, message: dict):\n        if span and span.is_recording():\n            span.set_attribute(\"custom_user_attribute_from_response_hook\", \"some-value\")\n\n   FastAPIInstrumentor().instrument(server_request_hook=server_request_hook, client_request_hook=client_request_hook, client_response_hook=client_response_hook)\n\nCapture HTTP request and response headers\n*****************************************\nYou can configure the agent to capture predefined HTTP headers as span attributes, according to the `semantic convention <https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#http-request-and-response-headers>`_.\n\nRequest headers\n***************\nTo capture predefined HTTP request headers as span attributes, set the environment variable ``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST``\nto a comma-separated list of HTTP header names.\n\nFor example,\n\n::\n\n    export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST=\"content-type,custom_request_header\"\n\nwill extract ``content-type`` and ``custom_request_header`` from request headers and add them as span attributes.\n\nIt is recommended that you should give the correct names of the headers to be captured in the environment variable.\nRequest header names in fastapi are case insensitive. So, giving header name as ``CUStom-Header`` in environment variable will be able capture header with name ``custom-header``.\n\nThe name of the added span attribute will follow the format ``http.request.header.<header_name>`` where ``<header_name>`` being the normalized HTTP header name (lowercase, with - characters replaced by _ ).\nThe value of the attribute will be single item list containing all the header values.\n\nExample of the added span attribute,\n``http.request.header.custom_request_header = [\"<value1>,<value2>\"]``\n\nResponse headers\n****************\nTo capture predefined HTTP response headers as span attributes, set the environment variable ``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE``\nto a comma-separated list of HTTP header names.\n\nFor example,\n\n::\n\n    export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE=\"content-type,custom_response_header\"\n\nwill extract ``content-type`` and ``custom_response_header`` from response headers and add them as span attributes.\n\nIt is recommended that you should give the correct names of the headers to be captured in the environment variable.\nResponse header names captured in fastapi are case insensitive. So, giving header name as ``CUStomHeader`` in environment variable will be able capture header with name ``customheader``.\n\nThe name of the added span attribute will follow the format ``http.response.header.<header_name>`` where ``<header_name>`` being the normalized HTTP header name (lowercase, with - characters replaced by _ ).\nThe value of the attribute will be single item list containing all the header values.\n\nExample of the added span attribute,\n``http.response.header.custom_response_header = [\"<value1>,<value2>\"]``\n\nNote:\n    Environment variable names to capture http headers are still experimental, and thus are subject to change.\n\nAPI\n---\n\"\"\"\nimport logging\nimport typing\nfrom typing import Collection\n\nimport fastapi\nfrom starlette.routing import Match\n\nfrom opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware\nfrom opentelemetry.instrumentation.asgi.package import _instruments\nfrom opentelemetry.instrumentation.fastapi.version import __version__\nfrom opentelemetry.instrumentation.instrumentor import BaseInstrumentor\nfrom opentelemetry.metrics import get_meter\nfrom opentelemetry.semconv.trace import SpanAttributes\nfrom opentelemetry.trace import Span\nfrom opentelemetry.util.http import get_excluded_urls, parse_excluded_urls\n\n_excluded_urls_from_env = get_excluded_urls(\"FASTAPI\")\n_logger = logging.getLogger(__name__)\n\n_ServerRequestHookT = typing.Optional[typing.Callable[[Span, dict], None]]\n_ClientRequestHookT = typing.Optional[typing.Callable[[Span, dict], None]]\n_ClientResponseHookT = typing.Optional[typing.Callable[[Span, dict], None]]\n\n\nclass FastAPIInstrumentor(BaseInstrumentor):\n    \"\"\"An instrumentor for FastAPI\n\n    See `BaseInstrumentor`\n    \"\"\"\n\n    _original_fastapi = None\n\n    @staticmethod\n    def instrument_app(\n        app: fastapi.FastAPI,\n        server_request_hook: _ServerRequestHookT = None,\n        client_request_hook: _ClientRequestHookT = None,\n        client_response_hook: _ClientResponseHookT = None,\n        tracer_provider=None,\n        meter_provider=None,\n        excluded_urls=None,\n    ):\n        \"\"\"Instrument an uninstrumented FastAPI application.\"\"\"\n        if not hasattr(app, \"_is_instrumented_by_opentelemetry\"):\n            app._is_instrumented_by_opentelemetry = False\n\n        if not getattr(app, \"_is_instrumented_by_opentelemetry\", False):\n            if excluded_urls is None:\n                excluded_urls = _excluded_urls_from_env\n            else:\n                excluded_urls = parse_excluded_urls(excluded_urls)\n            meter = get_meter(__name__, __version__, meter_provider)\n\n            app.add_middleware(\n                OpenTelemetryMiddleware,\n                excluded_urls=excluded_urls,\n                default_span_details=_get_route_details,\n                server_request_hook=server_request_hook,\n                client_request_hook=client_request_hook,\n                client_response_hook=client_response_hook,\n                tracer_provider=tracer_provider,\n                meter=meter,\n            )\n            app._is_instrumented_by_opentelemetry = True\n            if app not in _InstrumentedFastAPI._instrumented_fastapi_apps:\n                _InstrumentedFastAPI._instrumented_fastapi_apps.add(app)\n        else:\n            _logger.warning(\n                \"Attempting to instrument FastAPI app while already instrumented\"\n            )\n\n    @staticmethod\n    def uninstrument_app(app: fastapi.FastAPI):\n        app.user_middleware = [\n            x\n            for x in app.user_middleware\n            if x.cls is not OpenTelemetryMiddleware\n        ]\n        app.middleware_stack = app.build_middleware_stack()\n        app._is_instrumented_by_opentelemetry = False\n\n    def instrumentation_dependencies(self) -> Collection[str]:\n        return _instruments\n\n    def _instrument(self, **kwargs):\n        self._original_fastapi = fastapi.FastAPI\n        _InstrumentedFastAPI._tracer_provider = kwargs.get(\"tracer_provider\")\n        _InstrumentedFastAPI._server_request_hook = kwargs.get(\n            \"server_request_hook\"\n        )\n        _InstrumentedFastAPI._client_request_hook = kwargs.get(\n            \"client_request_hook\"\n        )\n        _InstrumentedFastAPI._client_response_hook = kwargs.get(\n            \"client_response_hook\"\n        )\n        _excluded_urls = kwargs.get(\"excluded_urls\")\n        _InstrumentedFastAPI._excluded_urls = (\n            _excluded_urls_from_env\n            if _excluded_urls is None\n            else parse_excluded_urls(_excluded_urls)\n        )\n        _InstrumentedFastAPI._meter_provider = kwargs.get(\"meter_provider\")\n        fastapi.FastAPI = _InstrumentedFastAPI\n\n    def _uninstrument(self, **kwargs):\n        for instance in _InstrumentedFastAPI._instrumented_fastapi_apps:\n            self.uninstrument_app(instance)\n        _InstrumentedFastAPI._instrumented_fastapi_apps.clear()\n        fastapi.FastAPI = self._original_fastapi\n\n\nclass _InstrumentedFastAPI(fastapi.FastAPI):\n    _tracer_provider = None\n    _meter_provider = None\n    _excluded_urls = None\n    _server_request_hook: _ServerRequestHookT = None\n    _client_request_hook: _ClientRequestHookT = None\n    _client_response_hook: _ClientResponseHookT = None\n    _instrumented_fastapi_apps = set()\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        meter = get_meter(\n            __name__, __version__, _InstrumentedFastAPI._meter_provider\n        )\n        self.add_middleware(\n            OpenTelemetryMiddleware,\n            excluded_urls=_InstrumentedFastAPI._excluded_urls,\n            default_span_details=_get_route_details,\n            server_request_hook=_InstrumentedFastAPI._server_request_hook,\n            client_request_hook=_InstrumentedFastAPI._client_request_hook,\n            client_response_hook=_InstrumentedFastAPI._client_response_hook,\n            tracer_provider=_InstrumentedFastAPI._tracer_provider,\n            meter=meter,\n        )\n        self._is_instrumented_by_opentelemetry = True\n        _InstrumentedFastAPI._instrumented_fastapi_apps.add(self)\n\n    def __del__(self):\n        if self in _InstrumentedFastAPI._instrumented_fastapi_apps:\n            _InstrumentedFastAPI._instrumented_fastapi_apps.remove(self)\n\n\ndef _get_route_details(scope):\n    \"\"\"Callback to retrieve the fastapi route being served.\n\n    TODO: there is currently no way to retrieve http.route from\n    a starlette application from scope.\n\n    See: https://github.com/encode/starlette/pull/804\n    \"\"\"\n    app = scope[\"app\"]\n    route = None\n    for starlette_route in app.routes:\n        match, _ = starlette_route.matches(scope)\n        if match == Match.FULL:\n            route = starlette_route.path\n            break\n        if match == Match.PARTIAL:\n            route = starlette_route.path\n    # method only exists for http, if websocket\n    # leave it blank.\n    span_name = route or scope.get(\"method\", \"\")\n    attributes = {}\n    if route:\n        attributes[SpanAttributes.HTTP_ROUTE] = route\n    return span_name, attributes\n", "path": "instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/__init__.py"}]}