Skip to content

Commit 0bad279

Browse files
committed
this might be working
1 parent dc498cc commit 0bad279

File tree

1 file changed

+76
-101
lines changed

1 file changed

+76
-101
lines changed

packages/service-library/src/servicelib/logging_utils.py

Lines changed: 76 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,23 @@ def _set_logging_handler(
183183
)
184184

185185

186+
def _apply_logger_filters(
187+
logger_filter_mapping: dict[LoggerName, list[MessageSubstring]],
188+
) -> None:
189+
"""Apply filters to specific loggers."""
190+
for logger_name, filtered_routes in logger_filter_mapping.items():
191+
logger = logging.getLogger(logger_name)
192+
if not logger.hasHandlers():
193+
_logger.warning(
194+
"Logger %s does not have any handlers. Filter will not be added.",
195+
logger_name,
196+
)
197+
continue
198+
199+
log_filter = GeneralLogFilter(filtered_routes)
200+
logger.addFilter(log_filter)
201+
202+
186203
def config_all_loggers(
187204
*,
188205
log_format_local_dev_enabled: bool,
@@ -218,17 +235,50 @@ def config_all_loggers(
218235
)
219236

220237
# Apply filters
221-
for logger_name, filtered_routes in logger_filter_mapping.items():
222-
logger = logging.getLogger(logger_name)
223-
if not logger.hasHandlers():
224-
_logger.warning(
225-
"Logger %s does not have any handlers. Filter will not be added.",
226-
logger_name,
227-
)
228-
continue
238+
_apply_logger_filters(logger_filter_mapping)
229239

230-
log_filter = GeneralLogFilter(filtered_routes)
231-
logger.addFilter(log_filter)
240+
241+
@asynccontextmanager
242+
async def setup_async_loggers(
243+
*,
244+
log_format_local_dev_enabled: bool = False,
245+
logger_filter_mapping: dict[LoggerName, list[MessageSubstring]] | None = None,
246+
tracing_settings: TracingSettings | None = None,
247+
) -> AsyncGenerator[None, None]:
248+
"""
249+
Async context manager for non-blocking logging infrastructure.
250+
251+
Usage:
252+
async with setup_async_loggers(log_format_local_dev_enabled=True):
253+
# Your async application code here
254+
logger.info("This is non-blocking!")
255+
256+
Args:
257+
log_format_local_dev_enabled: Enable local development formatting
258+
logger_filter_mapping: Mapping of logger names to filtered message substrings
259+
tracing_settings: OpenTelemetry tracing configuration
260+
"""
261+
# Create format string
262+
fmt = _setup_format_string(
263+
tracing_settings=tracing_settings,
264+
log_format_local_dev_enabled=log_format_local_dev_enabled,
265+
)
266+
267+
# Start the async logging context
268+
async with AsyncLoggingContext(
269+
log_format_local_dev_enabled=log_format_local_dev_enabled,
270+
fmt=fmt,
271+
):
272+
# Apply filters if provided
273+
if logger_filter_mapping:
274+
_apply_logger_filters(logger_filter_mapping)
275+
276+
_logger.info("Async logging setup completed")
277+
278+
try:
279+
yield
280+
finally:
281+
_logger.debug("Async logging context exiting")
232282

233283

234284
class LogExceptionsKwargsDict(TypedDict, total=True):
@@ -484,17 +534,15 @@ def set_parent_module_log_level(current_module: str, desired_log_level: int) ->
484534
class AsyncLoggingContext:
485535
"""
486536
Async context manager for non-blocking logging infrastructure.
487-
Based on the pattern from SuperFastPython article and integrated with background_task.
537+
Based on the pattern from SuperFastPython article.
488538
"""
489539

490540
def __init__(
491541
self,
492542
*,
493-
handlers: list[logging.Handler] | None = None,
494543
log_format_local_dev_enabled: bool = False,
495544
fmt: str | None = None,
496545
) -> None:
497-
self.handlers = handlers or [logging.StreamHandler()]
498546
self.log_format_local_dev_enabled = log_format_local_dev_enabled
499547
self.fmt = fmt or _DEFAULT_FORMATTING
500548
self.queue: queue.Queue | None = None
@@ -516,20 +564,18 @@ async def _setup_async_logging(self) -> None:
516564
# Create unlimited queue for log messages
517565
self.queue = queue.Queue()
518566

519-
# Configure handlers with proper formatting
520-
formatted_handlers = []
521-
for handler in self.handlers:
522-
handler.setFormatter(
523-
CustomFormatter(
524-
self.fmt,
525-
log_format_local_dev_enabled=self.log_format_local_dev_enabled,
526-
)
567+
# Use default StreamHandler with proper formatting
568+
handler = logging.StreamHandler()
569+
handler.setFormatter(
570+
CustomFormatter(
571+
self.fmt,
572+
log_format_local_dev_enabled=self.log_format_local_dev_enabled,
527573
)
528-
formatted_handlers.append(handler)
574+
)
529575

530576
# Create and start the queue listener
531577
self.listener = logging.handlers.QueueListener(
532-
self.queue, *formatted_handlers, respect_handler_level=True
578+
self.queue, handler, respect_handler_level=True
533579
)
534580
self.listener.start()
535581

@@ -542,25 +588,22 @@ async def _setup_async_logging(self) -> None:
542588
_logger.info("Async logging context initialized with unlimited queue")
543589

544590
async def _configure_loggers(self) -> None:
545-
"""Replace all logger handlers with queue handler."""
591+
"""Add queue handler to all loggers while preserving existing handlers."""
546592
# Get all loggers
547593
manager: logging.Manager = logging.Logger.manager
548594
root_logger = logging.getLogger()
549595
all_loggers = [root_logger] + [
550596
logging.getLogger(name) for name in manager.loggerDict
551597
]
552598

553-
# Store original handlers and replace with queue handler
599+
# Store original handlers and add queue handler
554600
for logger in all_loggers:
555601
logger_name = logger.name or "root"
556602

557603
# Store original handlers
558604
self.original_handlers[logger_name] = logger.handlers[:]
559605

560-
# Clear existing handlers
561-
logger.handlers.clear()
562-
563-
# Add queue handler
606+
# Add queue handler alongside existing handlers
564607
if self.queue_handler:
565608
logger.addHandler(self.queue_handler)
566609

@@ -570,22 +613,17 @@ async def _configure_loggers(self) -> None:
570613
async def _cleanup_async_logging(self) -> None:
571614
"""Restore original logging configuration."""
572615
try:
573-
# Restore original handlers
616+
# Remove queue handlers from all loggers
574617
manager: logging.Manager = logging.Logger.manager
575618
root_logger = logging.getLogger()
576619
all_loggers = [root_logger] + [
577620
logging.getLogger(name) for name in manager.loggerDict
578621
]
579622

580623
for logger in all_loggers:
581-
logger_name = logger.name or "root"
582-
if logger_name in self.original_handlers:
583-
# Clear queue handlers
584-
logger.handlers.clear()
585-
586-
# Restore original handlers
587-
for handler in self.original_handlers[logger_name]:
588-
logger.addHandler(handler)
624+
# Remove only the queue handler we added
625+
if self.queue_handler and self.queue_handler in logger.handlers:
626+
logger.removeHandler(self.queue_handler)
589627

590628
# Stop the queue listener
591629
if self.listener:
@@ -611,66 +649,3 @@ def get_metrics(self) -> dict[str, Any] | None:
611649
"listener_active": self.listener is not None,
612650
}
613651
return None
614-
615-
616-
@asynccontextmanager
617-
async def setup_async_loggers(
618-
*,
619-
log_format_local_dev_enabled: bool = False,
620-
logger_filter_mapping: dict[LoggerName, list[MessageSubstring]] | None = None,
621-
tracing_settings: TracingSettings | None = None,
622-
handlers: list[logging.Handler] | None = None,
623-
) -> AsyncGenerator[None, None]:
624-
"""
625-
Async context manager for non-blocking logging infrastructure.
626-
627-
Usage:
628-
async with setup_async_loggers(log_format_local_dev_enabled=True):
629-
# Your async application code here
630-
logger.info("This is non-blocking!")
631-
632-
Args:
633-
log_format_local_dev_enabled: Enable local development formatting
634-
logger_filter_mapping: Mapping of logger names to filtered message substrings
635-
tracing_settings: OpenTelemetry tracing configuration
636-
handlers: Custom handlers to use (defaults to StreamHandler)
637-
"""
638-
# Create format string
639-
fmt = _setup_format_string(
640-
tracing_settings=tracing_settings,
641-
log_format_local_dev_enabled=log_format_local_dev_enabled,
642-
)
643-
644-
# Start the async logging context
645-
async with AsyncLoggingContext(
646-
handlers=handlers,
647-
log_format_local_dev_enabled=log_format_local_dev_enabled,
648-
fmt=fmt,
649-
):
650-
# Apply filters if provided
651-
if logger_filter_mapping:
652-
_apply_logger_filters(logger_filter_mapping)
653-
654-
_logger.info("Async logging setup completed")
655-
656-
try:
657-
yield
658-
finally:
659-
_logger.debug("Async logging context exiting")
660-
661-
662-
def _apply_logger_filters(
663-
logger_filter_mapping: dict[LoggerName, list[MessageSubstring]],
664-
) -> None:
665-
"""Apply filters to specific loggers."""
666-
for logger_name, filtered_routes in logger_filter_mapping.items():
667-
logger = logging.getLogger(logger_name)
668-
if not logger.hasHandlers():
669-
_logger.warning(
670-
"Logger %s does not have any handlers. Filter will not be added.",
671-
logger_name,
672-
)
673-
continue
674-
675-
log_filter = GeneralLogFilter(filtered_routes)
676-
logger.addFilter(log_filter)

0 commit comments

Comments
 (0)