Add structured logging throughout the backend

Request-level logging middleware (method/path/status/duration),
per-module loggers in routes/services (nearby search radius
expansion, 404s, name search), LOG_LEVEL setting wired through
docker-compose, and seed_loader converted from print() to logging.

Verified: pytest passes, and manually confirmed log lines appear for
both successful and 404 requests via `docker compose logs api`.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
vrubelroman 2026-07-09 20:10:10 +00:00
parent 188bf01d6e
commit be67902b45
11 changed files with 114 additions and 4 deletions

View file

@ -1,4 +1,11 @@
import logging
import time
from collections.abc import Awaitable, Callable
from starlette.requests import Request
from starlette.responses import Response
request_logger = logging.getLogger("guidecity.http")
def configure_logging(level: int = logging.INFO) -> None:
@ -6,3 +13,25 @@ def configure_logging(level: int = logging.INFO) -> None:
level=level,
format="%(asctime)s %(levelname)s %(name)s %(message)s",
)
async def log_requests(
request: Request,
call_next: Callable[[Request], Awaitable[Response]],
) -> Response:
"""Logs method/path/status/duration for every request handled by the app."""
start = time.perf_counter()
try:
response = await call_next(request)
except Exception:
duration_ms = (time.perf_counter() - start) * 1000
request_logger.exception(
"%s %s -> unhandled exception (%.1fms)", request.method, request.url.path, duration_ms
)
raise
duration_ms = (time.perf_counter() - start) * 1000
request_logger.info(
"%s %s -> %d (%.1fms)", request.method, request.url.path, response.status_code, duration_ms
)
return response