logging.py 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  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, org)
  28. VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
  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. geo.org,
  41. )
  42. log_dict = {
  43. "id": row["id"],
  44. "ip": ip,
  45. "method": request.method,
  46. "path": request.url.path,
  47. "status_code": response.status_code,
  48. "latency_ms": latency_ms,
  49. "country": geo.country,
  50. "city": geo.city,
  51. "latitude": geo.latitude,
  52. "longitude": geo.longitude,
  53. "org": geo.org,
  54. "created_at": row["created_at"].isoformat(),
  55. }
  56. try:
  57. await hub.broadcast(log_dict)
  58. except Exception as exc:
  59. logger.error("WebSocket broadcast failed: %s", exc)
  60. except Exception as exc:
  61. logger.error("Failed to write access log: %s", exc)
  62. return response