Spaces:
Running
Running
AuthorBot
Convert ALL middleware to pure ASGI: eliminate BaseHTTPMiddleware completely to fix request body consumption bug causing 500 on ALL endpoints
59a89d6 | """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") | |