import logging import sys from typing import ClassVar class ColoredFormatter(logging.Formatter): # ANSI color codes COLORS: ClassVar[dict[str, str]] = { "DEBUG": "\033[36m", "INFO": "\033[32m", "WARNING": "\033[33m", "ERROR": "\033[31m", "CRITICAL": "\033[35m", "RESET": "\033[0m", } def format(self, record: logging.LogRecord) -> str: plain = record.levelname color = self.COLORS.get(plain, self.COLORS["RESET"]) record.levelname = f"{color}{plain}{self.COLORS['RESET']}" try: return super().format(record) finally: record.levelname = plain QUIET_LOGGERS = ("httpx", "httpcore", "hpack", "urllib3", "asyncio", "kombu", "amqp") def setup_logging( name: str = "rengine", level: str = "INFO", colored: bool = True, ) -> logging.Logger: logger = logging.getLogger(name) logger.setLevel(getattr(logging, level.upper())) logger.handlers.clear() console_handler = logging.StreamHandler(sys.stdout) console_handler.setLevel(logging.DEBUG) if colored: console_format = ColoredFormatter( fmt="%(asctime)s | %(levelname)-8s | %(name)s | %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) else: console_format = logging.Formatter( fmt="%(asctime)s | %(levelname)-8s | %(name)s | %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) console_handler.setFormatter(console_format) logger.addHandler(console_handler) for noisy in QUIET_LOGGERS: logging.getLogger(noisy).setLevel(logging.WARNING) return logger _RESERVED = frozenset({"exc_info", "stack_info", "stacklevel", "extra"}) class _FieldLogger(logging.LoggerAdapter): """Accepts structured fields as keywords and appends them to the message.""" def log(self, level, msg, *args, **kwargs): fields = {k: v for k, v in kwargs.items() if k not in _RESERVED} for key in fields: kwargs.pop(key) if fields: rendered = " ".join(f"{k}={v}" for k, v in fields.items()) msg = f"{msg} | {rendered}" super().log(level, msg, *args, **kwargs) def debug(self, msg, *args, **kwargs): self.log(logging.DEBUG, msg, *args, **kwargs) def info(self, msg, *args, **kwargs): self.log(logging.INFO, msg, *args, **kwargs) def warning(self, msg, *args, **kwargs): self.log(logging.WARNING, msg, *args, **kwargs) def error(self, msg, *args, **kwargs): self.log(logging.ERROR, msg, *args, **kwargs) def exception(self, msg, *args, **kwargs): kwargs.setdefault("exc_info", True) self.log(logging.ERROR, msg, *args, **kwargs) def critical(self, msg, *args, **kwargs): self.log(logging.CRITICAL, msg, *args, **kwargs) def get_logger(name: str) -> logging.Logger: return _FieldLogger(logging.getLogger(name), {})