synapse / backend /app /core /logging.py
adwitiyashukla's picture
pushed Synapse, an agentic AI assistant with hybrid RAG
b2c1c67
Raw
History Blame Contribute Delete
2.54 kB
"""Structured JSON logging with request context."""
import json
import logging
import sys
import time
import uuid
from collections.abc import Awaitable, Callable
from fastapi import Request, Response
class JsonFormatter(logging.Formatter):
"""Format log records as single-line JSON objects."""
def format(self, record: logging.LogRecord) -> str:
payload = {
"ts": self.formatTime(record, "%Y-%m-%dT%H:%M:%S%z"),
"level": record.levelname,
"logger": record.name,
"message": record.getMessage(),
}
for key in ("request_id", "method", "path", "status", "duration_ms", "user_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=False)
def configure_logging(debug: bool = False) -> None:
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(JsonFormatter())
root = logging.getLogger()
root.handlers = [handler]
root.setLevel(logging.DEBUG if debug else logging.INFO)
# Quiet noisy third-party loggers
for name in ("httpx", "httpcore", "chromadb", "uvicorn.access"):
logging.getLogger(name).setLevel(logging.WARNING)
logger = logging.getLogger("synapse")
async def request_logging_middleware(
request: Request, call_next: Callable[[Request], Awaitable[Response]]
) -> Response:
"""Log every request with a correlation id and duration."""
request_id = uuid.uuid4().hex[:12]
request.state.request_id = request_id
start = time.perf_counter()
try:
response = await call_next(request)
except Exception:
duration_ms = round((time.perf_counter() - start) * 1000)
logger.exception(
"request failed",
extra={
"request_id": request_id,
"method": request.method,
"path": request.url.path,
"duration_ms": duration_ms,
},
)
raise
duration_ms = round((time.perf_counter() - start) * 1000)
logger.info(
"request",
extra={
"request_id": request_id,
"method": request.method,
"path": request.url.path,
"status": response.status_code,
"duration_ms": duration_ms,
},
)
response.headers["X-Request-ID"] = request_id
return response