SwapnilPatil28's picture
Major Update 1 - Add server, domain, client, models, and tests
4058302 verified
"""Structured JSON logging for the environment server.
Every emitted log entry is one JSON object per line so it can be ingested by
standard log aggregators (Cloud Logging, Loki, Datadog, ELK) without extra
parsing.
"""
from __future__ import annotations
import json
import logging
import sys
import time
from typing import Any, Mapping
_LOGGER_CONFIGURED = False
class _JSONFormatter(logging.Formatter):
def format(self, record: logging.LogRecord) -> str:
payload: dict[str, Any] = {
"ts": time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime(record.created))
+ f".{int((record.created % 1) * 1000):03d}Z",
"level": record.levelname.lower(),
"logger": record.name,
"message": record.getMessage(),
}
extra = getattr(record, "extra_fields", None)
if isinstance(extra, Mapping):
payload.update(extra)
if record.exc_info:
payload["exc_info"] = self.formatException(record.exc_info)
return json.dumps(payload, ensure_ascii=False, default=str)
def configure_logging(level: str = "INFO", structured: bool = True) -> None:
global _LOGGER_CONFIGURED
if _LOGGER_CONFIGURED:
return
root = logging.getLogger()
for handler in list(root.handlers):
root.removeHandler(handler)
handler = logging.StreamHandler(stream=sys.stdout)
if structured:
handler.setFormatter(_JSONFormatter())
else:
handler.setFormatter(
logging.Formatter("%(asctime)s %(levelname)s %(name)s :: %(message)s")
)
root.addHandler(handler)
root.setLevel(level.upper())
_LOGGER_CONFIGURED = True
def log_event(logger: logging.Logger, message: str, **fields: Any) -> None:
logger.info(message, extra={"extra_fields": fields})