fastapi-runtime-details-collector
Prometheus metrics collector for FastAPI applications that exposes event loop health and thread pool utilization in real time.
Why
FastAPI runs on an asyncio event loop with a thread pool for synchronous route handlers. Neither is observable out of the box. This library adds two key signals:
- Event loop lag — how long the loop was blocked before it could process the next iteration. Sustained lag means CPU-bound or blocking work is starving your async handlers.
- Thread pool saturation — how many of anyio's sync-handler threads are currently active versus the total capacity.
Installation
pip install fastapi-runtime-details-collector
Requires Python 3.10+ and a FastAPI application already using prometheus_client to expose a /metrics endpoint.
Metrics
| Metric | Type | Description |
|---|---|---|
fastapi_eventloop_lag_seconds |
Gauge | Drift between a scheduled asyncio.sleep and its actual wake-up, in seconds |
fastapi_eventloop_tasks_total |
Gauge | Number of asyncio tasks currently pending in the event loop |
fastapi_threadpool_capacity_tokens |
Gauge | Total token capacity of anyio's default thread limiter (max concurrent sync handlers) |
fastapi_threadpool_active_threads |
Gauge | Number of sync handler threads currently executing (anyio borrowed tokens) |
fastapi_metrics_last_collection_timestamp_seconds |
Gauge | Unix timestamp of the last successful metrics collection |
Metrics are collected every second by a background task. No metrics are exposed until the first successful collection.
Setup
1. Expose a /metrics endpoint
If you don't already have one:
pip install prometheus-client
from prometheus_client import make_asgi_app
metrics_app = make_asgi_app()
app.mount("/metrics", metrics_app)
2. Register the collector in your lifespan
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi_runtime_details_collector import FastAPIRuntimeCollector, setup_lag_monitor
@asynccontextmanager
async def lifespan(app: FastAPI):
collector = FastAPIRuntimeCollector()
task = setup_lag_monitor(collector)
yield
task.cancel()
app = FastAPI(lifespan=lifespan)
setup_lag_monitor must be called from within a running anyio event loop, so the FastAPI lifespan startup is the right place.
Custom registry
If you use a custom Prometheus registry instead of the global default:
from prometheus_client import CollectorRegistry
from prometheus_client import make_asgi_app
from fastapi_runtime_details_collector import FastAPIRuntimeCollector, setup_lag_monitor
registry = CollectorRegistry()
@asynccontextmanager
async def lifespan(app: FastAPI):
collector = FastAPIRuntimeCollector(registry=registry)
task = setup_lag_monitor(collector)
yield
task.cancel()
metrics_app = make_asgi_app(registry=registry)
app.mount("/metrics", metrics_app)
Full example
from contextlib import asynccontextmanager
from fastapi import FastAPI
from prometheus_client import make_asgi_app
from fastapi_runtime_details_collector import FastAPIRuntimeCollector, setup_lag_monitor
@asynccontextmanager
async def lifespan(app: FastAPI):
collector = FastAPIRuntimeCollector()
task = setup_lag_monitor(collector)
yield
task.cancel()
app = FastAPI(lifespan=lifespan)
app.mount("/metrics", make_asgi_app())
@app.get("/")
async def root():
return {"status": "ok"}
Run with uvicorn main:app and scrape http://localhost:8000/metrics.
Grafana dashboard
A ready-to-import dashboard is available in grafana/fastapi-runtime-details.json.
Panels:
- Event loop lag, pending tasks, active threads, seconds since last collection (stat panels with color thresholds)
- Event loop lag over time (time series)
- Pending asyncio tasks over time (time series)
- Thread pool active vs capacity over time (time series)
- Thread pool saturation % (gauge)
Import steps:
- In Grafana, go to Dashboards → Import
- Click Upload dashboard JSON file and select
grafana/fastapi-runtime-details.json - Select your Prometheus data source
- Pick the
joblabel that matches your app's scrape config
The dashboard auto-refreshes every 10 seconds and exposes a $job variable to filter by service.
Alerting
Example Prometheus alert rules are provided in prometheus/alerts.yml.
| Alert | Severity | Condition | For |
|---|---|---|---|
FastAPIEventLoopLagHigh |
warning | lag > 50 ms | 1 min |
FastAPIEventLoopLagCritical |
critical | lag > 200 ms | 30 s |
FastAPIThreadPoolSaturationHigh |
warning | active / capacity > 80 % | 2 min |
FastAPIThreadPoolSaturationCritical |
critical | active / capacity > 95 % | 1 min |
Threshold rationale:
- 50 ms lag is the point where human-perceptible async latency degradation begins; 200 ms indicates serious blocking.
- 80 % thread pool utilisation leaves a safety margin for bursts; 95 % means the pool is effectively full and requests will queue for a thread.
Load the rules into Prometheus by adding the file path under rule_files in your prometheus.yml:
rule_files:
- prometheus/alerts.yml
Metadata
Release files for fastapi-runtime-details-collector 1.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| fastapi_runtime_details_collector-1.0.2.tar.gz | 9.4 kB | With provenance | Details for fastapi_runtime_details_collector-1.0.2.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| fastapi_runtime_details_collector-1.0.2-py3-none-any.whl | Python 3 | none | any | Details for fastapi_runtime_details_collector-1.0.2-py3-none-any.whl |
Total release size: 15.1 kB
Release files / fastapi_runtime_details_collector-1.0.2.tar.gz
| Download URL | fastapi_runtime_details_collector-1.0.2.tar.gz |
|---|---|
| Size | 9.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1ba4a528388848a9a678747896f4c9002a87f8ce80f777e7ea8163a45218ba02
|
|
BLAKE2b-256 checksum How to use checksums |
739fb4e062c364e0b1e231ec05a61db3c97ad062dc042e4104a7032f64661b34
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 17, 2026.
Transparency logRelease files / fastapi_runtime_details_collector-1.0.2-py3-none-any.whl
| Download URL | fastapi_runtime_details_collector-1.0.2-py3-none-any.whl |
|---|---|
| Size | 5.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
242c50cdf71d777f9bbefa10c19b6f27c1b6c88a057ac9b0e5559e832ea1c575
|
|
BLAKE2b-256 checksum How to use checksums |
4f1f4908ac3f8db132b283da264388128ee1d6a7f1a222608e06d0113833f6dc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 17, 2026.
Transparency logSecurity for fastapi-runtime-details-collector 1.0.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for fastapi_runtime_details_collector-1.0.2.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 17, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.