Spaces:
Running
Running
File size: 3,922 Bytes
84c328d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
"""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)
|