""" Centralized logging module for AGI Multi-Model API. Provides structured logging with: - Colored console output - File logging with rotation - Configurable log levels - Timestamp and module name tracking """ import logging import sys from pathlib import Path from logging.handlers import RotatingFileHandler from typing import Optional class ColoredFormatter(logging.Formatter): """Custom formatter with color support for console output.""" # ANSI color codes COLORS = { 'DEBUG': '\033[36m', # Cyan 'INFO': '\033[32m', # Green 'WARNING': '\033[33m', # Yellow 'ERROR': '\033[31m', # Red 'CRITICAL': '\033[35m', # Magenta } RESET = '\033[0m' BOLD = '\033[1m' def format(self, record): """Format log record with colors.""" # Add color to level name levelname = record.levelname if levelname in self.COLORS: record.levelname = f"{self.COLORS[levelname]}{self.BOLD}{levelname}{self.RESET}" # Format the message result = super().format(record) # Reset levelname for other handlers record.levelname = levelname return result class Logger: """ Singleton logger class for the entire application. Usage: from logger import get_logger logger = get_logger(__name__) logger.info("Application started") """ _instance: Optional[logging.Logger] = None _initialized: bool = False @classmethod def get_logger( cls, name: str = "AGI", level: int = logging.INFO, log_file: Optional[str] = "agi.log", max_bytes: int = 10 * 1024 * 1024, # 10MB backup_count: int = 5 ) -> logging.Logger: """ Get or create the application logger. Args: name: Logger name (typically module name) level: Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL) log_file: Path to log file (None to disable file logging) max_bytes: Maximum size of log file before rotation backup_count: Number of backup files to keep Returns: Configured logger instance """ # Create or get logger logger = logging.getLogger(name) # Only configure handlers once for the root logger if not cls._initialized and name == "AGI": logger.setLevel(level) # Console handler with colors console_handler = logging.StreamHandler(sys.stdout) console_handler.setLevel(level) console_formatter = ColoredFormatter( fmt='%(asctime)s | %(levelname)s | %(name)s | %(message)s', datefmt='%Y-%m-%d %H:%M:%S' ) console_handler.setFormatter(console_formatter) logger.addHandler(console_handler) # File handler with rotation (if enabled) if log_file: log_path = Path(log_file) log_path.parent.mkdir(parents=True, exist_ok=True) file_handler = RotatingFileHandler( log_file, maxBytes=max_bytes, backupCount=backup_count ) file_handler.setLevel(level) file_formatter = logging.Formatter( fmt='%(asctime)s | %(levelname)-8s | %(name)s | %(funcName)s:%(lineno)d | %(message)s', datefmt='%Y-%m-%d %H:%M:%S' ) file_handler.setFormatter(file_formatter) logger.addHandler(file_handler) # Prevent propagation to avoid duplicate logs logger.propagate = False cls._initialized = True return logger # Convenience function for easy import def get_logger(name: str = "AGI", level: int = logging.INFO) -> logging.Logger: """ Get a logger instance for the specified module. Args: name: Logger name (use __name__ for automatic module naming) level: Logging level (default: INFO) Returns: Configured logger instance Example: from logger import get_logger logger = get_logger(__name__) logger.info("Starting application") """ return Logger.get_logger(name, level) # Initialize the root logger on module import _root_logger = Logger.get_logger("AGI", level=logging.INFO) if __name__ == "__main__": # Test the logger logger = get_logger("test_module") logger.debug("This is a debug message") logger.info("This is an info message") logger.warning("This is a warning message") logger.error("This is an error message") logger.critical("This is a critical message") print("\nTesting with different module names:") api_logger = get_logger("api") api_logger.info("API logger initialized") client_logger = get_logger("client") client_logger.info("Client logger initialized")