@@ -186,6 +186,7 @@ def client_response_hook(span: Span, scope: dict[str, Any], message: dict[str, A
186
186
import logging
187
187
import types
188
188
from typing import Collection , Literal
189
+ from weakref import WeakSet as _WeakSet
189
190
190
191
import fastapi
191
192
from starlette .applications import Starlette
@@ -358,6 +359,11 @@ def uninstrument_app(app: fastapi.FastAPI):
358
359
app .middleware_stack = app .build_middleware_stack ()
359
360
app ._is_instrumented_by_opentelemetry = False
360
361
362
+ # Remove the app from the set of instrumented apps to avoid calling uninstrument twice
363
+ # if the instrumentation is later disabled or such
364
+ # Use discard to avoid KeyError if already GC'ed
365
+ _InstrumentedFastAPI ._instrumented_fastapi_apps .discard (app )
366
+
361
367
def instrumentation_dependencies (self ) -> Collection [str ]:
362
368
return _instruments
363
369
@@ -388,7 +394,11 @@ def _instrument(self, **kwargs):
388
394
fastapi .FastAPI = _InstrumentedFastAPI
389
395
390
396
def _uninstrument (self , ** kwargs ):
391
- for instance in _InstrumentedFastAPI ._instrumented_fastapi_apps :
397
+ # Create a copy of the set to avoid RuntimeError during iteration
398
+ instances_to_uninstrument = list (
399
+ _InstrumentedFastAPI ._instrumented_fastapi_apps
400
+ )
401
+ for instance in instances_to_uninstrument :
392
402
self .uninstrument_app (instance )
393
403
_InstrumentedFastAPI ._instrumented_fastapi_apps .clear ()
394
404
fastapi .FastAPI = self ._original_fastapi
@@ -406,7 +416,8 @@ class _InstrumentedFastAPI(fastapi.FastAPI):
406
416
_http_capture_headers_sanitize_fields : list [str ] | None = None
407
417
_exclude_spans : list [Literal ["receive" , "send" ]] | None = None
408
418
409
- _instrumented_fastapi_apps = set ()
419
+ # Track instrumented app instances using weak references to avoid GC leaks
420
+ _instrumented_fastapi_apps = _WeakSet ()
410
421
_sem_conv_opt_in_mode = _StabilityMode .DEFAULT
411
422
412
423
def __init__ (self , * args , ** kwargs ):
@@ -426,10 +437,6 @@ def __init__(self, *args, **kwargs):
426
437
)
427
438
_InstrumentedFastAPI ._instrumented_fastapi_apps .add (self )
428
439
429
- def __del__ (self ):
430
- if self in _InstrumentedFastAPI ._instrumented_fastapi_apps :
431
- _InstrumentedFastAPI ._instrumented_fastapi_apps .remove (self )
432
-
433
440
434
441
def _get_route_details (scope ):
435
442
"""
0 commit comments