fastapi-api-logger
Plug & play auto-logging middleware for FastAPI with async I/O, structured JSON logs, request tracing, loguru integration, and a built-in dashboard.
Features
- Zero-config middleware — add one line and all requests are logged
- Structured JSON logs — timestamp, method, endpoint, status, status_code, response_time_ms
- Correlation / Request ID — unique ID per request, forwarded from API gateways, injected into response headers
- Day-wise log files —
logs/2026-03-25.log,logs/2026-03-26.log, ... - Async & non-blocking — logging never slows your API (bounded queue + background writer)
- Status categorization — SUCCESS (2xx), REDIRECT (3xx), WARNING (4xx), ERROR (5xx)
- Error tracebacks — full traceback captured for 5xx errors
- Request/response body capture — opt-in, with automatic sensitive field masking
- Loguru integration — optionally pipe all logs through loguru with configurable levels
- Built-in dashboard — web UI at
/logsto browse, filter, and search logs in real-time - Configurable log rotation — auto-deletes log files older than N days (you choose how many)
- Pure ASGI — works with FastAPI, Starlette, or any ASGI framework
Quick Start
Install
pip install fastapi-api-logger
# With loguru support
pip install fastapi-api-logger[loguru]
# With everything
pip install fastapi-api-logger[all]
Basic Usage (2 lines)
from fastapi import FastAPI
from fastapi_logger import LoggingMiddleware
app = FastAPI()
app.add_middleware(LoggingMiddleware)
That's it! All requests are now logged to logs/YYYY-MM-DD.log with unique request IDs.
Full-Featured Usage
from fastapi import FastAPI
from fastapi_logger import LoggingMiddleware, LogConfig, create_dashboard_router
app = FastAPI()
config = LogConfig(
log_dir="logs",
excluded_paths=["/health", "/docs", "/openapi.json"],
enable_request_body=True,
enable_response_body=True,
sensitive_fields={"password", "token", "secret"},
# Request ID
enable_request_id=True,
request_id_header="X-Request-ID",
# Loguru
enable_loguru=True,
# Dashboard
enable_dashboard=True,
dashboard_prefix="/logs",
# Rotation — delete logs older than 7 days
log_rotation_days=7,
)
app.add_middleware(LoggingMiddleware, config=config)
# Mount the dashboard (visit http://localhost:8000/logs)
app.mount("", create_dashboard_router(config))
Correlation / Request ID
Every request gets a unique ID (UUID4). This is essential for debugging in production.
$ curl -v http://localhost:8000/users/1
< x-request-id: a1b2c3d4e5f6...
How it works:
- If the client sends
X-Request-IDheader (e.g., from an API gateway), the middleware uses it - Otherwise, a new UUID is generated
- The ID is added to the response headers AND the log entry
- Your endpoint can access it via
request.scope["state"]["request_id"]
@app.get("/users/{user_id}")
async def get_user(user_id: int, request: Request):
request_id = request.scope["state"]["request_id"]
return {"id": user_id, "request_id": request_id}
Loguru Integration
Enable loguru to get beautiful colored terminal output alongside file logging:
config = LogConfig(
enable_loguru=True,
loguru_level_success="INFO", # 2xx
loguru_level_warning="WARNING", # 4xx
loguru_level_error="ERROR", # 5xx
)
Output:
2026-03-25 14:30:00 | INFO | [a1b2c3d4] GET /users/1 → 200 SUCCESS (42.1ms)
2026-03-25 14:30:01 | WARNING | [b2c3d4e5] GET /users/0 → 404 WARNING (3.2ms)
2026-03-25 14:30:02 | ERROR | [c3d4e5f6] GET /crash → 500 ERROR (1.1ms)
Install loguru: pip install fastapi-api-logger[loguru]
Dashboard
A built-in web UI to browse your logs — no React, no npm, just enable it:
config = LogConfig(enable_dashboard=True, dashboard_prefix="/logs")
app.mount("", create_dashboard_router(config))
Visit http://localhost:8000/logs to see:
- Real-time log entries (auto-refreshes every 5s)
- Stats cards: total, success, warning, error counts + avg response time
- Filters: by status, method, endpoint search, date picker
- Sorting: newest, oldest, or slowest first
- Color-coded response times: green (<100ms), yellow (<500ms), red (>500ms)
- Expandable tracebacks and request bodies
Configuration Reference
All Options
| Field | Type | Default | Description |
|---|---|---|---|
log_dir |
str |
"logs" |
Directory for log files |
log_format |
"json" / "text" |
"json" |
Log output format |
excluded_paths |
list[str] |
["/health", ...] |
Glob patterns to skip |
enable_request_body |
bool |
False |
Capture request body |
enable_response_body |
bool |
False |
Capture response body |
sensitive_fields |
set[str] |
{"password", ...} |
Fields to mask as *** |
max_body_size |
int |
10000 |
Max body bytes to capture |
log_rotation_days |
int |
30 |
Auto-delete logs older than N days (0 = keep forever) |
include_traceback |
bool |
True |
Include traceback for 5xx errors |
enable_request_id |
bool |
True |
Generate unique request ID per request |
request_id_header |
str |
"X-Request-ID" |
Header name for request ID |
inject_response_header |
bool |
True |
Add request ID to response headers |
enable_loguru |
bool |
False |
Also emit logs through loguru |
loguru_level_success |
str |
"INFO" |
Loguru level for 2xx |
loguru_level_warning |
str |
"WARNING" |
Loguru level for 4xx |
loguru_level_error |
str |
"ERROR" |
Loguru level for 5xx |
enable_dashboard |
bool |
False |
Enable the /logs web UI |
dashboard_prefix |
str |
"/logs" |
URL prefix for dashboard routes |
dashboard_page_size |
int |
100 |
Entries per page in dashboard |
Log Format
{
"timestamp": "2026-03-25T14:30:00.123456+00:00",
"method": "POST",
"endpoint": "/orders",
"status": "SUCCESS",
"status_code": 201,
"response_time_ms": 85.23,
"request_id": "a1b2c3d4e5f67890abcdef1234567890",
"client_ip": "127.0.0.1",
"request_body": "{\"item\": \"widget\", \"password\": \"***\"}",
"response_body": "{\"id\": 42}"
}
Architecture
Request → LoggingMiddleware (pure ASGI)
├─ Generates/extracts Request ID
├─ Injects X-Request-ID response header
├─ Measures response time (perf_counter)
├─ Captures status code, method, path
├─ Optionally captures request/response body
├─ Masks sensitive fields
└─ Enqueues log entry (non-blocking, O(1))
↓
asyncio.Queue (bounded, 10k max)
↓
AsyncLogWriter (background task)
├─ Day-wise file: logs/YYYY-MM-DD.log
├─ Auto-rotation: deletes old files
└─ Optional: loguru sink
Development
git clone https://github.com/paresh/fastapi-api-logger.git
cd fastapi-api-logger
pip install -e ".[dev]"
# Run tests (51 tests)
pytest tests/ -v
# Run the demo app
uvicorn examples.demo_app:app --reload
# Visit http://localhost:8000/logs for the dashboard
Publishing to PyPI
# Install build tools
pip install build twine
# Build
python -m build
# Verify
twine check dist/*
# Upload to TestPyPI (testing)
twine upload --repository testpypi dist/*
# Upload to PyPI (production)
twine upload dist/*
Versioning
Update version in two places:
pyproject.toml→version = "X.Y.Z"src/fastapi_logger/__init__.py→__version__ = "X.Y.Z"
License
MIT
Metadata
Release files for fastapi-api-logger 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fastapi_api_logger-0.2.1.tar.gz | 20.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fastapi_api_logger-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 38.4 kB
Release files / fastapi_api_logger-0.2.1.tar.gz
| Download URL | fastapi_api_logger-0.2.1.tar.gz |
|---|---|
| Size | 20.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d0df85cd3647fcac721947a363dc9bda32af4c1fdc95621230e101f801fed658
|
|
BLAKE2b-256 checksum How to use checksums |
dc698bc79c6500c8c6d5561a507e68b7b57d49ad17a9a9f30ba51a490fb2e162
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.12
|
Release files / fastapi_api_logger-0.2.1-py3-none-any.whl
| Download URL | fastapi_api_logger-0.2.1-py3-none-any.whl |
|---|---|
| Size | 18.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
13ea0a37a4b79f894d4994391fd01fa90274e0d27d317a358e53fdc8da97185e
|
|
BLAKE2b-256 checksum How to use checksums |
6a96b8b0b0532fe1556041c9185fe0b5a04b05dd045f66d6db2bfec238304191
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.12
|