@@ -188,10 +188,27 @@ def __init__(
188188 # Track when we next *must* perform a scheduled export
189189 self ._next_export_time = time .time () + self ._schedule_delay
190190
191- self ._worker_thread = threading .Thread (target = self ._run , daemon = True )
192- self ._worker_thread .start ()
191+ # We lazily start the background worker thread the first time a span/trace is queued.
192+ self ._worker_thread : threading .Thread | None = None
193+ self ._thread_start_lock = threading .Lock ()
194+
195+ def _ensure_thread_started (self ) -> None :
196+ # Fast path without holding the lock
197+ if self ._worker_thread and self ._worker_thread .is_alive ():
198+ return
199+
200+ # Double-checked locking to avoid starting multiple threads
201+ with self ._thread_start_lock :
202+ if self ._worker_thread and self ._worker_thread .is_alive ():
203+ return
204+
205+ self ._worker_thread = threading .Thread (target = self ._run , daemon = True )
206+ self ._worker_thread .start ()
193207
194208 def on_trace_start (self , trace : Trace ) -> None :
209+ # Ensure the background worker is running before we enqueue anything.
210+ self ._ensure_thread_started ()
211+
195212 try :
196213 self ._queue .put_nowait (trace )
197214 except queue .Full :
@@ -206,6 +223,9 @@ def on_span_start(self, span: Span[Any]) -> None:
206223 pass
207224
208225 def on_span_end (self , span : Span [Any ]) -> None :
226+ # Ensure the background worker is running before we enqueue anything.
227+ self ._ensure_thread_started ()
228+
209229 try :
210230 self ._queue .put_nowait (span )
211231 except queue .Full :
@@ -216,7 +236,13 @@ def shutdown(self, timeout: float | None = None):
216236 Called when the application stops. We signal our thread to stop, then join it.
217237 """
218238 self ._shutdown_event .set ()
219- self ._worker_thread .join (timeout = timeout )
239+
240+ # Only join if we ever started the background thread; otherwise flush synchronously.
241+ if self ._worker_thread and self ._worker_thread .is_alive ():
242+ self ._worker_thread .join (timeout = timeout )
243+ else :
244+ # No background thread: process any remaining items synchronously.
245+ self ._export_batches (force = True )
220246
221247 def force_flush (self ):
222248 """
0 commit comments