Skip to content

Conversation

@karrtik159
Copy link

Exception Middleware & Logging Implementation

A comprehensive exception handling and logging system for the FastAPI boilerplate with structured JSON responses and automatic error message translation.


Architecture Overview

flowchart TB
    subgraph Request Flow
        A[Incoming Request] --> B[CORS Middleware]
        B --> C[Client Cache Middleware]
        C --> D[Logging Middleware]
        D --> E[Exception Handler Middleware]
        E --> F[Route Handler]
    end
    
    subgraph Exception Handling
        F -->|ValidationError| G[setup_exception_handlers]
        F -->|HTTPException| G
        F -->|Generic Exception| E
        G --> H[get_friendly_message]
        H --> I[APIResponse Format]
    end
    
    subgraph Logging
        D -->|Log Request| J[Logger]
        I -->|Log Response| J
        J --> K[Console Handler]
        J --> L[File Handler + Rotation]
    end
Loading

Files Created

1. API Response Schema

File: api_response.py

Defines the standardized JSON response structure for all API endpoints.

class APIResponse(BaseModel, Generic[T]):
    data: T | None = None          # Response payload
    isSuccess: bool                 # Success indicator
    message: str                    # Human-readable message  
    statusCode: int                 # HTTP status code

Helper Functions:

  • success_response(data, message, status_code) - Creates success responses
  • error_response(message, status_code, data) - Creates error responses

2. Exception Handler Middleware

File: exception_handler_middleware.py

Handles all exceptions and converts them to the APIResponse format.

Key Components:

Component Purpose
ERROR_MESSAGES dict Maps Pydantic error types → user-friendly messages
get_friendly_message() Translates error types with context (min_length, etc.)
ExceptionHandlerMiddleware Catches exceptions during request processing
setup_exception_handlers() Registers FastAPI exception handlers for validation errors

Error Message Translations:

ERROR_MESSAGES = {
    "missing": "This field is required",
    "string_too_short": "This field is too short",
    "string_too_long": "This field is too long", 
    "string_pattern_mismatch": "Invalid format",
    "value_error.email": "Must be a valid email address",
    "extra_forbidden": "Unknown field not allowed",
    # ... more translations
}

Context-aware messages:

  • string_too_short + min_length: 8"Must be at least 8 characters"
  • string_too_long + max_length: 100"Must be at most 100 characters"

3. Logging Middleware

File: logging_middleware.py

Logs all incoming requests and outgoing responses with timing information.

Features:

  • Unique request ID (added to X-Request-ID header)
  • Processing time tracking (added to X-Process-Time header)
  • Client IP detection (handles X-Forwarded-For for proxies)
  • Configurable path exclusions (e.g., /health, /metrics)
  • Optional request/response body logging

Log Format:

[a1b2c3d4] --> GET /api/v1/users from 127.0.0.1 (Mozilla/5.0...)
[a1b2c3d4] <-- GET /api/v1/users 200 (1234 bytes) in 0.045s

4. Enhanced Logger

File: logger.py

Centralized logging configuration with file rotation and colored console output.

Features:

  • Colored console output (DEBUG=cyan, INFO=green, WARNING=yellow, ERROR=red)
  • RotatingFileHandler for automatic log rotation
  • Configurable via LoggingSettings
  • Reduced noise from third-party libraries (uvicorn, sqlalchemy)

5. Logrotate Configuration

File: logrotate.conf

Production log rotation configuration for Linux systems.

# Install
sudo cp logrotate.conf /etc/logrotate.d/fastapi-app

# Test
sudo logrotate -d /etc/logrotate.d/fastapi-app

Settings:

  • Daily rotation
  • 14 days retention
  • Compressed with gzip
  • Handles missing files gracefully

Files Modified

1. Configuration

File: config.py

Added LoggingSettings class:

class LoggingSettings(BaseSettings):
    LOG_LEVEL: str = "INFO"
    LOG_DIR: str = "./logs"
    LOG_FILE: str = "api.log"
    LOG_MAX_BYTES: int = 10485760      # 10MB
    LOG_BACKUP_COUNT: int = 5
    LOG_FORMAT: str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
    LOG_DATE_FORMAT: str = "%Y-%m-%d %H:%M:%S"
    LOG_REQUEST_BODY: bool = False     # Enable for debugging
    LOG_RESPONSE_BODY: bool = False    # Enable for debugging
    LOG_EXCLUDE_PATHS: list[str] = ["/health", "/metrics", "/favicon.ico"]

2. Application Setup

File: setup.py

Changes in create_application():

  1. Initializes logging via setup_logging(settings)
  2. Registers exception handlers via setup_exception_handlers(application)
  3. Adds ExceptionHandlerMiddleware for runtime exceptions
  4. Adds LoggingMiddleware for request/response logging

Response Examples

Success Response (200)

{
    "data": {
        "id": 1,
        "name": "User Userson",
        "email": "user@example.com"
    },
    "isSuccess": true,
    "message": "User created successfully",
    "statusCode": 201
}

Validation Error (422)

{
    "data": [
        {
            "field": "password",
            "message": "This field is required",
            "type": "missing"
        },
        {
            "field": "email",
            "message": "Must be a valid email address",
            "type": "value_error.email"
        }
    ],
    "isSuccess": false,
    "message": "Validation failed",
    "statusCode": 422
}

Not Found Error (404)

{
    "data": null,
    "isSuccess": false,
    "message": "User not found",
    "statusCode": 404
}

Server Error (500) - Debug Mode

{
    "data": {
        "exception_type": "ValueError",
        "traceback": "Traceback (most recent call last):\n  ..."
    },
    "isSuccess": false,
    "message": "ValueError: Something went wrong",
    "statusCode": 500
}

Configuration

Add to your .env file:

# Logging Configuration
LOG_LEVEL=INFO                    # DEBUG, INFO, WARNING, ERROR, CRITICAL
LOG_DIR=./logs                    # Log file directory
LOG_FILE=api.log                  # Log file name
LOG_MAX_BYTES=10485760            # 10MB before rotation
LOG_BACKUP_COUNT=5                # Number of backup files
LOG_REQUEST_BODY=false            # Log request bodies (debug only!)
LOG_RESPONSE_BODY=false           # Log response bodies (debug only!)

# Environment (affects error detail in responses)
ENVIRONMENT=local                 # local, staging, production

Adding Custom Error Messages

To add more user-friendly messages, edit the ERROR_MESSAGES dictionary in exception_handler_middleware.py:

ERROR_MESSAGES = {
    # Add your custom translations
    "value_error.url.scheme": "URL must start with http:// or https://",
    "value_error.any_str.min_length": "This field is too short",
    # ...
}

No changes to your Pydantic schemas are required!

@karrtik159 karrtik159 changed the title adding a feature for custom exceptions and logs Add a feature for custom exceptions and logs Jan 17, 2026
@LucasQR
Copy link
Collaborator

LucasQR commented Jan 19, 2026

Hey @karrtik159, I'll merge #240, and I'd like you to change your pr to be built on top of it, with the changes @sri-dhurkesh suggested on the issue #239. Thanks for the contribution

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants