Skip to content

Commit 8a014e9

Browse files
committed
cleanup
1 parent 7ed8fe5 commit 8a014e9

File tree

1 file changed

+97
-54
lines changed

1 file changed

+97
-54
lines changed

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

Lines changed: 97 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import logging
1111
import logging.handlers
1212
import queue
13-
import sys
1413
from asyncio import iscoroutinefunction
1514
from collections.abc import Callable, Iterator
1615
from contextlib import contextmanager
@@ -155,19 +154,25 @@ def format(self, record) -> str:
155154
# log_level=%{WORD:log_level} \| log_timestamp=%{TIMESTAMP_ISO8601:log_timestamp} \| log_source=%{DATA:log_source} \| (log_uid=%{WORD:log_uid} \| )?log_msg=%{GREEDYDATA:log_msg}
156155

157156

158-
def _setup_format_string(
157+
def _setup_logging_formatter(
159158
*,
160159
tracing_settings: TracingSettings | None,
161160
log_format_local_dev_enabled: bool,
162-
) -> str:
161+
) -> logging.Formatter:
163162
if log_format_local_dev_enabled:
164-
return (
163+
fmt = (
165164
_LOCAL_TRACING_FORMATTING
166165
if tracing_settings is not None
167166
else _LOCAL_FORMATTING
168167
)
168+
else:
169+
fmt = (
170+
_TRACING_FORMATTING if tracing_settings is not None else _DEFAULT_FORMATTING
171+
)
169172

170-
return _TRACING_FORMATTING if tracing_settings is not None else _DEFAULT_FORMATTING
173+
return CustomFormatter(
174+
fmt, log_format_local_dev_enabled=log_format_local_dev_enabled
175+
)
171176

172177

173178
def _get_all_loggers() -> list[logging.Logger]:
@@ -260,25 +265,69 @@ def setup_loggers(
260265
_dampen_noisy_loggers(noisy_loggers)
261266
if tracing_settings is not None:
262267
setup_log_tracing(tracing_settings=tracing_settings)
263-
fmt = _setup_format_string(
268+
formatter = _setup_logging_formatter(
264269
tracing_settings=tracing_settings,
265270
log_format_local_dev_enabled=log_format_local_dev_enabled,
266271
)
267272

268273
# Create a properly formatted handler for the root logger
269-
root_handler = logging.StreamHandler()
270-
root_handler.setFormatter(
271-
CustomFormatter(fmt, log_format_local_dev_enabled=log_format_local_dev_enabled)
274+
stream_handler = logging.StreamHandler()
275+
stream_handler.setFormatter(formatter)
276+
277+
_store_logger_state(_get_all_loggers())
278+
_clean_all_handlers()
279+
_set_root_handler(stream_handler)
280+
281+
if logger_filter_mapping:
282+
_apply_logger_filters(logger_filter_mapping)
283+
284+
285+
@contextmanager
286+
def _queued_logging_handler(
287+
log_formatter: logging.Formatter,
288+
) -> Iterator[logging.Handler]:
289+
log_queue: queue.Queue[logging.LogRecord] = queue.Queue()
290+
# Create handler with proper formatting
291+
handler = logging.StreamHandler()
292+
handler.setFormatter(log_formatter)
293+
294+
# Create and start the queue listener
295+
listener = logging.handlers.QueueListener(
296+
log_queue, handler, respect_handler_level=True
272297
)
298+
listener.start()
299+
300+
queue_handler = logging.handlers.QueueHandler(log_queue)
301+
302+
yield queue_handler
303+
304+
# cleanup
305+
with log_context(
306+
_logger,
307+
level=logging.DEBUG,
308+
msg="Shutdown async logging listener",
309+
):
310+
listener.stop()
273311

312+
313+
def _clean_all_handlers() -> None:
314+
"""
315+
Cleans all handlers from all loggers.
316+
This is useful for resetting the logging configuration.
317+
"""
318+
root_logger = logging.getLogger()
274319
all_loggers = _get_all_loggers()
320+
for logger in all_loggers:
321+
if logger is root_logger:
322+
continue
323+
logger.handlers.clear()
324+
logger.propagate = True # Ensure propagation is enabled
275325

276-
# Apply comprehensive logging setup
277-
# Note: We don't store the original state here since this is a permanent setup
278-
_apply_comprehensive_logging_setup(all_loggers, root_handler)
279326

280-
# Apply filters
281-
_apply_logger_filters(logger_filter_mapping)
327+
def _set_root_handler(handler: logging.Handler) -> None:
328+
root_logger = logging.getLogger()
329+
root_logger.handlers.clear() # Clear existing handlers
330+
root_logger.addHandler(handler) # Add the new handler
282331

283332

284333
@contextmanager
@@ -338,57 +387,24 @@ def async_loggers(
338387

339388
if tracing_settings is not None:
340389
setup_log_tracing(tracing_settings=tracing_settings)
341-
fmt = _setup_format_string(
390+
formatter = _setup_logging_formatter(
342391
tracing_settings=tracing_settings,
343392
log_format_local_dev_enabled=log_format_local_dev_enabled,
344393
)
345394

346-
# Set up async logging infrastructure
347-
log_queue: queue.Queue[logging.LogRecord] = queue.Queue()
348-
# Create handler with proper formatting
349-
handler = logging.StreamHandler()
350-
handler.setFormatter(
351-
CustomFormatter(fmt, log_format_local_dev_enabled=log_format_local_dev_enabled)
352-
)
353-
354-
# Create and start the queue listener
355-
listener = logging.handlers.QueueListener(
356-
log_queue, handler, respect_handler_level=True
357-
)
358-
listener.start()
359-
360-
# Create queue handler for loggers
361-
queue_handler = logging.handlers.QueueHandler(log_queue)
362-
363-
# Apply comprehensive logging setup and store original state for restoration
364-
all_loggers = _get_all_loggers()
365-
original_logger_state = _apply_comprehensive_logging_setup(
366-
all_loggers, queue_handler
367-
)
395+
with (
396+
_queued_logging_handler(formatter) as queue_handler,
397+
_stored_logger_states(_get_all_loggers()),
398+
):
399+
_clean_all_handlers()
400+
_set_root_handler(queue_handler)
368401

369-
try:
370402
if logger_filter_mapping:
371403
_apply_logger_filters(logger_filter_mapping)
372404

373405
with log_context(_logger, logging.INFO, "Asynchronous logging"):
374406
yield
375407

376-
finally:
377-
try:
378-
_restore_logger_state(original_logger_state)
379-
380-
# Stop the queue listener
381-
with log_context(
382-
_logger,
383-
level=logging.DEBUG,
384-
msg="Shutdown async logging listener",
385-
):
386-
listener.stop()
387-
388-
except Exception as exc: # pylint: disable=broad-except
389-
sys.stderr.write(f"Error during async logging cleanup: {exc}\n")
390-
sys.stderr.flush()
391-
392408

393409
class LogExceptionsKwargsDict(TypedDict, total=True):
394410
logger: logging.Logger
@@ -642,10 +658,37 @@ class _LoggerState:
642658
propagate: bool
643659

644660

661+
@contextmanager
662+
def _stored_logger_states(
663+
loggers: list[logging.Logger],
664+
) -> Iterator[list[_LoggerState]]:
665+
"""
666+
Context manager to store and restore the state of loggers.
667+
It captures the current handlers and propagation state of each logger.
668+
"""
669+
original_state = _store_logger_state(loggers)
670+
671+
try:
672+
# log which loggers states were stored
673+
_logger.info(
674+
"Stored logger states: %s. TIP: these loggers configuration will be restored later.",
675+
json_dumps(
676+
[
677+
f"{state.logger.name}(handlers={len(state.handlers)}, propagate={state.propagate})"
678+
for state in original_state
679+
]
680+
),
681+
)
682+
yield original_state
683+
finally:
684+
_restore_logger_state(original_state)
685+
686+
645687
def _store_logger_state(loggers: list[logging.Logger]) -> list[_LoggerState]:
646688
return [
647689
_LoggerState(logger, logger.handlers.copy(), logger.propagate)
648690
for logger in loggers
691+
if logger.handlers or not logger.propagate
649692
]
650693

651694

0 commit comments

Comments
 (0)