|
| 1 | +# fastapi-structured-logging |
| 2 | + |
| 3 | +fastapi-structured-logging |
| 4 | + |
| 5 | +fastapi-structured-logging is a lightweight Python module that provides structured logging utilities and a configurable FastAPI access logging middleware. It configures `structlog` for JSON or console output, enriches log events with OpenTelemetry trace and span identifiers, and exposes an `AccessLogMiddleware` that can record request method, path, query parameters, client IP, user agent, status codes, processing time and more. |
| 6 | + |
| 7 | +The middleware supports filtering, trusted-proxy handling, custom fields and messages, and integrates cleanly with existing Python logging to produce consistent, machine-readable access logs for observability and tracing. |
| 8 | + |
| 9 | +## Usage |
| 10 | + |
| 11 | +```python |
| 12 | +from fastapi import FastAPI |
| 13 | + |
| 14 | +import fastapi_structured_logging |
| 15 | + |
| 16 | +# Set output to text if stdout is a tty, structured json if not |
| 17 | +fastapi_structured_logging.setup_logging() |
| 18 | + |
| 19 | +logger = fastapi_structured_logging.get_logger() |
| 20 | + |
| 21 | +app = FastAPI() |
| 22 | + |
| 23 | +app.add_middleware(fastapi_structured_logging.AccessLogMiddleware) |
| 24 | + |
| 25 | +``` |
| 26 | + |
| 27 | +## Configuration Options |
| 28 | + |
| 29 | +The library provides extensive configuration options to customize logging and access logging behavior. |
| 30 | + |
| 31 | +### setup_logging() Options |
| 32 | + |
| 33 | +- `json_logs` (Optional[bool]): Forces JSON output if True, console output if False. Defaults to JSON if stdout is not a tty (e.g., in containers or files). Example: `setup_logging(json_logs=True)` for always JSON logs. |
| 34 | +- `log_level` (str): Sets the logging level (e.g., "DEBUG", "INFO", "WARNING"). Defaults to "INFO". Example: `setup_logging(log_level="DEBUG")` to enable debug logging. |
| 35 | + |
| 36 | +### AccessLogMiddleware Options |
| 37 | + |
| 38 | +The middleware can be configured via an `AccessLogConfig` object passed to the middleware constructor. Example: |
| 39 | + |
| 40 | +```python |
| 41 | +from fastapi_structured_logging import AccessLogConfig |
| 42 | + |
| 43 | +config = AccessLogConfig( |
| 44 | + log_level="info", |
| 45 | + include_user_agent=False, |
| 46 | + exclude_paths={"/health"}, |
| 47 | + custom_fields={"app_version": "1.0.0"} |
| 48 | +) |
| 49 | +app.add_middleware(fastapi_structured_logging.AccessLogMiddleware, config=config) |
| 50 | +``` |
| 51 | + |
| 52 | +Key options include: |
| 53 | + |
| 54 | +- `enabled` (bool): Enables or disables access logging. Default: True. |
| 55 | +- `log_level` (str): Log level for access logs ("debug", "info", etc.). Default: "info". |
| 56 | +- `include_*` flags: Control which fields are logged, such as `include_method` (request method), `include_path` (request path), `include_query_params` (query parameters), `include_client_ip` (client IP), `include_user_agent` (user agent string), `include_forwarded_headers` (proxy headers), `include_status_code` (response status), `include_process_time` (processing time in ms), `include_content_length` (response content length), `include_referer` (referer header). All default to True except `include_referer` (False). |
| 57 | +- `exclude_*` sets: Filter out logs for specific paths (`exclude_paths`), methods (`exclude_methods`), status codes (`exclude_status_codes`), or paths only if status is 200 or 404 (`exclude_paths_if_ok_or_missing`). |
| 58 | +- `min_process_time` / `max_process_time` (Optional[float]): Only log requests with processing time within these bounds (in seconds). |
| 59 | +- `custom_message` (str): Custom log message. Default: "Access". |
| 60 | +- `custom_fields` (Dict[str, Any]): Additional fields to include in every log entry. Example: `{"app_version": "1.0.0"}`. |
| 61 | +- `logger_name` (str): Name of the logger to use. Default: "access_log". |
| 62 | +- `trusted_proxy` (List[str]): List of CIDR ranges for trusted proxies to extract real client IP. Example: `["10.0.0.0/8", "192.168.0.0/16"]`. |
| 63 | + |
| 64 | +## Convenience functions |
| 65 | + |
| 66 | +- `setup_logging()` initialize `structlog` to use line logging if stdout is a tty or JSONL if not (file, container output etc...) |
| 67 | + |
| 68 | +- `get_logger()` return a `structlog` logger |
| 69 | + |
| 70 | +- `json_serializer()` for fast serialization in `orjson` |
| 71 | + |
| 72 | + ```python |
| 73 | + |
| 74 | + @app.exception_handler(RequestValidationError) |
| 75 | + async def validation_exception_handler(request: Request, exc: RequestValidationError): |
| 76 | + exc_str = f"{exc}".replace("\n", " ").replace(" ", " ") |
| 77 | + logger.error("validation exception", validation_error=json_serializer(exc.errors())) |
| 78 | + content = {"status_code": 10422, "message": exc_str, "data": None} |
| 79 | + return JSONResponse(content=content, status_code=status.HTTP_422_UNPROCESSABLE_ENTITY) |
| 80 | + |
| 81 | + ``` |
| 82 | + |
| 83 | +## Setup dev env |
| 84 | + |
| 85 | +```bash |
| 86 | +uv venv |
| 87 | +uv pip install -r requirements-dev.txt |
| 88 | +pre-commit install |
| 89 | +``` |
| 90 | + |
| 91 | +## Test |
| 92 | + |
| 93 | +```bash |
| 94 | +. venv/bin/activate |
| 95 | +uv pip install -e .[full] |
| 96 | +pytest --cov-report html --cov-report term --cov-report xml:cov.xml |
| 97 | +``` |
| 98 | + |
| 99 | +## Build |
| 100 | + |
| 101 | +```bash |
| 102 | +echo x.y.z > VERSION |
| 103 | +uv pip install -r requirements-release.txt |
| 104 | +uv run python -m build -s -w |
| 105 | +``` |
0 commit comments