@@ -281,16 +281,53 @@ async def setup_async_loggers_lifespan(
281281 # Create queue handler for loggers
282282 queue_handler = logging .handlers .QueueHandler (log_queue )
283283
284- # Get all loggers and replace existing handlers with queue handler
284+ # Use root-only approach for better performance and simplicity
285+ root_logger = logging .getLogger ()
286+ original_root_handlers = root_logger .handlers .copy ()
287+
288+ # Check for edge cases and warn if found
285289 all_loggers = _get_all_loggers ()
286- original_handlers : dict [ logging . Logger , list [ logging . Handler ]] = {}
290+ edge_case_loggers = []
287291
288292 for logger in all_loggers :
289- # Store original handlers for cleanup
290- original_handlers [logger ] = logger .handlers .copy ()
291- # Clear existing handlers and add only the queue handler
292- logger .handlers .clear ()
293- logger .addHandler (queue_handler )
293+ if logger is root_logger :
294+ continue
295+
296+ # Check for loggers that might bypass root logging
297+ has_handlers = bool (logger .handlers )
298+ propagate_disabled = not logger .propagate
299+
300+ # Filter out harmless cases: NullHandler with propagate=True is fine
301+ has_meaningful_handlers = (
302+ any (not isinstance (h , logging .NullHandler ) for h in logger .handlers )
303+ if logger .handlers
304+ else False
305+ )
306+
307+ if has_meaningful_handlers or propagate_disabled :
308+ edge_case_loggers .append (
309+ {
310+ "name" : logger .name ,
311+ "has_handlers" : has_handlers ,
312+ "propagate" : logger .propagate ,
313+ "handlers" : [type (h ).__name__ for h in logger .handlers ],
314+ }
315+ )
316+
317+ if edge_case_loggers :
318+ _logger .warning (
319+ "Found %d loggers that may bypass async logging: %s. "
320+ "Consider reviewing logger configuration." ,
321+ len (edge_case_loggers ),
322+ [
323+ f"{ logger_info ['name' ]} (handlers={ logger_info ['handlers' ]} , propagate={ logger_info ['propagate' ]} )"
324+ for logger_info in edge_case_loggers [:3 ]
325+ ], # Show first 3 to avoid spam
326+ )
327+
328+ # Replace only root logger handlers
329+ root_logger .handlers .clear ()
330+ root_logger .addHandler (queue_handler )
294331
295332 try :
296333 # Apply filters if provided
@@ -301,12 +338,10 @@ async def setup_async_loggers_lifespan(
301338 yield
302339
303340 finally :
304- # Cleanup: Remove queue handlers and restore original handlers
341+ # Cleanup: Restore original root logger handlers
305342 try :
306- for logger in all_loggers :
307- # Clear queue handler and restore original handlers
308- logger .handlers .clear ()
309- logger .handlers .extend (original_handlers .get (logger , []))
343+ root_logger .handlers .clear ()
344+ root_logger .handlers .extend (original_root_handlers )
310345
311346 # Stop the queue listener
312347 with log_context (
0 commit comments