Coverage for barbet/logging.py: 80.00%
35 statements
« prev ^ index » next coverage.py v7.9.1, created at 2026-08-17 06:22 +0000
« prev ^ index » next coverage.py v7.9.1, created at 2026-08-17 06:22 +0000
1import sys
2import logging
3import io
4from pathlib import Path
5from rich.console import Console
8class BarbetLogger(logging.Logger):
9 def _log(self, level, msg, args, exc_info=None, extra=None, stack_info=False, stacklevel=1):
10 if not isinstance(msg, str):
11 try:
12 buf = io.StringIO()
13 Console(file=buf, force_terminal=False).print(msg)
14 msg = buf.getvalue().rstrip()
15 except Exception:
16 msg = str(msg)
17 super()._log(level, msg, args, exc_info=exc_info, extra=extra, stack_info=stack_info, stacklevel=stacklevel)
20logging.setLoggerClass(BarbetLogger)
23def setup_logger(output_dir: Path = None) -> logging.Logger:
24 """Set up and return a logger configured with stdout and file handlers."""
25 if output_dir is None:
26 output_dir = Path("output")
27 output_dir = Path(output_dir)
28 output_dir.mkdir(parents=True, exist_ok=True)
29 log_file = output_dir / "barbet.log"
31 logger = logging.getLogger("barbet")
32 logger.setLevel(logging.INFO)
34 file_handler_exists = any(
35 isinstance(h, logging.FileHandler) and Path(h.baseFilename).resolve() == log_file.resolve()
36 for h in logger.handlers
37 )
39 if not file_handler_exists:
40 logger.handlers.clear()
41 formatter = logging.Formatter("[%(asctime)s] %(levelname)s: %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
43 # File handler
44 fh = logging.FileHandler(log_file, mode="a")
45 fh.setFormatter(formatter)
46 logger.addHandler(fh)
48 # Console handler writing to stdout
49 ch = logging.StreamHandler(sys.stdout)
50 ch.setFormatter(formatter)
51 logger.addHandler(ch)
53 return logger