Skip to main content

FastAPI middleware for request/response logging, correlation IDs, and sensitive data redaction

Project description

auditry

FastAPI observability middleware with automatic request/response logging, correlation IDs, and sensitive data redaction.

PyPI version Python Versions License: MIT

Installation

pip install auditry

Quick Start

from fastapi import FastAPI
from auditry import configure_logging, ObservabilityMiddleware, ObservabilityConfig
from auditry import get_logger

# Configure structured logging at startup
configure_logging(level="INFO")

app = FastAPI()

# Add observability middleware (single line!)
app.add_middleware(
    ObservabilityMiddleware,
    config=ObservabilityConfig(
        service_name="my-service-name",
    ),
)

logger = get_logger(__name__) # always use the get_logger from this package

@app.get("/")
async def root():
   
    logger.info("Hello World")

    return {"message": "Hello World"}

Configuration

Required Configuration

config = ObservabilityConfig(
    service_name="your-service-name",  # REQUIRED
)

Full Configuration Options

config = ObservabilityConfig(
    # REQUIRED: Service name for log filtering
    service_name="my-service-name",

    # Correlation ID header name (default: X-Correlation-ID)
    # Use this if your org uses a different header, such as X-Request-ID
    correlation_id_header="X-Correlation-ID",

    # Maximum request/response body size to log (default: 10KB)
    payload_size_limit=10_240,

    # Additional sensitive field patterns to redact
    additional_redaction_patterns=["internal_id", "employee_ssn"],

    # Whether to log request headers (default: True)
    log_request_headers=True,

    # Whether to log response headers (default: False)
    log_response_headers=False,

    # Whether to log query parameters (default: True)
    log_query_params=True,
)

app.add_middleware(ObservabilityMiddleware, config=config)

Correlation IDs

Correlation IDs are automatically handled:

  • Incoming requests: Extracts from X-Correlation-ID header (or your custom header)
  • Generated if missing: Creates a new UUID if no correlation ID provided
  • Added to response: Returns the correlation ID in the response header
  • Included in logs: Automatically included in all structured logs

Using Correlation IDs in Your Code

from auditry import get_logger, get_correlation_id

logger = get_logger(__name__)

@app.get("/users/{user_id}")
async def get_user(user_id: str):
    # Correlation ID is automatically available
    correlation_id = get_correlation_id()

    # All logs automatically include the correlation ID
    logger.info(f"Fetching user {user_id}")

    return {"user_id": user_id, "correlation_id": correlation_id}

Propagating to Downstream Services

import httpx
from auditry import get_correlation_id

@app.get("/proxy")
async def proxy_request():
    # Get the current correlation ID
    correlation_id = get_correlation_id()

    # Pass it to downstream services
    async with httpx.AsyncClient() as client:
        response = await client.get(
            "https://downstream-service.com/api/data",
            headers={"X-Correlation-ID": correlation_id}  # Use your org's header name
        )

    return response.json()

User Tracking

The middleware automatically extracts user IDs from FastAPI dependencies and includes them in logs.

Supported User Patterns

The middleware automatically detects user IDs from these patterns:

  1. Object with id attribute: current_user.id
  2. Object with user_id attribute: current_user.user_id
  3. Dict with id key: user["id"]
  4. Dict with user_id key: user["user_id"]
  5. Dict with sub key: user["sub"] (JWT standard)
  6. Request state: request.state.user_id

You don't need to modify any existing code - the middleware automatically finds the user ID!

Business Event Tagging (For Analytics)

Tag specific endpoints as "business events" to make analytics queries easier for your sales/product teams.

Configuration

Tag endpoints in your middleware config - zero code changes needed in your actual endpoints:

from auditry import ObservabilityMiddleware, ObservabilityConfig, BusinessEventConfig

app.add_middleware(
    ObservabilityMiddleware,
    config=ObservabilityConfig(
        service_name="my-service-name",
        
        # Define which endpoints to tag for analytics
        business_events={
            "POST /workflows": BusinessEventConfig(
                event_type="workflow.created",
                extract_from_request=["file_id"],  # Pull file_id from request body
                extract_from_response=["id"],       # Pull workflow id from response
            ),
            "DELETE /workflows/{workflow_id}": BusinessEventConfig(
                event_type="workflow.deleted",
                extract_from_path=["workflow_id"],    # Pull workflow_id from URL path
            ),
        },
    ),
)

Log Output with Event Tags

Regular log (no tagging):

{
  "service": "my-service-name",
  "message": "Request completed: POST /workflows - Status: 201",
  "request": {...},
  "response": {...}
}

Tagged business event log:

{
  "service": "my-service-name",
  "message": "Request completed: POST /workflows - Status: 201",
  "event_type": "workflow.created",          // ← Filterable in log platform
  "business_context": {
    "file_id": "file_123",                   // ← From request body
    "id": "workflow_789"                    // ← From response body
  },
  "request": {...},
  "response": {...}
}

Supported Extract Locations

  • extract_from_request: Fields from request JSON body
  • extract_from_response: Fields from response JSON body
  • extract_from_path: Parameters from URL path (e.g., /workflows/{workflow_id})

Log Output

All logs are structured JSON, ready for log aggregators:

{
  "timestamp": "2025-10-28T12:34:56.789012+00:00",
  "level": "INFO",
  "service": "my-service-name",
  "correlation_id": "550e8400-e29b-41d4-a716-446655440000",
  "message": "Request completed: POST /workflows - Status: 201 - Duration: 45.23ms",
  "request": {
    "method": "POST",
    "path": "/workflows",
    "query_params": {},
    "headers": {"user-agent": "curl/7.64.1", "authorization": "[REDACTED]"},
    "body": {"name": "My Workflow", "password": "[REDACTED]"},
    "user_id": "user_12345"
  },
  "response": {
    "status_code": 201,
    "duration_ms": 45.23,
    "body": {"id": "workflow_789", "name": "My Workflow"}
  }
}

Sensitive Data Redaction

Automatically redacts these sensitive field patterns:

  • password
  • token
  • api_key / apikey
  • secret
  • authorization
  • ssn / social_security_number
  • credit_card / creditcard
  • x-api-key

Add custom patterns via configuration:

config = ObservabilityConfig(
    service_name="my-service-name",
    additional_redaction_patterns=["internal_token", "employee_id"],
)

Best Practices

1. Configure Logging Early

Call configure_logging() at application startup, before any other code:

from auditry import configure_logging

# First thing in your app
configure_logging(level="INFO")

app = FastAPI()
# ... rest of your app

2. Use Structured Logging

Always use get_logger(__name__) instead of standard Python logging:

from auditry import get_logger

logger = get_logger(__name__)

# Good - structured with correlation ID
logger.info("Processing payment", amount=100.50, currency="USD")

# Bad - loses structured data
import logging
logging.info("Processing payment")

3. Propagate Correlation IDs

When calling downstream services, always pass the correlation ID:

from auditry import get_correlation_id

correlation_id = get_correlation_id()
headers = {"X-Correlation-ID": correlation_id}  # Use your org's header name
response = await client.get(url, headers=headers)

4. Customize for Your Organization

Match your org's conventions:

config = ObservabilityConfig(
    service_name="my-service-name",
    correlation_id_header="X-Request-ID",  # If your org uses this header instead
    additional_redaction_patterns=["ssn", "tax_id"],  # Your sensitive fields
)

Example Output: Success vs Failure

Successful Request

{
  "level": "INFO",
  "service": "my-service-name",
  "correlation_id": "abc-123",
  "message": "Request completed: POST /workflows - Status: 201 - Duration: 45ms",
  "request": {...},
  "response": {...}
}

Failed Request

{
  "level": "ERROR",
  "service": "my-service-name",
  "correlation_id": "abc-123",
  "message": "Request failed: POST /workflows - Error: ValueError: Invalid name",
  "request": {...},
  "exception_type": "ValueError",
  "exception_message": "Invalid name",
  "execution_duration_ms": 12.34
}

License

MIT License - see LICENSE file for details.

Contributing

Contributions welcome! Please submit a Pull Request.

Support

For issues and questions: GitHub Issues

Author

Liv Stark - livstark.work@gmail.com

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

auditry-0.1.4.tar.gz (16.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

auditry-0.1.4-py3-none-any.whl (14.5 kB view details)

Uploaded Python 3

File details

Details for the file auditry-0.1.4.tar.gz.

File metadata

  • Download URL: auditry-0.1.4.tar.gz
  • Upload date:
  • Size: 16.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.9

File hashes

Hashes for auditry-0.1.4.tar.gz
Algorithm Hash digest
SHA256 37db397d9879a77f145ddb17a5b67e4d8ecdb645eb4a0f017485b11291d781b1
MD5 4422331c5974e1bbe1b23726643a329b
BLAKE2b-256 6b10bb613015b2279f5c409f3bbd5f4112a715ee60ed17c8a1d2929bfb47b10e

See more details on using hashes here.

File details

Details for the file auditry-0.1.4-py3-none-any.whl.

File metadata

  • Download URL: auditry-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 14.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.9

File hashes

Hashes for auditry-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 9749caa7813d0eb369fb2ee6bb6c7cb0a2c4f8b76a0812522f26dc1e20131003
MD5 c9c886e8a46c783ead7559b49d0d291f
BLAKE2b-256 16610ee1b5cd349731f50db5f97f67988cef5ee2e1e0457c0630b1d55f62f4cd

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page