README.md
March 24, 2026 Β· View on GitHub
fastapi_profiler
A FastAPI Middleware of joerick/pyinstrument to check your service performance.
π£ Info
A FastAPI Middleware of pyinstrument to check your service code performance.
Supports per-request profiling, sampling rate control, structured JSON logging, a built-in Web UI Dashboard, per-route profile history, runtime enable/disable, and request statistics aggregation (p95/p99).
π° Installation
Use uv (recommended)
$ uv add fastapi_profiler
Use pip
$ pip install fastapi_profiler -U
π Quick Start
import uvicorn
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from fastapi_profiler import PyInstrumentProfilerMiddleware
app = FastAPI()
app.add_middleware(PyInstrumentProfilerMiddleware)
@app.get("/test")
async def normal_request():
return JSONResponse({"retMsg": "Hello World!"})
if __name__ == "__main__":
uvicorn.run(app=app, host="0.0.0.0", port=8080, workers=1)
βοΈ Configuration Reference
All parameters are passed as keyword arguments to add_middleware().
Core Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
server_app | FastAPI | None | None | Pass the FastAPI app instance to register a shutdown handler that writes file-based output automatically. Required for html, prof, json, speedscope output types. |
profiler_output_type | str | "text" | Output format. One of "text", "html", "prof", "json", "speedscope". |
is_print_each_request | bool | True | Print/log the profile summary after every request. |
profiler_interval | float | 0.0001 | pyinstrument sampling interval in seconds. |
async_mode | str | "enabled" | pyinstrument async mode. |
html_file_name | str | None | "./fastapi-profiler.html" | Output file name for html type. |
prof_file_name | str | None | "./fastapi-profiler.prof" | Output file name for prof, json, and speedscope types. |
open_in_browser | bool | False | Automatically open the HTML report in a browser on shutdown. |
filter_paths | list[str] | None | None | List of URL path prefixes to skip profiling entirely (e.g. ["/health", "/metrics"]). |
1.5.0 New Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
slow_request_threshold_ms | float | 0 | Only emit profile output when request duration exceeds this value in milliseconds. 0 means always emit. |
profiler_sample_rate | float | 1.0 | Fraction of requests to profile (0.0 β 1.0). Useful for reducing overhead in production. |
always_profile_errors | bool | True | Always profile 5xx responses regardless of profiler_sample_rate or slow_request_threshold_ms. |
log_format | str | "text" | Log format for request lines. "text" emits a human-readable string; "json" emits a structured JSON object. |
max_profiles_per_route | int | 10 | Maximum number of ProfileRecord objects to keep in memory per route (rolling window). |
enable_dashboard | bool | False | Mount a built-in Web UI Dashboard with stats and runtime control APIs. |
dashboard_path | str | "/__profiler__" | URL prefix for the dashboard. Only used when enable_dashboard=True. |
enabled | bool | True | Master switch. When False, the middleware passes all requests through without profiling. Controllable at runtime via the /config API. |
π Usage Examples
Basic β print profile to stdout
app.add_middleware(PyInstrumentProfilerMiddleware)
Output to HTML file
Each sampled request that exceeds the slow-request threshold writes (or overwrites) the configured HTML file with the latest call-tree profile:
app.add_middleware(
PyInstrumentProfilerMiddleware,
server_app=app,
profiler_output_type="html",
is_print_each_request=False,
html_file_name="./fastapi-profiler.html",
)
Note: The file is updated on every qualifying request; it always contains the most recent profile. Use
profiler_output_type="text"withis_print_each_request=Trueif you want a log entry per request.
Sampling rate β profile only 10% of requests
app.add_middleware(
PyInstrumentProfilerMiddleware,
server_app=app,
profiler_sample_rate=0.1, # Profile ~10% of requests
)
Always profile errors β even with sampling disabled
app.add_middleware(
PyInstrumentProfilerMiddleware,
server_app=app,
profiler_sample_rate=0.0, # Normally profile nothing...
always_profile_errors=True, # ...but always profile 5xx responses
)
Slow-request threshold β only profile requests slower than 200 ms
app.add_middleware(
PyInstrumentProfilerMiddleware,
server_app=app,
slow_request_threshold_ms=200,
)
Structured JSON logging
import logging
logging.basicConfig(level=logging.INFO, format="%(message)s")
app.add_middleware(
PyInstrumentProfilerMiddleware,
server_app=app,
log_format="json", # {"logger": "fastapi_profiler", "method": "GET", ...}
)
Built-in Web UI Dashboard
app.add_middleware(
PyInstrumentProfilerMiddleware,
server_app=app,
enable_dashboard=True,
dashboard_path="/__profiler__",
filter_paths=["/__profiler__"], # Exclude dashboard from stats
)
After starting the server, open http://localhost:8080/__profiler__ in your browser.
Dashboard API endpoints
| Method | Path | Description |
|---|---|---|
GET | /__profiler__/ | HTML dashboard UI |
GET | /__profiler__/stats | JSON stats for all routes |
POST | /__profiler__/reset | Clear all collected stats |
POST | /__profiler__/config | Update runtime configuration |
/stats response shape:
{
"enabled": true,
"sample_rate": 1.0,
"slow_request_threshold_ms": 0,
"routes": [
{
"path": "/test",
"method": "GET",
"count": 42,
"error_count": 1,
"avg_duration_ms": 3.14,
"p95_duration_ms": 8.20,
"p99_duration_ms": 12.50,
"max_duration_ms": 15.00
}
]
}
/config request body (all fields optional):
{
"enabled": false,
"sample_rate": 0.5,
"slow_request_threshold_ms": 200
}
Runtime enable/disable
app.add_middleware(
PyInstrumentProfilerMiddleware,
server_app=app,
enabled=True,
enable_dashboard=True,
)
Toggle profiling without restarting the server:
# Disable profiling
curl -X POST http://localhost:8080/__profiler__/config \
-H "Content-Type: application/json" \
-d '{"enabled": false}'
# Re-enable with 50% sampling
curl -X POST http://localhost:8080/__profiler__/config \
-H "Content-Type: application/json" \
-d '{"enabled": true, "sample_rate": 0.5}'
Per-route profile history
app.add_middleware(
PyInstrumentProfilerMiddleware,
server_app=app,
enable_dashboard=True,
max_profiles_per_route=20, # Keep last 20 profiles per route
)
All features combined
app.add_middleware(
PyInstrumentProfilerMiddleware,
server_app=app,
# Sampling & thresholds
profiler_sample_rate=0.5,
always_profile_errors=True,
slow_request_threshold_ms=100,
# Logging
is_print_each_request=True,
log_format="json",
# Dashboard & stats
enable_dashboard=True,
dashboard_path="/__profiler__",
max_profiles_per_route=10,
filter_paths=["/__profiler__"],
# Runtime toggle
enabled=True,
)
π Example Files
| File | Description |
|---|---|
fastapi_example.py | Minimal setup β print to stdout |
fastapi_to_html_example.py | Output to HTML file |
fastapi_to_json_example.py | Output to JSON file |
fastapi_to_prof_example.py | Output to .prof file |
fastapi_to_speedscope_example.py | Output to Speedscope JSON |
fastapi_sampling_rate_example.py | Sampling rate control |
fastapi_always_profile_errors_example.py | Always profile 5xx errors |
fastapi_json_logging_example.py | Structured JSON logging |
fastapi_stats_dashboard_example.py | Web UI Dashboard + stats API |
fastapi_per_route_history_example.py | Per-route profile history |
fastapi_runtime_toggle_example.py | Runtime enable/disable |
fastapi_full_features_example.py | All features combined |
β Development
Setup
This project is managed with uv. Install all dependencies (including dev tools) with:
$ uv sync --group dev
Common Tasks
Use make (Linux/macOS) or make.bat (Windows) for common development tasks:
| Command | Description |
|---|---|
make install | Install all dependencies |
make lint | Run ruff + flake8 linters |
make typecheck | Run ty type checker |
make test | Run pytest with coverage |
make check | Run lint + typecheck + test |
make build | Build distribution packages |
make publish | Build and publish to PyPI |
make clean | Remove build artifacts |
Code Style
This project uses the following tools to ensure code quality:
- ruff β fast linter and formatter
- flake8 β style guide enforcement
- ty β fast Python type checker
- Codecov β test coverage reporting
CI
GitHub Actions runs the full matrix across Python 3.8 β 3.14 on Ubuntu, macOS, and Windows.
π‘ Author
π License
MIT Β©sunhailin-Leo