Coverage for netrun_errors / middleware.py: 100%

26 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-12-15 18:37 +0000

1""" 

2Error logging middleware for FastAPI applications. 

3 

4Provides request/response logging with correlation ID injection, 

5performance tracking, and structured logging integration. 

6""" 

7 

8import logging 

9import time 

10from typing import Callable 

11 

12from fastapi import Request, Response 

13from starlette.middleware.base import BaseHTTPMiddleware 

14 

15from .base import NetrunException 

16 

17logger = logging.getLogger(__name__) 

18 

19 

20class ErrorLoggingMiddleware(BaseHTTPMiddleware): 

21 """ 

22 Middleware for request/response logging with correlation IDs. 

23 

24 Features: 

25 - Automatic correlation ID generation and injection 

26 - Request/response logging with timing 

27 - Structured logging with request context 

28 - Performance tracking 

29 """ 

30 

31 async def dispatch(self, request: Request, call_next: Callable) -> Response: 

32 """ 

33 Process request with correlation ID and logging. 

34 

35 Args: 

36 request: FastAPI request object 

37 call_next: Next middleware/endpoint in chain 

38 

39 Returns: 

40 Response from downstream handlers 

41 """ 

42 # Generate correlation ID 

43 correlation_id = NetrunException._generate_correlation_id() 

44 

45 # Add correlation ID to request state for downstream access 

46 request.state.correlation_id = correlation_id 

47 

48 # Start timing 

49 start_time = time.time() 

50 

51 # Log request 

52 logger.info( 

53 f"Request started: {request.method} {request.url.path}", 

54 extra={ 

55 "correlation_id": correlation_id, 

56 "method": request.method, 

57 "path": request.url.path, 

58 "query_params": dict(request.query_params), 

59 "client_host": request.client.host if request.client else None, 

60 }, 

61 ) 

62 

63 try: 

64 # Process request 

65 response = await call_next(request) 

66 

67 # Calculate duration 

68 duration_ms = (time.time() - start_time) * 1000 

69 

70 # Log response 

71 logger.info( 

72 f"Request completed: {request.method} {request.url.path} - {response.status_code}", 

73 extra={ 

74 "correlation_id": correlation_id, 

75 "method": request.method, 

76 "path": request.url.path, 

77 "status_code": response.status_code, 

78 "duration_ms": round(duration_ms, 2), 

79 }, 

80 ) 

81 

82 # Inject correlation ID into response headers 

83 response.headers["X-Correlation-ID"] = correlation_id 

84 

85 return response 

86 

87 except Exception as exc: 

88 # Calculate duration 

89 duration_ms = (time.time() - start_time) * 1000 

90 

91 # Log exception 

92 logger.error( 

93 f"Request failed: {request.method} {request.url.path}", 

94 extra={ 

95 "correlation_id": correlation_id, 

96 "method": request.method, 

97 "path": request.url.path, 

98 "duration_ms": round(duration_ms, 2), 

99 "exception_type": type(exc).__name__, 

100 }, 

101 exc_info=True, 

102 ) 

103 

104 # Re-raise for exception handlers 

105 raise 

106 

107 

108def install_error_logging_middleware(app) -> None: 

109 """ 

110 Install error logging middleware on a FastAPI application. 

111 

112 Usage: 

113 from fastapi import FastAPI 

114 from netrun_errors import install_error_logging_middleware 

115 

116 app = FastAPI() 

117 install_error_logging_middleware(app) 

118 

119 Args: 

120 app: FastAPI application instance 

121 """ 

122 app.add_middleware(ErrorLoggingMiddleware) 

123 logger.info("Error logging middleware installed successfully")