66from fastapi import FastAPI , Request
77from fastapi_lifespan_manager import State
88from httpx import AsyncClient , Client
9- from opentelemetry import trace
109from opentelemetry .exporter .otlp .proto .http .trace_exporter import (
1110 OTLPSpanExporter as OTLPSpanExporterHTTP ,
1211)
1312from opentelemetry .instrumentation .fastapi import FastAPIInstrumentor
1413from opentelemetry .instrumentation .httpx import HTTPXClientInstrumentor
15- from opentelemetry .sdk .resources import Resource
1614from opentelemetry .sdk .trace import SpanProcessor , TracerProvider
1715from opentelemetry .sdk .trace .export import BatchSpanProcessor
18- from opentelemetry .sdk .trace .sampling import ParentBased , TraceIdRatioBased
1916from settings_library .tracing import TracingSettings
2017from starlette .middleware .base import BaseHTTPMiddleware
2118from yarl import URL
2219
2320from ..logging_utils import log_context
24- from ..tracing import get_trace_id_header
21+ from ..tracing import TracingData , get_trace_id_header
2522
2623_logger = logging .getLogger (__name__ )
2724
@@ -79,22 +76,14 @@ def _create_span_processor(tracing_destination: str) -> SpanProcessor:
7976 return BatchSpanProcessor (otlp_exporter )
8077
8178
82- def _startup (tracing_settings : TracingSettings , service_name : str ) -> None :
79+ def _startup (tracing_settings : TracingSettings , tracing_data : TracingData ) -> None :
8380 if (
8481 not tracing_settings .TRACING_OPENTELEMETRY_COLLECTOR_ENDPOINT
8582 and not tracing_settings .TRACING_OPENTELEMETRY_COLLECTOR_PORT
8683 ):
8784 _logger .warning ("Skipping opentelemetry tracing setup" )
8885 return
89- # Set up the tracer provider
90- resource = Resource (attributes = {"service.name" : service_name })
91- sampler = ParentBased (
92- root = TraceIdRatioBased (tracing_settings .TRACING_SAMPLING_PROBABILITY )
93- )
94- trace_provider = TracerProvider (resource = resource , sampler = sampler )
95- trace .set_tracer_provider (trace_provider )
96- global_tracer_provider = trace .get_tracer_provider ()
97- assert isinstance (global_tracer_provider , TracerProvider ) # nosec
86+ assert isinstance (tracing_data .tracer_provider , TracerProvider ) # nosec
9887
9988 opentelemetry_collector_endpoint : str = (
10089 f"{ tracing_settings .TRACING_OPENTELEMETRY_COLLECTOR_ENDPOINT } "
@@ -106,11 +95,11 @@ def _startup(tracing_settings: TracingSettings, service_name: str) -> None:
10695
10796 _logger .info (
10897 "Trying to connect service %s to opentelemetry tracing collector at %s." ,
109- service_name ,
98+ tracing_data . service_name ,
11099 tracing_destination ,
111100 )
112101 # Add the span processor to the tracer provider
113- global_tracer_provider .add_span_processor (
102+ tracing_data . tracer_provider .add_span_processor (
114103 _create_span_processor (tracing_destination )
115104 )
116105
@@ -120,75 +109,95 @@ def _startup(tracing_settings: TracingSettings, service_name: str) -> None:
120109 logging .INFO ,
121110 msg = "Attempting to add asyncpg opentelemetry autoinstrumentation..." ,
122111 ):
123- AiopgInstrumentor ().instrument ()
112+ AiopgInstrumentor ().instrument (tracer_provider = tracing_data . tracer_provider )
124113 if HAS_AIOPIKA_INSTRUMENTOR :
125114 with log_context (
126115 _logger ,
127116 logging .INFO ,
128117 msg = "Attempting to add aio_pika opentelemetry autoinstrumentation..." ,
129118 ):
130- AioPikaInstrumentor ().instrument ()
119+ AioPikaInstrumentor ().instrument (
120+ tracer_provider = tracing_data .tracer_provider
121+ )
131122 if HAS_ASYNCPG :
132123 with log_context (
133124 _logger ,
134125 logging .INFO ,
135126 msg = "Attempting to add asyncpg opentelemetry autoinstrumentation..." ,
136127 ):
137- AsyncPGInstrumentor ().instrument ()
128+ AsyncPGInstrumentor ().instrument (
129+ tracer_provider = tracing_data .tracer_provider
130+ )
138131 if HAS_REDIS :
139132 with log_context (
140133 _logger ,
141134 logging .INFO ,
142135 msg = "Attempting to add redis opentelemetry autoinstrumentation..." ,
143136 ):
144- RedisInstrumentor ().instrument ()
137+ RedisInstrumentor ().instrument (tracer_provider = tracing_data . tracer_provider )
145138 if HAS_BOTOCORE :
146139 with log_context (
147140 _logger ,
148141 logging .INFO ,
149142 msg = "Attempting to add botocore opentelemetry autoinstrumentation..." ,
150143 ):
151- BotocoreInstrumentor ().instrument ()
144+ BotocoreInstrumentor ().instrument (
145+ tracer_provider = tracing_data .tracer_provider
146+ )
152147 if HAS_REQUESTS :
153148 with log_context (
154149 _logger ,
155150 logging .INFO ,
156151 msg = "Attempting to add requests opentelemetry autoinstrumentation..." ,
157152 ):
158- RequestsInstrumentor ().instrument ()
153+ RequestsInstrumentor ().instrument (
154+ tracer_provider = tracing_data .tracer_provider
155+ )
159156
160157
161- def _shutdown () -> None :
158+ def _shutdown (tracing_data : TracingData ) -> None :
162159 """Uninstruments all opentelemetry instrumentors that were instrumented."""
163- FastAPIInstrumentor ().uninstrument ()
160+ FastAPIInstrumentor ().uninstrument (tracer_provider = tracing_data . tracer_provider )
164161 if HAS_AIOPG :
165162 try :
166- AiopgInstrumentor ().uninstrument ()
163+ AiopgInstrumentor ().uninstrument (
164+ tracer_provider = tracing_data .tracer_provider
165+ )
167166 except Exception : # pylint:disable=broad-exception-caught
168167 _logger .exception ("Failed to uninstrument AiopgInstrumentor" )
169168 if HAS_AIOPIKA_INSTRUMENTOR :
170169 try :
171- AioPikaInstrumentor ().uninstrument ()
170+ AioPikaInstrumentor ().uninstrument (
171+ tracer_provider = tracing_data .tracer_provider
172+ )
172173 except Exception : # pylint:disable=broad-exception-caught
173174 _logger .exception ("Failed to uninstrument AioPikaInstrumentor" )
174175 if HAS_ASYNCPG :
175176 try :
176- AsyncPGInstrumentor ().uninstrument ()
177+ AsyncPGInstrumentor ().uninstrument (
178+ tracer_provider = tracing_data .tracer_provider
179+ )
177180 except Exception : # pylint:disable=broad-exception-caught
178181 _logger .exception ("Failed to uninstrument AsyncPGInstrumentor" )
179182 if HAS_REDIS :
180183 try :
181- RedisInstrumentor ().uninstrument ()
184+ RedisInstrumentor ().uninstrument (
185+ tracer_provider = tracing_data .tracer_provider
186+ )
182187 except Exception : # pylint:disable=broad-exception-caught
183188 _logger .exception ("Failed to uninstrument RedisInstrumentor" )
184189 if HAS_BOTOCORE :
185190 try :
186- BotocoreInstrumentor ().uninstrument ()
191+ BotocoreInstrumentor ().uninstrument (
192+ tracer_provider = tracing_data .tracer_provider
193+ )
187194 except Exception : # pylint:disable=broad-exception-caught
188195 _logger .exception ("Failed to uninstrument BotocoreInstrumentor" )
189196 if HAS_REQUESTS :
190197 try :
191- RequestsInstrumentor ().uninstrument ()
198+ RequestsInstrumentor ().uninstrument (
199+ tracer_provider = tracing_data .tracer_provider
200+ )
192201 except Exception : # pylint:disable=broad-exception-caught
193202 _logger .exception ("Failed to uninstrument RequestsInstrumentor" )
194203
@@ -209,19 +218,24 @@ def setup_tracing(
209218 app : FastAPI , tracing_settings : TracingSettings , service_name : str
210219) -> None :
211220 # NOTE: This does not instrument the app itself. Call setup_fastapi_app_tracing to do that.
212- _startup (tracing_settings = tracing_settings , service_name = service_name )
221+ assert getattr (app .state , "tracing_data" , None ) is None
222+ tracing_data = TracingData .create (
223+ tracing_settings = tracing_settings , service_name = service_name
224+ )
225+ app .state .tracing_data = tracing_data
226+ _startup (tracing_settings = tracing_settings , tracing_data = get_tracing_data (app ))
213227
214228 def _on_shutdown () -> None :
215- _shutdown ()
229+ _shutdown (tracing_data = get_tracing_data ( app ) )
216230
217231 app .add_event_handler ("shutdown" , _on_shutdown )
218232
219233
220234def get_tracing_instrumentation_lifespan (
221- tracing_settings : TracingSettings , service_name : str
235+ tracing_settings : TracingSettings , tracing_data : TracingData
222236):
223237 # NOTE: This lifespan does not instrument the app itself. Call setup_fastapi_app_tracing to do that.
224- _startup (tracing_settings = tracing_settings , service_name = service_name )
238+ _startup (tracing_settings = tracing_settings , tracing_data = tracing_data )
225239
226240 async def tracing_instrumentation_lifespan (
227241 app : FastAPI ,
@@ -230,7 +244,7 @@ async def tracing_instrumentation_lifespan(
230244
231245 yield {}
232246
233- _shutdown ()
247+ _shutdown (tracing_data = tracing_data )
234248
235249 return tracing_instrumentation_lifespan
236250
@@ -243,3 +257,8 @@ async def dispatch(self, request: Request, call_next):
243257 if trace_id_header :
244258 response .headers .update (trace_id_header )
245259 return response
260+
261+
262+ def get_tracing_data (app : FastAPI ) -> TracingData :
263+ assert hasattr (app .state , "tracing_data" ), "Tracing not setup for this app" # nosec
264+ return app .state .tracing_data
0 commit comments