"""Custom log formatters.""" import json import logging from datetime import datetime from typing import Any, Dict try: import colorlog except ImportError: colorlog = None class JSONFormatter(logging.Formatter): """JSON formatter for structured logging.""" def format(self, record: logging.LogRecord) -> str: """ Format log record as JSON. Args: record: Log record to format Returns: JSON string """ log_data: Dict[str, Any] = { "timestamp": datetime.utcnow().isoformat(), "level": record.levelname, "logger": record.name, "message": record.getMessage(), "module": record.module, "function": record.funcName, "line": record.lineno, } # Add exception info if present if record.exc_info: log_data["exception"] = self.formatException(record.exc_info) # Add extra fields if hasattr(record, "extra_fields"): log_data.update(record.extra_fields) return json.dumps(log_data) class DetailedFormatter(logging.Formatter): """Detailed text formatter with full context.""" def __init__(self, *args: Any, **kwargs: Any): """Initialize formatter.""" super().__init__( fmt="%(asctime)s - %(name)s - %(levelname)s - %(message)s - [%(pathname)s:%(lineno)d]", datefmt="%Y-%m-%d %H:%M:%S", *args, **kwargs, ) class ColoredFormatter(logging.Formatter): """Colored console formatter.""" def __init__(self, *args: Any, **kwargs: Any): """Initialize formatter.""" if colorlog is None: # Fallback to standard formatter if colorlog not available super().__init__( fmt="%(asctime)s - %(name)s - %(levelname)s - %(message)s", datefmt="%Y-%m-%d %H:%M:%S", *args, **kwargs, ) else: # Use colorlog if available super().__init__(*args, **kwargs) def format(self, record: logging.LogRecord) -> str: """ Format log record with colors. Args: record: Log record to format Returns: Formatted string with colors """ if colorlog is None: return super().format(record) # Create colored formatter formatter = colorlog.ColoredFormatter( "%(log_color)s%(asctime)s - %(name)s - %(levelname)s - %(message)s%(reset)s", datefmt="%Y-%m-%d %H:%M:%S", log_colors={ "DEBUG": "cyan", "INFO": "green", "WARNING": "yellow", "ERROR": "red", "CRITICAL": "red,bg_white", }, ) return formatter.format(record)