Arag / app /middleware /logging_middleware.py
AuthorBot
Convert ALL middleware to pure ASGI: eliminate BaseHTTPMiddleware completely to fix request body consumption bug causing 500 on ALL endpoints
59a89d6
Raw
History Blame Contribute Delete
1.97 kB
"""Author RAG Chatbot SaaS — Structured Logging Middleware (Pure ASGI).
Logs every request with: method, path, status, duration_ms, client_ip.
Uses structlog for JSON-formatted structured logging.
Uses pure ASGI to avoid BaseHTTPMiddleware request body consumption issues.
"""
import time
import structlog
from starlette.types import ASGIApp, Receive, Scope, Send
logger = structlog.get_logger(__name__)
class LoggingMiddleware:
"""Log all HTTP requests with timing and status (pure ASGI)."""
def __init__(self, app: ASGIApp) -> None:
self.app = app
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
if scope["type"] != "http":
await self.app(scope, receive, send)
return
start_time = time.monotonic()
status_code = 500 # Default in case of unhandled error
async def send_with_logging(message):
nonlocal status_code
if message["type"] == "http.response.start":
status_code = message["status"]
await send(message)
try:
await self.app(scope, receive, send_with_logging)
finally:
duration_ms = int((time.monotonic() - start_time) * 1000)
path = scope.get("path", "")
# Skip health check noise in logs
if path != "/health":
client = scope.get("client")
ip = client[0] if client else "unknown"
log = logger.bind(
method=scope.get("method", ""),
path=path,
status=status_code,
duration_ms=duration_ms,
ip=ip,
)
if status_code >= 500:
log.error("Request failed")
elif status_code >= 400:
log.warning("Client error")
else:
log.info("Request completed")