from __future__ import annotations import json import logging from datetime import datetime, timezone from pathlib import Path from typing import Any from src.config.settings import settings class JsonFormatter(logging.Formatter): def format(self, record: logging.LogRecord) -> str: payload: dict[str, Any] = { "timestamp": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"), "level": record.levelname, "logger": record.name, "message": record.getMessage(), } for key in ( "request_id", "job_id", "conversation_id", "duration_ms", "path", "method", "status_code", "attempt", "sap_status", "sap_http_status", "sap_lead_id", ): value = getattr(record, key, None) if value is not None: payload[key] = value if record.exc_info: payload["exception"] = self.formatException(record.exc_info) return json.dumps(payload, ensure_ascii=True) def configure_logging() -> None: settings.logs_dir.mkdir(parents=True, exist_ok=True) log_path = Path(settings.logs_dir) / "nexus_ai.log" root = logging.getLogger() root.setLevel(logging.INFO) root.handlers.clear() formatter = JsonFormatter() stream_handler = logging.StreamHandler() stream_handler.setFormatter(formatter) file_handler = logging.FileHandler(log_path, encoding="utf-8") file_handler.setFormatter(formatter) root.addHandler(stream_handler) root.addHandler(file_handler)