Skip to main content

asyncio Eventloop Blockers Detector/Analyzer.

Project description

LoopSentry

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

▶ View Sample Report

HTML Report

LoopSentry Interface

Installation

pip install loopsentry

OR

uv add loopsentry

Requires psutil and rich (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.

▶ View Sample Report

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

loopsentry-1.1.1.tar.gz (3.2 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

loopsentry-1.1.1-py3-none-any.whl (22.4 kB view details)

Uploaded Python 3

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

Hashes for loopsentry-1.1.1.tar.gz
Algorithm Hash digest
SHA256 f181c3b88f740210691098c1372c412f062fd94698566a533daa793ccfcf49b4
MD5 83c64a2494204bb7a0cb9658bb4c0008
BLAKE2b-256 b32182079473997d0491b6678f4dfb9bd4dac950736bd100429e7b32750cba49

See more details on using hashes here.

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

Hashes for loopsentry-1.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 3c1e6230711eddc44a85aefc20769b75e0661093acba03cc8b9aba7a4ec18237
MD5 5825ec30c2a08a27c7830bf747dd4891
BLAKE2b-256 a21797abdc5c329748f3a5354ed677ae97951e240c7fb18e730e439b0199cfb9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page