""" Logging configuration for VideoDetect. Sets up JSON structured logging via python-json-logger with: - Configurable log levels - Standardized field names - Log rotation - All logs to stdout for Docker capture """ import json import logging import logging.handlers import sys from datetime import datetime, timezone from python_json_logger import json_formatter def setup_logging( level: str = "INFO", log_format: str = "json", rotation_max_bytes: int = 104857600, # 100MB rotation_backup_count: int = 10, ): """Configure structured logging for the application.""" log_level = getattr(logging, level.upper(), logging.INFO) # Root logger root_logger = logging.getLogger() root_logger.setLevel(log_level) # Remove existing handlers root_logger.handlers.clear() # Handler: stdout (for Docker capture) handler = logging.StreamHandler(sys.stdout) handler.setLevel(log_level) if log_format == "json": formatter = JsonFormatter() else: formatter = logging.Formatter( "%(asctime)s %(levelname)s %(name)s %(message)s", datefmt="%Y-%m-%dT%H:%M:%S%z", ) handler.setFormatter(formatter) root_logger.addHandler(handler) # Handler: file rotation (for persistence) file_handler = logging.handlers.RotatingFileHandler( filename="/logs/videodetect.log", maxBytes=rotation_max_bytes, backupCount=rotation_backup_count, encoding="utf-8", ) file_handler.setLevel(log_level) file_handler.setFormatter(formatter) root_logger.addHandler(file_handler) logging.info("Logging configured: level=%s format=%s", level, log_format) class JsonFormatter(logging.Formatter): """JSON log formatter with standardized fields.""" def format(self, record: logging.LogRecord) -> str: log_data = { "timestamp": datetime.fromtimestamp(record.created, tz=timezone.utc).isoformat(), "level": record.levelname, "logger": record.name, "message": record.getMessage(), "module": record.module, "function": record.funcName, "line": record.lineno, } # Add extra fields if hasattr(record, "video_id"): log_data["video_id"] = record.video_id if hasattr(record, "metadata"): log_data["metadata"] = record.metadata # Add exception info if present if record.exc_info and record.exc_info[0] is not None: log_data["exception"] = self.formatException(record.exc_info) return json.dumps(log_data, default=str)