| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576 |
- from __future__ import annotations
- import logging
- import time
- from starlette.middleware.base import BaseHTTPMiddleware
- from starlette.requests import Request
- from starlette.responses import Response
- from app.db import get_pool
- from app.services.geo import geo_resolver
- from app.services.ws_hub import hub
- logger = logging.getLogger(__name__)
- class LoggingMiddleware(BaseHTTPMiddleware):
- async def dispatch(self, request: Request, call_next) -> Response:
- # Skip WebSocket upgrade requests on /ws/ paths
- if request.url.path.startswith("/ws/"):
- return await call_next(request)
- start = time.monotonic()
- ip = request.client.host if request.client else "unknown"
- response = await call_next(request)
- latency_ms = (time.monotonic() - start) * 1000.0
- geo = geo_resolver.resolve(ip)
- try:
- pool = get_pool()
- row = await pool.fetchrow(
- """
- INSERT INTO access_logs
- (ip, method, path, status_code, latency_ms,
- country, city, latitude, longitude, org)
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
- RETURNING id, created_at
- """,
- ip,
- request.method,
- request.url.path,
- response.status_code,
- latency_ms,
- geo.country,
- geo.city,
- geo.latitude,
- geo.longitude,
- geo.org,
- )
- log_dict = {
- "id": row["id"],
- "ip": ip,
- "method": request.method,
- "path": request.url.path,
- "status_code": response.status_code,
- "latency_ms": latency_ms,
- "country": geo.country,
- "city": geo.city,
- "latitude": geo.latitude,
- "longitude": geo.longitude,
- "org": geo.org,
- "created_at": row["created_at"].isoformat(),
- }
- try:
- await hub.broadcast(log_dict)
- except Exception as exc:
- logger.error("WebSocket broadcast failed: %s", exc)
- except Exception as exc:
- logger.error("Failed to write access log: %s", exc)
- return response
|