Spaces:
Running
Running
| """Clean logging configuration for development. | |
| Provides simple, readable logs for development with optional JSON mode for production. | |
| """ | |
| import logging | |
| import logging.config | |
| import sys | |
| from typing import Optional | |
| class CleanFormatter(logging.Formatter): | |
| """Simple, clean formatter for readable development logs.""" | |
| # Color codes for terminal output | |
| COLORS = { | |
| "DEBUG": "\033[36m", # Cyan | |
| "INFO": "\033[32m", # Green | |
| "WARNING": "\033[33m", # Yellow | |
| "ERROR": "\033[31m", # Red | |
| "CRITICAL": "\033[35m", # Magenta | |
| "RESET": "\033[0m", # Reset | |
| } | |
| def __init__(self, use_colors: bool = True): | |
| """Initialize formatter. | |
| Args: | |
| use_colors: Whether to use ANSI color codes (disable for file logs) | |
| """ | |
| self.use_colors = use_colors | |
| super().__init__() | |
| def format(self, record: logging.LogRecord) -> str: | |
| """Format log record as a clean, readable string.""" | |
| level = record.levelname | |
| module = record.name.split(".")[-1] if "." in record.name else record.name | |
| message = record.getMessage() | |
| # Build the log line | |
| if self.use_colors: | |
| color = self.COLORS.get(level, "") | |
| reset = self.COLORS["RESET"] | |
| formatted = f"{color}{level:8}{reset} {module:20} | {message}" | |
| else: | |
| formatted = f"{level:8} {module:20} | {message}" | |
| # Add exception info if present | |
| if record.exc_info: | |
| formatted += f"\n{self.formatException(record.exc_info)}" | |
| return formatted | |
| def setup_logging( | |
| level: str = "INFO", | |
| json_mode: bool = False, | |
| quiet_sql: bool = True | |
| ) -> None: | |
| """Configure logging for the application. | |
| Args: | |
| level: Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL) | |
| json_mode: Use structured JSON logging (for production) | |
| quiet_sql: Suppress verbose SQL query logs | |
| """ | |
| log_level = getattr(logging, level.upper(), logging.INFO) | |
| # Configure root logger | |
| logging.root.setLevel(log_level) | |
| logging.root.handlers.clear() | |
| # Create handler | |
| handler = logging.StreamHandler(sys.stdout) | |
| handler.setLevel(log_level) | |
| # Set formatter | |
| if json_mode: | |
| # Import JSON formatter for production | |
| import json | |
| from datetime import datetime | |
| class JSONFormatter(logging.Formatter): | |
| def format(self, record): | |
| log_entry = { | |
| "timestamp": datetime.utcnow().isoformat() + "Z", | |
| "level": record.levelname, | |
| "logger": record.name, | |
| "message": record.getMessage(), | |
| } | |
| if record.exc_info: | |
| log_entry["exception"] = self.formatException(record.exc_info) | |
| return json.dumps(log_entry) | |
| handler.setFormatter(JSONFormatter()) | |
| else: | |
| handler.setFormatter(CleanFormatter(use_colors=True)) | |
| logging.root.addHandler(handler) | |
| # Configure third-party loggers | |
| if quiet_sql: | |
| logging.getLogger("sqlalchemy.engine").setLevel(logging.WARNING) | |
| logging.getLogger("sqlalchemy.pool").setLevel(logging.WARNING) | |
| logging.getLogger("sqlmodel").setLevel(logging.WARNING) | |
| logging.getLogger("uvicorn.access").setLevel(logging.WARNING) | |
| logging.getLogger("uvicorn.error").setLevel(logging.ERROR) | |
| logging.getLogger("fastapi").setLevel(logging.INFO) | |
| # Log startup message (but only in non-JSON mode) | |
| if not json_mode: | |
| logger = logging.getLogger(__name__) | |
| logger.info(f"Logging configured at {level} level") | |
| def get_logger(name: str) -> logging.Logger: | |
| """Get a logger instance. | |
| Args: | |
| name: Logger name (typically __name__ of the module) | |
| Returns: | |
| Logger instance | |
| """ | |
| return logging.getLogger(name) | |