OpenBIDSifier / logging_utils.py
stefanches7
implement logging to a text file
f957d0a
import logging
import logging.handlers
import multiprocessing
import datetime
from pathlib import Path
import atexit
from typing import Tuple, Optional
DEFAULT_LOGS_DIR = "logs"
def setup_logging(project_name: str, logs_dir: str = DEFAULT_LOGS_DIR, level: int = logging.INFO,
queue_logging: bool = True) -> Tuple[logging.Logger, Optional[logging.handlers.QueueListener]]:
"""
Configure parallel-safe logging.
Creates a log file named ``{project_name}-{timestamp}.log`` inside ``logs_dir``.
If ``queue_logging`` is True, uses ``multiprocessing.Queue`` with ``QueueHandler``/``QueueListener``
for process-safe logging. Returns the application logger and the listener (if any).
Caller does not need to manage handlers individually; the listener is auto-stopped at exit.
"""
timestamp = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
logs_path = Path(logs_dir)
logs_path.mkdir(parents=True, exist_ok=True)
logfile = logs_path / f"{project_name}-{timestamp}.log"
formatter = logging.Formatter(
fmt="%(asctime)s | %(levelname)s | %(processName)s | %(name)s | %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
logger = logging.getLogger("bidsifier")
logger.setLevel(level)
listener: Optional[logging.handlers.QueueListener] = None
if queue_logging:
log_queue: multiprocessing.Queue = multiprocessing.Queue(-1)
queue_handler = logging.handlers.QueueHandler(log_queue)
file_handler = logging.FileHandler(str(logfile), encoding="utf-8")
file_handler.setFormatter(formatter)
listener = logging.handlers.QueueListener(log_queue, file_handler)
listener.start()
logger.addHandler(queue_handler)
def _stop_listener() -> None:
try:
listener.stop()
except Exception:
pass
atexit.register(_stop_listener)
else:
file_handler = logging.FileHandler(str(logfile), encoding="utf-8")
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
# Also add a simple stderr stream handler for immediate feedback.
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(formatter)
logger.addHandler(stream_handler)
logger.debug("Logging initialized: %s", logfile)
return logger, listener