import datetime import logging as _logging from pathlib import Path from loguru import logger from rich.console import Console from rich.logging import RichHandler """ Setup logging to use loguru for logging, with rich for pretty console output. Intercepts standard logging calls. """ console = Console() # "{time:MM-DD HH:mm:ss.SSS} | {level: <8} | {name}:{function}:{line} - {message}", def setup_logging( level: int | str = _logging.INFO, console_level: int | str | None = None, log_file: str | Path | None = None, format_console: str | None = "{message}", format_logs: str | None = "{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | {name}:{function}:{line} - {message}", console_locals: bool = False, enqueue: bool = True, console_tracebacks: bool = False, console_markup: bool = True, console_omit_repeated_times: bool = False, console_show_time: bool = True, console_show_path: bool = False, loguru_tracebacks: bool = False, loguru_locals: bool = False, loguru_rotation: str | int | datetime.time | datetime.timedelta | None = "10 MB", loguru_retention: str | int | datetime.timedelta | None = "10 days", loguru_compression: str = "gz", # "gz", "bz2", "xz", "lzma", "tar", "tar.gz", "tar.bz2", "tar.xz", "zip" loguru_watch_files: bool = False, loguru_delay_file_creation: bool = False, ) -> None: """Setup logging with loguru and rich. Disclosure: Most of this code is adapted from online examples, loguru documentation, and Gemini's suggestions. I am not an expert in logging, loguru, or rich. """ # Intercept standard logging class InterceptHandler(_logging.Handler): def emit(self, record: _logging.LogRecord): try: level = logger.level(record.levelname).name except ValueError: level = record.levelno # type: ignore frame, depth = _logging.currentframe(), 2 while frame and frame.f_code.co_filename == _logging.__file__: frame = frame.f_back # type: ignore depth += 1 logger.opt(depth=depth, exception=record.exc_info).log(level, record.getMessage()) logger.remove() _logging.basicConfig(handlers=[InterceptHandler()], level=0, force=True) # Add console handler # Hanlder for my console output handler_console = RichHandler( console=console, level=console_level if console_level is not None else level, show_level=True, show_path=console_show_path, show_time=console_show_time, rich_tracebacks=console_tracebacks, tracebacks_show_locals=console_locals, markup=console_markup, omit_repeated_times=console_omit_repeated_times, ) # handler_console.setFormatter(_logging.Formatter(format_console)) logger.add( handler_console, format=str(format_console), level=console_level if console_level is not None else level, backtrace=console_tracebacks, diagnose=console_locals, enqueue=enqueue, ) # Add log file handler if log_file: logger.add( log_file, level=level, format=str(format_logs), rotation=loguru_rotation, retention=loguru_retention, compression=loguru_compression, enqueue=enqueue, backtrace=loguru_tracebacks, diagnose=loguru_locals, watch=loguru_watch_files, delay=loguru_delay_file_creation, ) # def setup_logging( # level: Union[int, str] = _logging.INFO, # log_format: str = "{time:YYYY-MM-DD HH:mm:ss.SSS} | " # "{level: <8} | " # "{name}:{function}:{line} - " # "{message}", # date_format: str = "YYYY-MM-DD HH:mm:ss.SSS", # log_file: str | None = None, # file_log_level: Union[int, str] = _logging.DEBUG, # file_log_format: str = "{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | {name}:{function}:{line} - {message}", # file_date_format: str = "YYYY-MM-DD HH:mm:ss.SSS", # ) -> None: # class InterceptHandler(_logging.Handler): # def emit(self, record: _logging.LogRecord) -> None: # try: # level = logger.level(record.levelname).name # except ValueError: # level = record.levelno # frame, depth = _logging.currentframe(), 2 # while frame.f_code.co_filename == _logging.__file__: # frame = frame.f_back # type: ignore[union-attr] # depth += 1 # logger.opt(depth=depth, exception=record.exc_info).log(level, record.getMessage()) # _logging.root.handlers = [InterceptHandler()] # _logging.root.setLevel(level) # rich_handler = RichHandler( # rich_tracebacks=True, # tracebacks_show_locals=True, # markup=True, # log_time_format=date_format, # ) # rich_handler.setLevel(level) # rich_handler.setFormatter(_logging.Formatter(log_format)) # _logging.getLogger().addHandler(rich_handler) # if log_file: # logger.add( # log_file, # level=file_log_level, # format=file_log_format, # datefmt=file_date_format, # rotation="10 MB", # retention="10 days", # compression="zip", # )