import logging import sys import contextvars from uuid import uuid4 # Zmienna kontekstowa definiująca unikalne ID zapytania HTTP (lub tła) request_id_ctx_var: contextvars.ContextVar[str] = contextvars.ContextVar( "request_id", default="SYSTEM" ) class RequestIDFilter(logging.Filter): """ Filtr wstrzykujący contextvar (RequestID) do rekordu logów. """ def filter(self, record): record.request_id = request_id_ctx_var.get() return True def setup_logging(): """ Konfiguruje główny logger aplikacji tak, aby: - używał StreamHandler (stdout) pod kontener (np. Render/Docker) - dodawał ustrukturyzowany prefiks: [Data] [Poziom] [RequestID: ...] Wiadomość. """ logger = logging.getLogger("DotacjeAI") logger.setLevel(logging.INFO) # Zapobiega duplikacji, gdy powtarzamy użycie skryptu gunicorn/uvicorn if logger.handlers: logger.handlers.clear() handler = logging.StreamHandler(sys.stdout) handler.setLevel(logging.INFO) # Format zadeklarowany we wdrożeniu (DEPLOYMENT.md) formatter = logging.Formatter( fmt="[%(asctime)s] [%(levelname)s] [RequestID: %(request_id)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) handler.setFormatter(formatter) # Dodajemy filtr wyciągający zmienne kontekstowe filter_req_id = RequestIDFilter() handler.addFilter(filter_req_id) logger.addFilter(filter_req_id) logger.addHandler(handler) # Przechwytuj uvicorn i langserve logi w naszym stylu logging.getLogger("uvicorn.access").addFilter(filter_req_id) return logger def set_request_id(req_id: str | None = None) -> str: """Ustawia request ID w zmiennej kontekstowej dla bieżącego cyklu asynchronicznego.""" new_id = req_id or f"req_{uuid4().hex[:8]}" request_id_ctx_var.set(new_id) return new_id