asyncio Eventloop Blockers Detector/Analyzer.
Project description
LoopSentry
Asyncio Event Loop Blocker Detector & Analyzer
Detect blocking calls, slow async tasks, and performance bottlenecks in your asyncio applications. Captures stack traces, function arguments, CPU/memory/GC metrics. Generates standalone HTML reports and CSV exports.
HTML Report
Installation
pip install loopsentry
OR
uv add loopsentry
Requires
psutilandrich(installed automatically as dependencies).
Quick Start
import asyncio
from loopsentry import LoopSentry
async def main():
sentry = LoopSentry(threshold=0.1)
sentry.start()
# ... your application
sentry.stop()
asyncio.run(main())
Configuration
sentry = LoopSentry(
base_dir="sentry_logs", # log output directory
threshold=0.1, # blocking detection threshold (seconds)
async_threshold=1.0, # slow async task threshold (seconds)
capture_args=True, # capture function arguments at block time
detect_async_bottlenecks=True, # track slow async tasks
)
| Parameter | Default | Description |
|---|---|---|
base_dir |
"sentry_logs" |
Directory for log output |
threshold |
0.1 |
Seconds before a blocking call is flagged |
async_threshold |
Same as threshold |
Separate threshold for slow async tasks |
capture_args |
False |
Capture local variables from stack frames |
detect_async_bottlenecks |
False |
Monitor async task completion times |
FastAPI / Uvicorn
from contextlib import asynccontextmanager
from fastapi import FastAPI
from loopsentry import LoopSentry
@asynccontextmanager
async def lifespan(app: FastAPI):
sentry = LoopSentry(
threshold=0.1,
async_threshold=2.0,
capture_args=True,
detect_async_bottlenecks=True,
)
sentry.start()
yield
sentry.stop()
app = FastAPI(lifespan=lifespan)
What It Detects
| Pattern | Type | Example |
|---|---|---|
| Blocking sleep | Block | time.sleep() in async context |
| Sync HTTP | Block | requests.get() instead of aiohttp/httpx |
| Sync DB calls | Block | PyMongo, sqlite3 sync operations |
| Subprocess | Block | subprocess.run() |
| CPU loops | Block | Tight loops without yielding |
| Slow coroutines | Async | Tasks exceeding async_threshold |
| Crashes | Crash | Process killed during a block |
CLI
Interactive TUI
loopsentry analyze # auto-select latest logs
loopsentry analyze -d sentry_logs/ # specific directory
| Key | Action |
|---|---|
<ID> |
View event detail (stack trace, args, metrics) |
n / p |
Next / Previous page |
g |
Toggle group view (top offenders) |
s |
Cycle sort: time → duration → cpu → memory → type |
s:cpu |
Sort by specific column |
/text |
Search/filter events |
q |
Quit |
HTML Report
loopsentry analyze -d sentry_logs/ --html
loopsentry analyze -d sentry_logs/ --html -o report.html
Generates a standalone HTML file — no dependencies, no internet required. Open directly in any browser.
CSV Export
loopsentry analyze -d sentry_logs/ --csv
loopsentry analyze -d sentry_logs/ --csv -o data.csv
Full CLI Reference
loopsentry --version # Show version
loopsentry analyze [OPTIONS]
-d, --dir DIR Directory to scan
-f, --file FILE Specific .jsonl file to scan
--html Generate standalone HTML report
--csv Generate CSV report
--sort COLUMN Sort by: time | duration | cpu | memory | type
-o, --output PATH Output file path for HTML/CSV
Example
The examples/ directory contains a complete FastAPI application that deliberately triggers every type of blocking pattern LoopSentry detects.
Run the demo:
cd examples/
# Terminal 1 — start the server
uv run app.py
# Terminal 2 — fire test requests
uv run mock_server.py
# Terminal 3 — fire test requests
uv run loadtest.py -d 120 -o example_logs/loadtest
# Terminal 1 — stop server (Ctrl+C), then generate report
loopsentry analyze -d example_logs/ --html
Endpoints in the demo app:
| Endpoint | What it does |
|---|---|
GET /healthy |
Clean async endpoint (no block) |
GET /users/sync |
Sync SQLite query blocking the loop |
POST /hash |
CPU-bound PBKDF2 hashing |
GET /external |
Async HTTP via aiohttp (clean) |
GET /external/sync |
Sync HTTP via requests (blocks loop) |
GET /sleep/{seconds} |
time.sleep() blocking the loop |
GET /compute |
CPU-bound loop (5M iterations) |
GET /mixed |
Sleep + DB + hashing in one request |
How to Interpret the result
blocks smaller than 0.5 sec during high load / stress testing should be ignored. grouped view is just so you can find offender quickly , but in order for you to understand overall health of app , you should switch to timeline mode and see block durations across all events , to get better idea. during heavy load smaller non attention required tasks will also start blocking the event loop , unless those blocks are not associated with massvie cpu usage then you should focus on them later and should focus on blocks where avg block is greater than 0.6-7 seconds. in group view you do not need read each and every separate event , you can just read 1st one and get the idea of blocks.
you also need to take concurrency as variable to mind , check out report_async.html and report.html to understand how to interpret both blocks , also look at their loadtest.html files loadtest_async.html and loadtest.html
async app has proccessed ~11k requests in 2 min , while sync app has around 1k requests in 2min. if you notice in async app there is no block , all of the blocks are from loopsentry itself and that is expected , loopsentry will also have its own blockings given stack frames capture as well as , per core cpu usage etc... processes happens. which is fine given the value it can provide.
License
MIT
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file loopsentry-1.1.1.tar.gz.
File metadata
- Download URL: loopsentry-1.1.1.tar.gz
- Upload date:
- Size: 3.2 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.0 {"installer":{"name":"uv","version":"0.10.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Fedora Linux","version":"43","id":"","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f181c3b88f740210691098c1372c412f062fd94698566a533daa793ccfcf49b4
|
|
| MD5 |
83c64a2494204bb7a0cb9658bb4c0008
|
|
| BLAKE2b-256 |
b32182079473997d0491b6678f4dfb9bd4dac950736bd100429e7b32750cba49
|
File details
Details for the file loopsentry-1.1.1-py3-none-any.whl.
File metadata
- Download URL: loopsentry-1.1.1-py3-none-any.whl
- Upload date:
- Size: 22.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.0 {"installer":{"name":"uv","version":"0.10.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Fedora Linux","version":"43","id":"","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3c1e6230711eddc44a85aefc20769b75e0661093acba03cc8b9aba7a4ec18237
|
|
| MD5 |
5825ec30c2a08a27c7830bf747dd4891
|
|
| BLAKE2b-256 |
a21797abdc5c329748f3a5354ed677ae97951e240c7fb18e730e439b0199cfb9
|