logging.py 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. from __future__ import annotations
  2. import logging
  3. import time
  4. from starlette.middleware.base import BaseHTTPMiddleware
  5. from starlette.requests import Request
  6. from starlette.responses import Response
  7. from app.db import get_pool
  8. from app.services.geo import geo_resolver
  9. from app.services.ws_hub import hub
  10. logger = logging.getLogger(__name__)
  11. class LoggingMiddleware(BaseHTTPMiddleware):
  12. async def dispatch(self, request: Request, call_next) -> Response:
  13. # Skip WebSocket upgrade requests on /ws/ paths
  14. if request.url.path.startswith("/ws/"):
  15. return await call_next(request)
  16. start = time.monotonic()
  17. ip = request.client.host if request.client else "unknown"
  18. response = await call_next(request)
  19. latency_ms = (time.monotonic() - start) * 1000.0
  20. geo = geo_resolver.resolve(ip)
  21. try:
  22. pool = get_pool()
  23. row = await pool.fetchrow(
  24. """
  25. INSERT INTO access_logs
  26. (ip, method, path, status_code, latency_ms,
  27. country, city, latitude, longitude)
  28. VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
  29. RETURNING id, created_at
  30. """,
  31. ip,
  32. request.method,
  33. request.url.path,
  34. response.status_code,
  35. latency_ms,
  36. geo.country,
  37. geo.city,
  38. geo.latitude,
  39. geo.longitude,
  40. )
  41. log_dict = {
  42. "id": row["id"],
  43. "ip": ip,
  44. "method": request.method,
  45. "path": request.url.path,
  46. "status_code": response.status_code,
  47. "latency_ms": latency_ms,
  48. "country": geo.country,
  49. "city": geo.city,
  50. "latitude": geo.latitude,
  51. "longitude": geo.longitude,
  52. "created_at": row["created_at"].isoformat(),
  53. }
  54. try:
  55. await hub.broadcast(log_dict)
  56. except Exception as exc:
  57. logger.error("WebSocket broadcast failed: %s", exc)
  58. except Exception as exc:
  59. logger.error("Failed to write access log: %s", exc)
  60. return response