""" Structured logging configuration for MTG Online backend. Supports JSON and text log formats with container metadata. Configured via MTG_LOG_LEVEL and MTG_LOG_FORMAT environment variables. """ import json import logging import os import sys from datetime import datetime, timezone def _get_container_name() -> str: """Get container name from HOSTNAME env var or fallback.""" return os.environ.get("HOSTNAME", "unknown") class ContainerFormatter(logging.Formatter): """Formatter that adds container metadata to log records. Supports both text and JSON output formats. """ SERVICE_NAME = "mtgonline-backend" def __init__(self, fmt_type: str = "text"): super().__init__() self.fmt_type = fmt_type self.container_name = _get_container_name() def format(self, record: logging.LogRecord) -> str: record.service = self.SERVICE_NAME record.container = self.container_name if self.fmt_type == "json": return self._format_json(record) return self._format_text(record) def _format_text(self, record: logging.LogRecord) -> str: return ( f"{self.formatTime(record)} [{record.service}] " f"{record.levelname} - {record.getMessage()}" ) def _format_json(self, record: logging.LogRecord) -> str: log_entry = { "timestamp": datetime.fromtimestamp( record.created, tz=timezone.utc ).isoformat(), "level": record.levelname, "service": record.service, "container": record.container, "logger": record.name, "message": record.getMessage(), } if record.exc_info and record.exc_info[0] is not None: log_entry["exception"] = self.formatException(record.exc_info) return json.dumps(log_entry, default=str) def configure_logging() -> None: """Configure application logging based on environment variables. Reads: MTG_LOG_LEVEL - Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL) MTG_LOG_FORMAT - Output format ("text" or "json") """ log_level = os.environ.get("MTG_LOG_LEVEL", "INFO").upper() log_format = os.environ.get("MTG_LOG_FORMAT", "text").lower() level = getattr(logging, log_level, logging.INFO) formatter = ContainerFormatter(fmt_type=log_format) handler = logging.StreamHandler(sys.stdout) handler.setFormatter(formatter) root_logger = logging.getLogger() root_logger.setLevel(level) root_logger.handlers.clear() root_logger.addHandler(handler) # Suppress noisy loggers unless DEBUG if level != logging.DEBUG: logging.getLogger("sqlalchemy.engine").setLevel(logging.WARNING) logging.getLogger("sqlalchemy.pool").setLevel(logging.WARNING) logger = logging.getLogger(__name__) logger.info( f"Logging configured: level={log_level}, " f"format={log_format}, container={_get_container_name()}" ) # Auto-configure on import configure_logging()