{"id":51544966,"url":"https://github.com/robertolima-dev/rust-py-monitor","last_synced_at":"2026-07-09T17:01:55.760Z","repository":{"id":365262000,"uuid":"1271297936","full_name":"robertolima-dev/rust-py-monitor","owner":"robertolima-dev","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-16T14:34:29.000Z","size":29,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-16T16:23:33.456Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/robertolima-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-16T14:22:28.000Z","updated_at":"2026-06-16T14:44:07.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/robertolima-dev/rust-py-monitor","commit_stats":null,"previous_names":["robertolima-dev/rust-py-monitor"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/robertolima-dev/rust-py-monitor","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertolima-dev%2Frust-py-monitor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertolima-dev%2Frust-py-monitor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertolima-dev%2Frust-py-monitor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertolima-dev%2Frust-py-monitor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/robertolima-dev","download_url":"https://codeload.github.com/robertolima-dev/rust-py-monitor/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertolima-dev%2Frust-py-monitor/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35306717,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-09T02:00:07.329Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-07-09T17:01:50.958Z","updated_at":"2026-07-09T17:01:55.750Z","avatar_url":"https://github.com/robertolima-dev.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# rust-py-monitor\n\n[![PyPI](https://img.shields.io/pypi/v/rust-py-monitor?color=e8673a\u0026label=PyPI)](https://pypi.org/project/rust-py-monitor/)\n[![Python](https://img.shields.io/pypi/pyversions/rust-py-monitor?color=4b8bbe)](https://pypi.org/project/rust-py-monitor/)\n[![License](https://img.shields.io/pypi/l/rust-py-monitor?color=3fb950)](https://github.com/robertolima-dev/rust-py-monitor/blob/main/LICENSE)\n[![GitHub](https://img.shields.io/github/stars/robertolima-dev/rust-py-monitor?style=flat\u0026color=e8673a)](https://github.com/robertolima-dev/rust-py-monitor)\n\n🌐 **[rust-py-monitor.vercel.app](https://rust-py-monitor.vercel.app/)**\n\nHigh-performance Python monitoring library with a Rust core.\n\nCollects CPU, memory, threads, and HTTP request metrics from Django and FastAPI applications with minimal overhead. Exports metrics to logs, JSON, and Prometheus.\n\n---\n\n## Features\n\n- **Process snapshot** — CPU %, RSS memory, virtual memory, thread count, PID\n- **FastAPI middleware** — per-request latency, method, path, status code (errors recorded even when a handler raises)\n- **Django middleware** — same, for WSGI and ASGI Django apps\n- **Aggregator** — total requests, error count, error rate, avg/min/max/p50/p95/p99 latency\n- **Bounded store** — request history is a capped ring buffer (default 10k) — constant memory under any traffic\n- **Multi-worker aggregation** — opt-in shared store merges metrics across gunicorn/uvicorn workers (`RPY_MULTIPROC_DIR`)\n- **Prometheus exporter** — `/metrics` endpoint compatible with Prometheus scraper\n- **Threshold alerts** — `check_alerts(...)` flags high CPU / memory against your limits\n- **Rust core** — collection and aggregation happen in Rust via PyO3; Python API stays simple\n\n---\n\n## Requirements\n\n- Python 3.10+\n- No mandatory runtime dependencies\n\nOptional, installed separately:\n- `fastapi` + `starlette` — for `MonitorMiddleware` and `make_fastapi_router()`\n- `django` — for `MonitorMiddleware` and `django_metrics_view`\n\n---\n\n## Installation\n\n```bash\npip install rust-py-monitor\n```\n\nWith optional extras:\n\n```bash\npip install \"rust-py-monitor[fastapi]\"\npip install \"rust-py-monitor[django]\"\npip install \"rust-py-monitor[fastapi,django,prometheus]\"\n```\n\n---\n\n## Quick Start\n\n```python\nimport rust_py_monitor\n\n# Process snapshot\nm = rust_py_monitor.snapshot()\nprint(m)\n# Snapshot(pid=1234, cpu=0.3%, rss=45.2MB, virt=512.0MB, threads=4, ts=1718000000)\n\nprint(m.pid)            # 1234\nprint(m.memory_rss_mb)  # 45.2\nprint(m.to_dict())      # {\"pid\": 1234, \"cpu_percent\": 0.3, ...}\n\n# Aggregated request metrics\nstats = rust_py_monitor.aggregate()\nprint(stats.total_requests)  # 0 (no middleware active yet)\nprint(stats.p95_latency_ms)  # 0.0\n```\n\n---\n\n## FastAPI\n\n### Middleware\n\n```python\nfrom fastapi import FastAPI\nfrom rust_py_monitor.fastapi import MonitorMiddleware\n\napp = FastAPI()\napp.add_middleware(MonitorMiddleware)\n\n\n@app.get(\"/\")\nasync def root():\n    return {\"status\": \"ok\"}\n```\n\n### Prometheus endpoint\n\n```python\nfrom fastapi import FastAPI\nfrom rust_py_monitor.fastapi import MonitorMiddleware\nfrom rust_py_monitor.prometheus import make_fastapi_router\n\napp = FastAPI()\napp.add_middleware(MonitorMiddleware)\napp.include_router(make_fastapi_router())          # GET /metrics\n# app.include_router(make_fastapi_router(\"/prom\")) # custom path\n```\n\n### Inspect metrics programmatically\n\n```python\nimport rust_py_monitor\n\nstats = rust_py_monitor.aggregate()\nprint(f\"Requests: {stats.total_requests}\")\nprint(f\"Errors:   {stats.total_errors} ({stats.error_rate:.1f}%)\")\nprint(f\"p95:      {stats.p95_latency_ms:.1f}ms\")\nprint(f\"p99:      {stats.p99_latency_ms:.1f}ms\")\n\nfor req in rust_py_monitor.get_requests()[-5:]:\n    print(req)\n    # RequestMetric(GET /api/users 200 12.34ms)\n```\n\n---\n\n## Django\n\n### Middleware\n\n```python\n# settings.py\nMIDDLEWARE = [\n    \"rust_py_monitor.django.MonitorMiddleware\",\n    # ... other middlewares ...\n]\n```\n\n### Prometheus endpoint\n\n```python\n# urls.py\nfrom django.urls import path\nfrom rust_py_monitor.prometheus import django_metrics_view\n\nurlpatterns = [\n    path(\"metrics/\", django_metrics_view),\n    # ...\n]\n```\n\nThe middleware supports both WSGI and ASGI Django applications automatically.\n\n---\n\n## Prometheus Output\n\n`GET /metrics` returns:\n\n```\n# HELP rpy_requests_total Total HTTP requests recorded\n# TYPE rpy_requests_total counter\nrpy_requests_total 1024\n\n# HELP rpy_errors_total Total HTTP errors (status \u003e= 400)\n# TYPE rpy_errors_total counter\nrpy_errors_total 12\n\n# HELP rpy_error_rate_percent HTTP error rate as a percentage\n# TYPE rpy_error_rate_percent gauge\nrpy_error_rate_percent 1.171875\n\n# HELP rpy_latency_p95_ms P95 request latency in milliseconds\n# TYPE rpy_latency_p95_ms gauge\nrpy_latency_p95_ms 47.3\n\n# HELP rpy_process_memory_rss_bytes Process RSS memory in bytes\n# TYPE rpy_process_memory_rss_bytes gauge\nrpy_process_memory_rss_bytes 52428800\n\n# ... (13 metrics total)\n```\n\n**Content-Type:** `text/plain; version=0.0.4; charset=utf-8`\n\n---\n\n## API Reference\n\n### `rust_py_monitor.snapshot() → Snapshot`\n\nCaptures a point-in-time snapshot of the current process.\n\n| Property | Type | Description |\n|---|---|---|\n| `pid` | `int` | Process ID |\n| `cpu_percent` | `float` | CPU usage (0–100 × cores). First call may return 0.0. |\n| `memory_rss` | `int` | Resident Set Size in bytes |\n| `memory_rss_mb` | `float` | RSS in megabytes (convenience) |\n| `memory_virtual` | `int` | Virtual memory in bytes |\n| `threads` | `int` | Thread count (0 on macOS/Windows) |\n| `timestamp` | `int` | Unix timestamp in seconds |\n| `to_dict()` | `dict` | All fields as a plain dict |\n\n---\n\n### `rust_py_monitor.aggregate() → AggregatedMetrics`\n\nComputes statistics over all requests recorded since startup (or last `clear_requests()`).\n\n| Property | Type | Description |\n|---|---|---|\n| `total_requests` | `int` | Total request count |\n| `total_errors` | `int` | Requests with status ≥ 400 |\n| `error_rate` | `float` | `total_errors / total_requests × 100` |\n| `avg_latency_ms` | `float` | Mean latency |\n| `min_latency_ms` | `float` | Minimum latency |\n| `max_latency_ms` | `float` | Maximum latency |\n| `p50_latency_ms` | `float` | Median latency |\n| `p95_latency_ms` | `float` | 95th percentile latency |\n| `p99_latency_ms` | `float` | 99th percentile latency |\n| `to_dict()` | `dict` | All fields as a plain dict |\n\n---\n\n### `rust_py_monitor.get_requests() → list[RequestMetric]`\n\nReturns all recorded requests. Each `RequestMetric` has:\n\n| Property | Type |\n|---|---|\n| `method` | `str` |\n| `path` | `str` |\n| `status_code` | `int` |\n| `duration_ms` | `float` |\n| `timestamp` | `int` |\n| `to_dict()` | `dict` |\n\n---\n\n### `rust_py_monitor.metrics_text() → str`\n\nReturns all metrics in Prometheus text exposition format (v0.0.4).\n\n---\n\n### `rust_py_monitor.check_alerts(cpu_percent=None, memory_rss_mb=None, memory_virtual_mb=None) → list[dict]`\n\nSimple, stateless threshold alerts over the current process snapshot. Pass the\nthresholds you want to watch; it returns the alerts that fired (a metric\n**exceeds** its threshold). Memory thresholds are in **megabytes**. Only the\nthresholds you provide are evaluated.\n\n```python\nimport rust_py_monitor\n\nfired = rust_py_monitor.check_alerts(cpu_percent=80, memory_rss_mb=500)\n# [{\"metric\": \"memory_rss_mb\", \"value\": 612.4, \"threshold\": 500, \"severity\": \"warning\"}]\n\nfor alert in fired:\n    print(f\"[alert] {alert['metric']}={alert['value']} \u003e {alert['threshold']}\")\n```\n\nEach alert is a dict `{\"metric\", \"value\", \"threshold\", \"severity\"}`, where\n`metric` is one of `\"cpu_percent\"`, `\"memory_rss_mb\"`, `\"memory_virtual_mb\"`.\nBeing stateless, you decide when to call it (in a `/health` handler, a periodic\ntask, etc.) and what to do with the result.\n\n---\n\n### `rust_py_monitor.clear_requests()`\n\nClears the request store. Useful for testing and periodic resets.\n\n---\n\n### `rust_py_monitor.set_max_requests(n)` / `get_max_requests() → int`\n\nThe request store is a bounded ring buffer (default capacity **10 000**). Once\nfull, the oldest entries are evicted first, so memory never grows without bound.\nUse these to tune the retention window.\n\n---\n\n## Multi-worker deployments (gunicorn / uvicorn)\n\nBy default each worker process keeps its own in-memory store. A Prometheus\nscrape of `/metrics` reaches only one worker, so the numbers would reflect just\nthat worker's traffic.\n\nSet the **`RPY_MULTIPROC_DIR`** environment variable to a writable directory to\nenable shared aggregation. Each worker writes a small fixed-size shard file\n(`rpy-\u003cpid\u003e.shard`); `aggregate()` and `metrics_text()` then merge **all** live\nworkers' shards at read time. Shards of dead workers are pruned automatically.\n\n```bash\nexport RPY_MULTIPROC_DIR=/tmp/rpy-metrics\ngunicorn -w 4 myapp:app\n```\n\nYou can also configure it at runtime:\n\n```python\nimport rust_py_monitor\nrust_py_monitor.set_multiproc_dir(\"/tmp/rpy-metrics\")\nrust_py_monitor.multiproc_enabled()   # True\nrust_py_monitor.get_multiproc_dir()   # \"/tmp/rpy-metrics\"\n```\n\n**Notes:**\n- Counters (`total_requests`, `total_errors`) and latency **histogram buckets**\n  are summed across workers. Latency percentiles (p50/p95/p99) are therefore\n  **approximated from the merged histogram** rather than computed exactly.\n- `get_requests()` always returns the **local** process's recent requests only.\n- Process metrics (CPU/memory/threads) reflect the worker that served the\n  scrape.\n\n---\n\n## Roadmap\n\n`rust-py-monitor` is mature (v0.2.0): process snapshots, FastAPI/Django\nmiddlewares, the latency aggregator, the bounded ring-buffer store, multi-worker\naggregation, and the Prometheus exporter are shipped. Directional ideas under\nconsideration (simple CPU/memory alerts, GC metrics, per-route labeled metrics,\nmore exporters/sinks, a Flask middleware) are tracked in\n[ROADMAP.md](./ROADMAP.md).\n\n---\n\n## Building from Source\n\nRequires Rust and [maturin](https://github.com/PyO3/maturin).\n\n```bash\npip install maturin\ngit clone https://github.com/robertolima-dev/rust-py-monitor\ncd rust-py-monitor\n\n# Development build (installs into current Python environment)\nmaturin develop\n\n# Release wheel\nmaturin build --release\n```\n\n### Running tests\n\n```bash\n# Rust unit tests\ncargo test\n\n# Python integration tests\npip install pytest pytest-asyncio httpx fastapi django\npytest tests/\n```\n\n---\n\n## Architecture\n\n```\nPython API (rust_py_monitor)\n    ├── snapshot()          ──► src/snapshot.rs   (sysinfo crate)\n    ├── aggregate()         ──► src/aggregator.rs (pure Rust math)\n    ├── get_requests()      ──► src/request_metrics.rs (static Mutex\u003cVecDeque\u003e, bounded)\n    ├── metrics_text()      ──► src/prometheus.rs (text formatter)\n    ├── set_multiproc_dir() ──► src/multiproc.rs  (mmap shard per worker)\n    │\n    ├── fastapi.MonitorMiddleware  ──► record_request() ──► Rust store\n    ├── django.MonitorMiddleware   ──► record_request() ──► Rust store\n    └── prometheus.make_fastapi_router() / django_metrics_view\n```\n\nThe Rust core is compiled to a native `.so` / `.pyd` extension module by [maturin](https://github.com/PyO3/maturin) and [PyO3](https://pyo3.rs). The Python layer is thin — it just routes calls and provides framework-specific adapters.\n\n---\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobertolima-dev%2Frust-py-monitor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frobertolima-dev%2Frust-py-monitor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobertolima-dev%2Frust-py-monitor/lists"}