""" Logging Configuration Utility Provides centralized logging setup for the Vibeon Translator API """ import logging import sys from pathlib import Path import uuid from typing import Callable, Optional from starlette.middleware.base import BaseHTTPMiddleware # type: ignore from starlette.requests import Request # type: ignore def setup_logging(log_level: str = "INFO", log_file: Optional[str] = None): """ Setup logging configuration for the application Args: log_level: Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL) log_file: Optional log file path """ # Create formatter formatter = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S' ) # Get root logger root_logger = logging.getLogger() root_logger.setLevel(getattr(logging, log_level.upper())) # Clear existing handlers root_logger.handlers.clear() # Console handler with UTF-8 encoding for Unicode support console_handler = logging.StreamHandler(sys.stdout) console_handler.setFormatter(formatter) # Force UTF-8 encoding to handle Unicode characters (Kinyarwanda, Hindi, etc.) if hasattr(sys.stdout, 'reconfigure'): try: sys.stdout.reconfigure(encoding='utf-8', errors='replace') except Exception: pass # Fallback silently if reconfigure not available root_logger.addHandler(console_handler) # File handler if log file is specified if log_file: # Create log directory if it doesn't exist log_path = Path(log_file) log_path.parent.mkdir(parents=True, exist_ok=True) # Use UTF-8 encoding for file handler as well file_handler = logging.FileHandler(log_file, encoding='utf-8', errors='replace') file_handler.setFormatter(formatter) root_logger.addHandler(file_handler) # Set specific log levels for noisy libraries logging.getLogger("uvicorn.access").setLevel(logging.WARNING) logging.getLogger("httpx").setLevel(logging.WARNING) logging.getLogger("transformers").setLevel(logging.WARNING) logging.getLogger("torch").setLevel(logging.WARNING) return root_logger class RequestIdLoggingMiddleware(BaseHTTPMiddleware): """Attach request ID header and log structured request/response""" async def dispatch(self, request: Request, call_next: Callable): request_id = request.headers.get("X-Request-ID") or str(uuid.uuid4()) request.state.request_id = request_id logger = logging.getLogger("request") try: response = await call_next(request) response.headers["X-Request-ID"] = request_id logger.info( "method=%s path=%s status=%s request_id=%s", request.method, str(request.url.path), response.status_code, request_id, ) return response except Exception: logger.exception("error request_id=%s", request_id) raise