import logging import sys from pathlib import Path from datetime import datetime from logging.handlers import RotatingFileHandler from typing import Optional LOG_DIR = Path(__file__).parent / "logs" LOG_DIR.mkdir(exist_ok=True) class ColorFormatter(logging.Formatter): COLORS = { "DEBUG": "\033[36m", "INFO": "\033[32m", "WARNING": "\033[33m", "ERROR": "\033[31m", "CRITICAL": "\033[35m", } RESET = "\033[0m" BOLD = "\033[1m" def format(self, record): color = self.COLORS.get(record.levelname, "") record.levelname = f"{color}{self.BOLD}{record.levelname:8}{self.RESET}" return super().format(record) def get_logger( name: str, level: int = logging.DEBUG, log_file: Optional[str] = None ) -> logging.Logger: logger = logging.getLogger(name) logger.setLevel(level) if logger.handlers: return logger console_handler = logging.StreamHandler(sys.stdout) console_handler.setLevel(level) console_format = ColorFormatter( fmt="%(asctime)s | %(levelname)s | %(name)s: %(message)s", datefmt="%H:%M:%S" ) console_handler.setFormatter(console_format) logger.addHandler(console_handler) file_path = LOG_DIR / (log_file or "ragger.log") file_handler = RotatingFileHandler( file_path, maxBytes=5 * 1024 * 1024, backupCount=3, encoding="utf-8" ) file_handler.setLevel(level) file_format = logging.Formatter( fmt="%(asctime)s | %(levelname)-8s | %(name)s: %(message)s", datefmt="%Y-%m-%d %H:%M:%S" ) file_handler.setFormatter(file_format) logger.addHandler(file_handler) return logger log = get_logger("ragger") def log_function_call(func): import functools @functools.wraps(func) def wrapper(*args, **kwargs): args_repr = [repr(a) for a in args[:3]] kwargs_repr = [f"{k}={v!r}" for k, v in list(kwargs.items())[:3]] signature = ", ".join(args_repr + kwargs_repr) if len(signature) > 80: signature = signature[:77] + "..." log.debug(f"[CALL] {func.__name__}({signature})") try: result = func(*args, **kwargs) result_repr = repr(result) if len(result_repr) > 100: result_repr = result_repr[:97] + "..." log.debug(f"[RETURN] {func.__name__} -> {result_repr}") return result except Exception as e: log.error(f"[ERROR] {func.__name__}: {type(e).__name__}: {e}") raise return wrapper