Files
2026-03-17 18:32:44 +03:00

101 lines
2.8 KiB
Python

from __future__ import annotations
import json
import logging
import os
from datetime import datetime, timezone
from typing import Any
from app.utils.log_context import get_log_context
SERVICE_NAME = os.getenv("APP_SERVICE_NAME", "backend-api")
LOG_RECORD_RESERVED_FIELDS = set(
logging.LogRecord(
name="",
level=0,
pathname="",
lineno=0,
msg="",
args=(),
exc_info=None,
).__dict__.keys()
) | {"message", "asctime"}
def _normalize_extra_value(value: Any) -> Any:
if isinstance(value, (str, int, float, bool)) or value is None:
return value
if isinstance(value, (list, tuple)):
return [_normalize_extra_value(item) for item in value]
if isinstance(value, dict):
normalized: dict[str, Any] = {}
for key, nested_value in value.items():
normalized[str(key)] = _normalize_extra_value(nested_value)
return normalized
return str(value)
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(),
"service_name": SERVICE_NAME,
}
for key in (
"event",
"trace_id",
"path",
"method",
"status_code",
"duration_ms",
"user_id",
"email",
"role",
"dialog_id",
"pipeline_id",
"run_id",
"result_status",
"message_len",
"capability_ids_count",
"reason",
):
value = getattr(record, key, None)
if value is not None:
payload[key] = value
for key, value in record.__dict__.items():
if key in LOG_RECORD_RESERVED_FIELDS or key in payload:
continue
payload[key] = _normalize_extra_value(value)
if record.exc_info:
payload["exception"] = self.formatException(record.exc_info)
return json.dumps(payload, ensure_ascii=True)
class RequestContextFilter(logging.Filter):
def filter(self, record: logging.LogRecord) -> bool:
for key, value in get_log_context().items():
if getattr(record, key, None) is None:
setattr(record, key, value)
return True
def configure_logging() -> None:
level = os.getenv("LOG_LEVEL", "INFO").upper()
root_logger = logging.getLogger()
root_logger.handlers.clear()
root_logger.setLevel(level)
handler = logging.StreamHandler()
handler.setFormatter(JsonFormatter())
handler.addFilter(RequestContextFilter())
root_logger.addHandler(handler)