import logging import sys import os from datetime import datetime from typing import Optional class CustomFormatter(logging.Formatter): """Custom formatter with color coding and detailed formatting""" grey = "\x1b[38;21m" blue = "\x1b[38;5;39m" yellow = "\x1b[38;5;226m" red = "\x1b[38;5;196m" bold_red = "\x1b[31;1m" reset = "\x1b[0m" def __init__(self, include_path: bool = False): date_fmt = "%Y-%m-%d %H:%M:%S.%f" if include_path: fmt = ("%(asctime)s.%(msecs)03d - %(name)s - %(levelname)s " "[%(pathname)s:%(lineno)d] - %(message)s") else: fmt = "%(asctime)s.%(msecs)03d - %(name)s - %(levelname)s - %(message)s" super().__init__(fmt=fmt, datefmt=date_fmt) def format(self, record): # Save original format format_orig = self._style._fmt # Apply color based on log level if record.levelno == logging.DEBUG: self._style._fmt = self.grey + format_orig + self.reset elif record.levelno == logging.INFO: self._style._fmt = self.blue + format_orig + self.reset elif record.levelno == logging.WARNING: self._style._fmt = self.yellow + format_orig + self.reset elif record.levelno == logging.ERROR: self._style._fmt = self.red + format_orig + self.reset elif record.levelno == logging.CRITICAL: self._style._fmt = self.bold_red + format_orig + self.reset # Call original format result = logging.Formatter.format(self, record) # Restore original format self._style._fmt = format_orig return result def setup_logger( name: str = __name__, log_level: int = logging.DEBUG, log_file: Optional[str] = None ) -> logging.Logger: """ Sets up a configured logger with both console and file handlers. Args: name: Logger name log_level: Logging level log_file: Optional specific log file path Returns: Configured logger instance """ logger = logging.getLogger(name) # Only add handlers if logger doesn't have any if not logger.handlers: logger.setLevel(log_level) # Ensure logs directory exists os.makedirs('logs', exist_ok=True) # Console handler with minimal formatting console_handler = logging.StreamHandler(sys.stdout) console_handler.setLevel(logging.INFO) console_formatter = CustomFormatter(include_path=False) console_handler.setFormatter(console_formatter) # File handler with detailed formatting if not log_file: log_file = f'logs/sse_stream_{datetime.now():%Y%m%d}.log' file_handler = logging.FileHandler(log_file) file_handler.setLevel(logging.DEBUG) file_formatter = CustomFormatter(include_path=True) file_handler.setFormatter(file_formatter) # Debug file handler for trace-level logging debug_file = f'logs/debug_{datetime.now():%Y%m%d}.log' debug_handler = logging.FileHandler(debug_file) debug_handler.setLevel(logging.DEBUG) debug_formatter = CustomFormatter(include_path=True) debug_handler.setFormatter(debug_formatter) logger.addHandler(console_handler) logger.addHandler(file_handler) logger.addHandler(debug_handler) return logger