Spaces:
Running on Zero
Running on Zero
| """Structured JSON logging and request context.""" | |
| from __future__ import annotations | |
| import contextvars | |
| import json | |
| import logging | |
| import sys | |
| from datetime import datetime, timezone | |
| from typing import Any | |
| request_id_context: contextvars.ContextVar[str] = contextvars.ContextVar( | |
| "request_id", default="-" | |
| ) | |
| class JsonFormatter(logging.Formatter): | |
| """Render log records as one-line JSON.""" | |
| _reserved = set(logging.makeLogRecord({}).__dict__) | {"message", "asctime"} | |
| def format(self, record: logging.LogRecord) -> str: | |
| payload: dict[str, Any] = { | |
| "timestamp": datetime.now(timezone.utc).isoformat(), | |
| "level": record.levelname, | |
| "logger": record.name, | |
| "message": record.getMessage(), | |
| "request_id": getattr(record, "request_id", request_id_context.get()), | |
| } | |
| for key, value in record.__dict__.items(): | |
| if key not in self._reserved and not key.startswith("_"): | |
| payload[key] = value | |
| if record.exc_info: | |
| payload["exception"] = self.formatException(record.exc_info) | |
| return json.dumps(payload, default=str, ensure_ascii=False) | |
| def configure_logging(level: str = "INFO") -> None: | |
| """Configure process logging once.""" | |
| root = logging.getLogger() | |
| root.handlers.clear() | |
| handler = logging.StreamHandler(sys.stdout) | |
| handler.setFormatter(JsonFormatter()) | |
| root.addHandler(handler) | |
| root.setLevel(level.upper()) | |
| def get_logger(name: str) -> logging.Logger: | |
| return logging.getLogger(name) | |