""" Centralized logging configuration for the backtest engine. Provides colored console output and rotating file logs. """ import logging import sys from logging.handlers import RotatingFileHandler from pathlib import Path # ANSI color codes for terminal output class Colors: """ANSI escape codes for colored terminal output.""" RESET = "\033[0m" BOLD = "\033[1m" # Log level colors DEBUG = "\033[36m" # Cyan INFO = "\033[32m" # Green WARNING = "\033[33m" # Yellow ERROR = "\033[31m" # Red CRITICAL = "\033[35m" # Magenta class ColoredFormatter(logging.Formatter): """ Custom formatter that adds colors to log level names in terminal output. """ LEVEL_COLORS = { logging.DEBUG: Colors.DEBUG, logging.INFO: Colors.INFO, logging.WARNING: Colors.WARNING, logging.ERROR: Colors.ERROR, logging.CRITICAL: Colors.CRITICAL, } def __init__(self, fmt: str = None, datefmt: str = None): super().__init__(fmt, datefmt) def format(self, record: logging.LogRecord) -> str: # Save original levelname original_levelname = record.levelname # Add color to levelname color = self.LEVEL_COLORS.get(record.levelno, Colors.RESET) record.levelname = f"{color}{record.levelname}{Colors.RESET}" # Format the message result = super().format(record) # Restore original levelname record.levelname = original_levelname return result def setup_logging( log_dir: str = "logs", log_level: int = logging.INFO, console_level: int = logging.INFO, max_bytes: int = 5 * 1024 * 1024, # 5MB backup_count: int = 3 ) -> None: """ Configure logging for the application. Args: log_dir: Directory for log files log_level: File logging level console_level: Console logging level max_bytes: Max size per log file before rotation backup_count: Number of backup files to keep """ log_path = Path(log_dir) log_path.mkdir(parents=True, exist_ok=True) # Get root logger root_logger = logging.getLogger() root_logger.setLevel(logging.DEBUG) # Capture all, handlers filter # Clear existing handlers root_logger.handlers.clear() # Console handler with colors console_handler = logging.StreamHandler(sys.stdout) console_handler.setLevel(console_level) console_fmt = ColoredFormatter( fmt="[%(asctime)s] %(levelname)s - %(message)s", datefmt="%H:%M:%S" ) console_handler.setFormatter(console_fmt) root_logger.addHandler(console_handler) # File handler with rotation file_handler = RotatingFileHandler( log_path / "backtest.log", maxBytes=max_bytes, backupCount=backup_count, encoding="utf-8" ) file_handler.setLevel(log_level) file_fmt = logging.Formatter( fmt="%(asctime)s - %(name)s - %(levelname)s - %(message)s", datefmt="%Y-%m-%d %H:%M:%S" ) file_handler.setFormatter(file_fmt) root_logger.addHandler(file_handler) def get_logger(name: str) -> logging.Logger: """ Get a logger instance for the given module name. Args: name: Module name (typically __name__) Returns: Configured logger instance """ return logging.getLogger(name)