reqly 0.2.0__tar.gz → 0.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. {reqly-0.2.0 → reqly-0.4.0}/PKG-INFO +241 -206
  2. {reqly-0.2.0 → reqly-0.4.0}/README.md +31 -5
  3. {reqly-0.2.0 → reqly-0.4.0}/pyproject.toml +10 -3
  4. {reqly-0.2.0 → reqly-0.4.0}/reqly/__init__.py +52 -19
  5. {reqly-0.2.0 → reqly-0.4.0}/reqly/core/client.py +14 -0
  6. {reqly-0.2.0 → reqly-0.4.0}/reqly/core/config.py +7 -0
  7. reqly-0.4.0/reqly/core/openapi_push.py +58 -0
  8. reqly-0.4.0/reqly/integrations/django.py +142 -0
  9. {reqly-0.2.0 → reqly-0.4.0}/reqly/integrations/fastapi.py +40 -6
  10. reqly-0.4.0/reqly/integrations/litestar.py +13 -0
  11. reqly-0.4.0/reqly/integrations/starlette.py +37 -0
  12. {reqly-0.2.0 → reqly-0.4.0}/reqly.egg-info/PKG-INFO +241 -206
  13. {reqly-0.2.0 → reqly-0.4.0}/reqly.egg-info/SOURCES.txt +6 -0
  14. {reqly-0.2.0 → reqly-0.4.0}/reqly.egg-info/requires.txt +11 -0
  15. {reqly-0.2.0 → reqly-0.4.0}/setup.cfg +4 -4
  16. reqly-0.4.0/tests/test_more_frameworks.py +250 -0
  17. reqly-0.4.0/tests/test_openapi_push.py +170 -0
  18. {reqly-0.2.0 → reqly-0.4.0}/reqly/core/__init__.py +0 -0
  19. {reqly-0.2.0 → reqly-0.4.0}/reqly/core/buffer.py +0 -0
  20. {reqly-0.2.0 → reqly-0.4.0}/reqly/core/capture.py +0 -0
  21. {reqly-0.2.0 → reqly-0.4.0}/reqly/core/sampling.py +0 -0
  22. {reqly-0.2.0 → reqly-0.4.0}/reqly/core/shipper.py +0 -0
  23. {reqly-0.2.0 → reqly-0.4.0}/reqly/integrations/__init__.py +0 -0
  24. {reqly-0.2.0 → reqly-0.4.0}/reqly/integrations/flask.py +0 -0
  25. {reqly-0.2.0 → reqly-0.4.0}/reqly.egg-info/dependency_links.txt +0 -0
  26. {reqly-0.2.0 → reqly-0.4.0}/reqly.egg-info/top_level.txt +0 -0
  27. {reqly-0.2.0 → reqly-0.4.0}/tests/test_buffer.py +0 -0
  28. {reqly-0.2.0 → reqly-0.4.0}/tests/test_capture.py +0 -0
  29. {reqly-0.2.0 → reqly-0.4.0}/tests/test_fastapi_integration.py +0 -0
  30. {reqly-0.2.0 → reqly-0.4.0}/tests/test_flask_integration.py +0 -0
  31. {reqly-0.2.0 → reqly-0.4.0}/tests/test_instrument.py +0 -0
  32. {reqly-0.2.0 → reqly-0.4.0}/tests/test_sampling.py +0 -0
  33. {reqly-0.2.0 → reqly-0.4.0}/tests/test_shipper.py +0 -0
  34. {reqly-0.2.0 → reqly-0.4.0}/tests/test_v2_fields.py +0 -0
@@ -1,206 +1,241 @@
1
- Metadata-Version: 2.4
2
- Name: reqly
3
- Version: 0.2.0
4
- Summary: Self-hosted API monitoring for FastAPI and Flask in two lines: latency percentiles, error rates and release tracking per route, with deploy-aware alerts and weekly AI anomaly reports.
5
- Author: Tanish Poddar
6
- License-Expression: GPL-3.0-or-later
7
- Project-URL: Homepage, https://reqly.tanisheesh.in
8
- Project-URL: Repository, https://github.com/tanisheesh/reqly
9
- Project-URL: Issues, https://github.com/tanisheesh/reqly/issues
10
- Project-URL: Documentation, https://github.com/tanisheesh/reqly/blob/main/docs/SETUP.md
11
- Project-URL: Changelog, https://github.com/tanisheesh/reqly/blob/main/sdk/CHANGELOG.md
12
- Project-URL: Live Demo, https://reqly-eventflow-dashboard.onrender.com
13
- Keywords: observability,apm,monitoring,fastapi,flask,telemetry,metrics,latency,percentiles,alerting,anomaly-detection,release-tracking,self-hosted,timescaledb
14
- Classifier: Development Status :: 4 - Beta
15
- Classifier: Intended Audience :: Developers
16
- Classifier: Programming Language :: Python :: 3
17
- Classifier: Programming Language :: Python :: 3.9
18
- Classifier: Programming Language :: Python :: 3.10
19
- Classifier: Programming Language :: Python :: 3.11
20
- Classifier: Programming Language :: Python :: 3.12
21
- Classifier: Programming Language :: Python :: 3.13
22
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
- Classifier: Topic :: System :: Monitoring
24
- Classifier: Framework :: FastAPI
25
- Classifier: Framework :: Flask
26
- Requires-Python: >=3.9
27
- Description-Content-Type: text/markdown
28
- Requires-Dist: httpx<1.0,>=0.27
29
- Provides-Extra: fastapi
30
- Requires-Dist: fastapi>=0.100; extra == "fastapi"
31
- Provides-Extra: flask
32
- Requires-Dist: flask>=2.3; extra == "flask"
33
- Provides-Extra: dev
34
- Requires-Dist: pytest>=8.0; extra == "dev"
35
- Requires-Dist: fastapi>=0.100; extra == "dev"
36
- Requires-Dist: flask>=2.3; extra == "dev"
37
- Requires-Dist: httpx>=0.27; extra == "dev"
38
-
39
- # reqly
40
-
41
- **Self-hosted API monitoring for FastAPI and Flask — two lines of code.**
42
- Latency percentiles, error rates and release tracking for every route, shipped to your own
43
- [Reqly](https://github.com/tanisheesh/reqly) collector — which turns them into deploy-aware
44
- hourly alerts and weekly AI anomaly reports.
45
-
46
- [![PyPI](https://img.shields.io/pypi/v/reqly?color=06b6d4&label=reqly)](https://pypi.org/project/reqly/)
47
- [![Python](https://img.shields.io/pypi/pyversions/reqly?color=06b6d4)](https://pypi.org/project/reqly/)
48
- [![License: GPL v3](https://img.shields.io/badge/license-GPL--3.0-06b6d4)](https://github.com/tanisheesh/reqly/blob/main/LICENSE)
49
-
50
- ---
51
-
52
- ## Install
53
-
54
- ```bash
55
- pip install reqly
56
- ```
57
-
58
- ## Usage
59
-
60
- **FastAPI**
61
-
62
- ```python
63
- import reqly
64
- from fastapi import FastAPI
65
-
66
- app = FastAPI()
67
- reqly.instrument(
68
- app,
69
- service_name="checkout-api",
70
- collector_url="https://reqly.example.com",
71
- api_key="your-ingest-key",
72
- )
73
- # Every route is now tracked: latency, errors, status codes, release
74
- ```
75
-
76
- **Flask**
77
-
78
- ```python
79
- import reqly
80
- from flask import Flask
81
-
82
- app = Flask(__name__)
83
- reqly.instrument(app, service_name="checkout-api") # settings from REQLY_* env vars
84
- ```
85
-
86
- `instrument()` detects FastAPI or Flask by itself — no decorators, no middleware to wire up.
87
- The release you're running is picked up automatically from your CI or host
88
- (`GITHUB_SHA`, `RENDER_GIT_COMMIT`, `VERCEL_GIT_COMMIT_SHA`, …), so deploys show up in
89
- Reqly with no extra code.
90
-
91
- ## What you get
92
-
93
- From the SDK, per request: method, **route template** (`/orders/{id}`, never the raw
94
- path), status code, duration, error type, host, **release**, environment and
95
- request/response **body size**.
96
-
97
- In the Reqly dashboard and collector:
98
-
99
- - **p50 / p95 / p99 latency** per route and per service — real percentiles from
100
- mergeable sketches, not the max of per-route numbers
101
- - **Error rates, status codes and top routes** over 1h / 6h / 24h / 7d
102
- - **Deploy markers and per-release health** — each release's error rate and p95
103
- - **Hourly alerts** to Slack, Discord or a webhook when a route breaks from its usual
104
- weekday-hour pattern, with **root-cause hints**
105
- - **Weekly AI report** — statistics find the anomalies, Groq (Llama 3.3-70b) writes the
106
- summary; plain-text fallback without an API key
107
-
108
- An alert from the demo data looks like this:
109
-
110
- ```
111
- 🔴 Anomaly — flask-demo /orders (Friday 15:00-16:00 UTC, z=5.37)
112
- • error rate 30.0% vs 2.2% usual · p95 6588ms vs 1576ms usual
113
- • running release v2 — vs v1: errors 2.6% → 33.1%, p95 2072ms → 4501ms
114
- • 100% of errors came from host pod-3, which served 23% of requests
115
- ```
116
-
117
- **Not on Python?** Node, Java, Go and .NET apps can report to the same collector through
118
- OpenTelemetry — no Reqly SDK needed. See the
119
- [OpenTelemetry guide](https://github.com/tanisheesh/reqly/blob/main/docs/OTEL.md).
120
-
121
- ## Configuration
122
-
123
- Every option can be passed to `instrument()` or set as an environment variable.
124
- Resolution order: **argument → environment variable → default**.
125
-
126
- | argument | environment variable | default |
127
- |---|---|---|
128
- | `service_name` | `REQLY_SERVICE_NAME` | `sys.argv[0]` basename |
129
- | `collector_url` | `REQLY_COLLECTOR_URL` | `http://localhost:8000` |
130
- | `api_key` | `REQLY_API_KEY` | `None` |
131
- | `release` | `REQLY_RELEASE`, then CI variables (`GITHUB_SHA`, `CI_COMMIT_SHA`, `RENDER_GIT_COMMIT`, `VERCEL_GIT_COMMIT_SHA`, `RAILWAY_GIT_COMMIT_SHA`, `HEROKU_SLUG_COMMIT`, `K_REVISION`, …) | auto-detected, else `None` |
132
- | `environment` | `REQLY_ENVIRONMENT` | `None` |
133
- | `sample_rate` | `REQLY_SAMPLE_RATE` | `1.0` |
134
- | `flush_interval_seconds` | `REQLY_FLUSH_INTERVAL_SECONDS` | `5.0` |
135
- | `max_batch_size` | `REQLY_MAX_BATCH_SIZE` | `200` |
136
- | `max_queue_size` | `REQLY_MAX_QUEUE_SIZE` | `2000` |
137
- | `ignore_routes` | `REQLY_IGNORE_ROUTES` (comma-separated) | `/health,/metrics` |
138
- | `capture_request_body` | `REQLY_CAPTURE_REQUEST_BODY` | `False` (not implemented yet) |
139
-
140
- With `sample_rate` below 1.0, request counts in the dashboard are the sampled volume;
141
- latency percentiles and error rates stay unbiased.
142
-
143
- ## Design guarantees
144
-
145
- **Fail-open** — any internal SDK error is caught and logged once; instrumentation disables
146
- itself rather than raise into your app. A slow or unreachable collector never blocks
147
- request threads — shipping happens on a background thread with strict HTTP timeouts.
148
-
149
- **Bounded cardinality** — routes are recorded as the framework's matched template
150
- (`/users/{id}`), never the raw path (`/users/123`). Unmatched paths (404s, scanners)
151
- collapse into a single `__unmatched__` bucket.
152
-
153
- **Bounded memory** — events wait in a fixed-size in-memory queue; under backpressure the
154
- oldest events are dropped and counted instead of growing without limit.
155
-
156
- **Safe retries** — batches are retried with exponential backoff on `408`, `429` and any
157
- `5xx` (for example a collector restart behind a proxy); other `4xx` responses are dropped
158
- immediately. Every event carries a unique `event_id` the collector deduplicates on, so a
159
- retry never double-counts.
160
-
161
- **Pre-fork servers** — under gunicorn `--preload` (or uWSGI without lazy-apps) each
162
- forked worker restarts its own flush thread and HTTP connection pool, so workers' events
163
- are shipped instead of silently queuing forever.
164
-
165
- ## Compatibility
166
-
167
- | | Supported |
168
- |---|---|
169
- | Python | 3.9 – 3.13 |
170
- | FastAPI | 0.100+ |
171
- | Flask | 2.3+ |
172
- | Collector | any version; `release`, `environment` and body sizes are stored by collector 0.3.0+ and ignored by older ones |
173
-
174
- ## Self-hosting the collector
175
-
176
- The SDK sends data to a Reqly collector you run. The full stack — collector,
177
- TimescaleDB and dashboard — starts with Docker Compose:
178
-
179
- ```bash
180
- git clone https://github.com/tanisheesh/reqly.git
181
- cd reqly
182
- docker compose up -d
183
- ```
184
-
185
- Setup, configuration and AWS deployment:
186
- [docs/SETUP.md](https://github.com/tanisheesh/reqly/blob/main/docs/SETUP.md) ·
187
- [infra/DEPLOY.md](https://github.com/tanisheesh/reqly/blob/main/infra/DEPLOY.md) ·
188
- [ingest API spec](https://github.com/tanisheesh/reqly/blob/main/docs/INGEST_SPEC.md)
189
-
190
- ## Live demo
191
-
192
- Reqly monitors [EventFlow](https://eventflow-g2h5.onrender.com), a Flask event management
193
- app, in production:
194
-
195
- - **Demo app** → [eventflow-g2h5.onrender.com](https://eventflow-g2h5.onrender.com)
196
- - **Metrics dashboard** → [reqly-eventflow-dashboard.onrender.com](https://reqly-eventflow-dashboard.onrender.com)
197
-
198
- > Log in as **Administrator** (`admin@eventhub.com` / `Admin@123`) → click **Metrics** in the nav.
199
-
200
- ## Changelog
201
-
202
- See [CHANGELOG.md](https://github.com/tanisheesh/reqly/blob/main/sdk/CHANGELOG.md).
203
-
204
- ## License
205
-
206
- GPL-3.0-or-later — see [LICENSE](https://github.com/tanisheesh/reqly/blob/main/LICENSE).
1
+ Metadata-Version: 2.4
2
+ Name: reqly
3
+ Version: 0.4.0
4
+ Summary: Self-hosted API monitoring for FastAPI, Flask, Django, Starlette and Litestar in two lines: latency percentiles, error rates and release tracking per route, with deploy-aware alerts and weekly AI anomaly reports.
5
+ Author: Tanish Poddar
6
+ License-Expression: GPL-3.0-or-later
7
+ Project-URL: Homepage, https://reqly.tanisheesh.in
8
+ Project-URL: Repository, https://github.com/tanisheesh/reqly
9
+ Project-URL: Issues, https://github.com/tanisheesh/reqly/issues
10
+ Project-URL: Documentation, https://github.com/tanisheesh/reqly/blob/main/docs/SETUP.md
11
+ Project-URL: Changelog, https://github.com/tanisheesh/reqly/blob/main/sdk/CHANGELOG.md
12
+ Project-URL: Live Demo, https://reqly-eventflow-dashboard.onrender.com
13
+ Keywords: observability,apm,monitoring,fastapi,flask,django,starlette,litestar,telemetry,metrics,latency,percentiles,alerting,anomaly-detection,release-tracking,self-hosted,timescaledb
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Classifier: Topic :: System :: Monitoring
24
+ Classifier: Framework :: Django
25
+ Classifier: Framework :: FastAPI
26
+ Classifier: Framework :: Flask
27
+ Requires-Python: >=3.9
28
+ Description-Content-Type: text/markdown
29
+ Requires-Dist: httpx<1.0,>=0.27
30
+ Provides-Extra: fastapi
31
+ Requires-Dist: fastapi>=0.100; extra == "fastapi"
32
+ Provides-Extra: starlette
33
+ Requires-Dist: starlette>=0.27; extra == "starlette"
34
+ Provides-Extra: litestar
35
+ Requires-Dist: litestar>=2.0; extra == "litestar"
36
+ Provides-Extra: flask
37
+ Requires-Dist: flask>=2.3; extra == "flask"
38
+ Provides-Extra: django
39
+ Requires-Dist: django>=4.2; extra == "django"
40
+ Provides-Extra: dev
41
+ Requires-Dist: pytest>=8.0; extra == "dev"
42
+ Requires-Dist: fastapi>=0.100; extra == "dev"
43
+ Requires-Dist: litestar>=2.0; extra == "dev"
44
+ Requires-Dist: flask>=2.3; extra == "dev"
45
+ Requires-Dist: django>=4.2; extra == "dev"
46
+ Requires-Dist: httpx>=0.27; extra == "dev"
47
+
48
+ # reqly
49
+
50
+ **Self-hosted API monitoring for FastAPI, Flask, Django, Starlette and Litestar — two lines of code.**
51
+ Latency percentiles, error rates and release tracking for every route, shipped to your own
52
+ [Reqly](https://github.com/tanisheesh/reqly) collector — which turns them into deploy-aware
53
+ hourly alerts and weekly AI anomaly reports.
54
+
55
+ [![PyPI](https://img.shields.io/pypi/v/reqly?color=06b6d4&label=reqly)](https://pypi.org/project/reqly/)
56
+ [![Python](https://img.shields.io/pypi/pyversions/reqly?color=06b6d4)](https://pypi.org/project/reqly/)
57
+ [![License: GPL v3](https://img.shields.io/badge/license-GPL--3.0-06b6d4)](https://github.com/tanisheesh/reqly/blob/main/LICENSE)
58
+
59
+ ---
60
+
61
+ ## Install
62
+
63
+ ```bash
64
+ pip install reqly
65
+ ```
66
+
67
+ ## Usage
68
+
69
+ **FastAPI**
70
+
71
+ ```python
72
+ import reqly
73
+ from fastapi import FastAPI
74
+
75
+ app = FastAPI()
76
+ reqly.instrument(
77
+ app,
78
+ service_name="checkout-api",
79
+ collector_url="https://reqly.example.com",
80
+ api_key="your-ingest-key",
81
+ )
82
+ # Every route is now tracked: latency, errors, status codes, release
83
+ ```
84
+
85
+ **Flask**
86
+
87
+ ```python
88
+ import reqly
89
+ from flask import Flask
90
+
91
+ app = Flask(__name__)
92
+ reqly.instrument(app, service_name="checkout-api") # settings from REQLY_* env vars
93
+ ```
94
+
95
+ **Starlette / Litestar** — same call:
96
+
97
+ ```python
98
+ reqly.instrument(app, service_name="checkout-api")
99
+ ```
100
+
101
+ **Django** (also Django REST Framework and Django Ninja) — Django has no app object, so add
102
+ the middleware first in `MIDDLEWARE`:
103
+
104
+ ```python
105
+ # settings.py
106
+ MIDDLEWARE = [
107
+ "reqly.integrations.django.ReqlyMiddleware",
108
+ # ...
109
+ ]
110
+ REQLY = {"service_name": "checkout-api", "api_key": "your-ingest-key"} # optional
111
+ ```
112
+
113
+ `instrument()` detects the framework by itself — no decorators, no middleware to wire up.
114
+ Routes are recorded as templates in one style across frameworks: Django's
115
+ `users/<int:pk>/` and DRF's `^users/(?P<pk>[^/.]+)/$` both become `/users/{pk}/`.
116
+ The release you're running is picked up automatically from your CI or host
117
+ (`GITHUB_SHA`, `RENDER_GIT_COMMIT`, `VERCEL_GIT_COMMIT_SHA`, …), so deploys show up in
118
+ Reqly with no extra code.
119
+
120
+ ## What you get
121
+
122
+ From the SDK, per request: method, **route template** (`/orders/{id}`, never the raw
123
+ path), status code, duration, error type, host, **release**, environment and
124
+ request/response **body size**.
125
+
126
+ In the Reqly dashboard and collector:
127
+
128
+ - **p50 / p95 / p99 latency** per route and per service — real percentiles from
129
+ mergeable sketches, not the max of per-route numbers
130
+ - **Error rates, status codes and top routes** over 1h / 6h / 24h / 7d
131
+ - **Deploy markers and per-release health** — each release's error rate and p95
132
+ - **Hourly alerts** to Slack, Discord or a webhook when a route breaks from its usual
133
+ weekday-hour pattern, with **root-cause hints**
134
+ - **API surface vs your OpenAPI spec** — undocumented endpoints that get traffic, documented
135
+ ones nobody calls, and deprecated ones still in use (`push_openapi=True`)
136
+ - **Weekly AI report** — statistics find the anomalies, Groq (gpt-oss-120b) writes the
137
+ summary; plain-text fallback without an API key
138
+
139
+ An alert from the demo data looks like this:
140
+
141
+ ```
142
+ 🔴 Anomaly — flask-demo /orders (Friday 15:00-16:00 UTC, z=5.37)
143
+ • error rate 30.0% vs 2.2% usual · p95 6588ms vs 1576ms usual
144
+ • running release v2 — vs v1: errors 2.6% → 33.1%, p95 2072ms → 4501ms
145
+ • 100% of errors came from host pod-3, which served 23% of requests
146
+ ```
147
+
148
+ **Not on Python?** Node, Java, Go and .NET apps can report to the same collector through
149
+ OpenTelemetry — no Reqly SDK needed. See the
150
+ [OpenTelemetry guide](https://github.com/tanisheesh/reqly/blob/main/docs/OTEL.md).
151
+
152
+ ## Configuration
153
+
154
+ Every option can be passed to `instrument()` or set as an environment variable.
155
+ Resolution order: **argument → environment variable → default**.
156
+
157
+ | argument | environment variable | default |
158
+ |---|---|---|
159
+ | `service_name` | `REQLY_SERVICE_NAME` | `sys.argv[0]` basename |
160
+ | `collector_url` | `REQLY_COLLECTOR_URL` | `http://localhost:8000` |
161
+ | `api_key` | `REQLY_API_KEY` | `None` |
162
+ | `release` | `REQLY_RELEASE`, then CI variables (`GITHUB_SHA`, `CI_COMMIT_SHA`, `RENDER_GIT_COMMIT`, `VERCEL_GIT_COMMIT_SHA`, `RAILWAY_GIT_COMMIT_SHA`, `HEROKU_SLUG_COMMIT`, `K_REVISION`, …) | auto-detected, else `None` |
163
+ | `environment` | `REQLY_ENVIRONMENT` | `None` |
164
+ | `sample_rate` | `REQLY_SAMPLE_RATE` | `1.0` |
165
+ | `flush_interval_seconds` | `REQLY_FLUSH_INTERVAL_SECONDS` | `5.0` |
166
+ | `max_batch_size` | `REQLY_MAX_BATCH_SIZE` | `200` |
167
+ | `max_queue_size` | `REQLY_MAX_QUEUE_SIZE` | `2000` |
168
+ | `ignore_routes` | `REQLY_IGNORE_ROUTES` (comma-separated) | `/health,/metrics` |
169
+ | `push_openapi` | `REQLY_PUSH_OPENAPI` | `False` — upload the app's OpenAPI spec (FastAPI, Litestar) on the first request |
170
+ | `capture_request_body` | `REQLY_CAPTURE_REQUEST_BODY` | `False` (not implemented yet) |
171
+
172
+ With `sample_rate` below 1.0, request counts in the dashboard are the sampled volume;
173
+ latency percentiles and error rates stay unbiased.
174
+
175
+ ## Design guarantees
176
+
177
+ **Fail-open** — any internal SDK error is caught and logged once; instrumentation disables
178
+ itself rather than raise into your app. A slow or unreachable collector never blocks
179
+ request threads — shipping happens on a background thread with strict HTTP timeouts.
180
+
181
+ **Bounded cardinality** — routes are recorded as the framework's matched template
182
+ (`/users/{id}`), never the raw path (`/users/123`). Unmatched paths (404s, scanners)
183
+ collapse into a single `__unmatched__` bucket.
184
+
185
+ **Bounded memory** — events wait in a fixed-size in-memory queue; under backpressure the
186
+ oldest events are dropped and counted instead of growing without limit.
187
+
188
+ **Safe retries** — batches are retried with exponential backoff on `408`, `429` and any
189
+ `5xx` (for example a collector restart behind a proxy); other `4xx` responses are dropped
190
+ immediately. Every event carries a unique `event_id` the collector deduplicates on, so a
191
+ retry never double-counts.
192
+
193
+ **Pre-fork servers** — under gunicorn `--preload` (or uWSGI without lazy-apps) each
194
+ forked worker restarts its own flush thread and HTTP connection pool, so workers' events
195
+ are shipped instead of silently queuing forever.
196
+
197
+ ## Compatibility
198
+
199
+ | | Supported |
200
+ |---|---|
201
+ | Python | 3.9 – 3.13 |
202
+ | FastAPI | 0.100+ (including routes in `app.mount()`ed sub-apps) |
203
+ | Starlette | 0.27+ (including `Mount`) |
204
+ | Litestar | 2.0+ |
205
+ | Flask | 2.3+ |
206
+ | Django | 4.2+, sync and async views; DRF and Django Ninja |
207
+ | Collector | any version; `release`, `environment` and body sizes are stored by collector 0.3.0+ and ignored by older ones; `push_openapi` needs 0.7.0+ |
208
+
209
+ ## Self-hosting the collector
210
+
211
+ The SDK sends data to a Reqly collector you run. The full stack — collector,
212
+ TimescaleDB and dashboard — starts with Docker Compose:
213
+
214
+ ```bash
215
+ git clone https://github.com/tanisheesh/reqly.git
216
+ cd reqly
217
+ docker compose up -d
218
+ ```
219
+
220
+ Setup, configuration and AWS deployment:
221
+ [docs/SETUP.md](https://github.com/tanisheesh/reqly/blob/main/docs/SETUP.md) ·
222
+ [infra/DEPLOY.md](https://github.com/tanisheesh/reqly/blob/main/infra/DEPLOY.md) ·
223
+ [ingest API spec](https://github.com/tanisheesh/reqly/blob/main/docs/INGEST_SPEC.md)
224
+
225
+ ## Live demo
226
+
227
+ Reqly monitors [EventFlow](https://eventflow-g2h5.onrender.com), a Flask event management
228
+ app, in production:
229
+
230
+ - **Demo app** → [eventflow-g2h5.onrender.com](https://eventflow-g2h5.onrender.com)
231
+ - **Metrics dashboard** → [reqly-eventflow-dashboard.onrender.com](https://reqly-eventflow-dashboard.onrender.com)
232
+
233
+ > Log in as **Administrator** (`admin@eventhub.com` / `Admin@123`) → click **Metrics** in the nav.
234
+
235
+ ## Changelog
236
+
237
+ See [CHANGELOG.md](https://github.com/tanisheesh/reqly/blob/main/sdk/CHANGELOG.md).
238
+
239
+ ## License
240
+
241
+ GPL-3.0-or-later — see [LICENSE](https://github.com/tanisheesh/reqly/blob/main/LICENSE).
@@ -1,6 +1,6 @@
1
1
  # reqly
2
2
 
3
- **Self-hosted API monitoring for FastAPI and Flask — two lines of code.**
3
+ **Self-hosted API monitoring for FastAPI, Flask, Django, Starlette and Litestar — two lines of code.**
4
4
  Latency percentiles, error rates and release tracking for every route, shipped to your own
5
5
  [Reqly](https://github.com/tanisheesh/reqly) collector — which turns them into deploy-aware
6
6
  hourly alerts and weekly AI anomaly reports.
@@ -45,7 +45,27 @@ app = Flask(__name__)
45
45
  reqly.instrument(app, service_name="checkout-api") # settings from REQLY_* env vars
46
46
  ```
47
47
 
48
- `instrument()` detects FastAPI or Flask by itself — no decorators, no middleware to wire up.
48
+ **Starlette / Litestar** — same call:
49
+
50
+ ```python
51
+ reqly.instrument(app, service_name="checkout-api")
52
+ ```
53
+
54
+ **Django** (also Django REST Framework and Django Ninja) — Django has no app object, so add
55
+ the middleware first in `MIDDLEWARE`:
56
+
57
+ ```python
58
+ # settings.py
59
+ MIDDLEWARE = [
60
+ "reqly.integrations.django.ReqlyMiddleware",
61
+ # ...
62
+ ]
63
+ REQLY = {"service_name": "checkout-api", "api_key": "your-ingest-key"} # optional
64
+ ```
65
+
66
+ `instrument()` detects the framework by itself — no decorators, no middleware to wire up.
67
+ Routes are recorded as templates in one style across frameworks: Django's
68
+ `users/<int:pk>/` and DRF's `^users/(?P<pk>[^/.]+)/$` both become `/users/{pk}/`.
49
69
  The release you're running is picked up automatically from your CI or host
50
70
  (`GITHUB_SHA`, `RENDER_GIT_COMMIT`, `VERCEL_GIT_COMMIT_SHA`, …), so deploys show up in
51
71
  Reqly with no extra code.
@@ -64,7 +84,9 @@ In the Reqly dashboard and collector:
64
84
  - **Deploy markers and per-release health** — each release's error rate and p95
65
85
  - **Hourly alerts** to Slack, Discord or a webhook when a route breaks from its usual
66
86
  weekday-hour pattern, with **root-cause hints**
67
- - **Weekly AI report** — statistics find the anomalies, Groq (Llama 3.3-70b) writes the
87
+ - **API surface vs your OpenAPI spec** — undocumented endpoints that get traffic, documented
88
+ ones nobody calls, and deprecated ones still in use (`push_openapi=True`)
89
+ - **Weekly AI report** — statistics find the anomalies, Groq (gpt-oss-120b) writes the
68
90
  summary; plain-text fallback without an API key
69
91
 
70
92
  An alert from the demo data looks like this:
@@ -97,6 +119,7 @@ Resolution order: **argument → environment variable → default**.
97
119
  | `max_batch_size` | `REQLY_MAX_BATCH_SIZE` | `200` |
98
120
  | `max_queue_size` | `REQLY_MAX_QUEUE_SIZE` | `2000` |
99
121
  | `ignore_routes` | `REQLY_IGNORE_ROUTES` (comma-separated) | `/health,/metrics` |
122
+ | `push_openapi` | `REQLY_PUSH_OPENAPI` | `False` — upload the app's OpenAPI spec (FastAPI, Litestar) on the first request |
100
123
  | `capture_request_body` | `REQLY_CAPTURE_REQUEST_BODY` | `False` (not implemented yet) |
101
124
 
102
125
  With `sample_rate` below 1.0, request counts in the dashboard are the sampled volume;
@@ -129,9 +152,12 @@ are shipped instead of silently queuing forever.
129
152
  | | Supported |
130
153
  |---|---|
131
154
  | Python | 3.9 – 3.13 |
132
- | FastAPI | 0.100+ |
155
+ | FastAPI | 0.100+ (including routes in `app.mount()`ed sub-apps) |
156
+ | Starlette | 0.27+ (including `Mount`) |
157
+ | Litestar | 2.0+ |
133
158
  | Flask | 2.3+ |
134
- | Collector | any version; `release`, `environment` and body sizes are stored by collector 0.3.0+ and ignored by older ones |
159
+ | Django | 4.2+, sync and async views; DRF and Django Ninja |
160
+ | Collector | any version; `release`, `environment` and body sizes are stored by collector 0.3.0+ and ignored by older ones; `push_openapi` needs 0.7.0+ |
135
161
 
136
162
  ## Self-hosting the collector
137
163
 
@@ -4,14 +4,15 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "reqly"
7
- version = "0.2.0"
8
- description = "Self-hosted API monitoring for FastAPI and Flask in two lines: latency percentiles, error rates and release tracking per route, with deploy-aware alerts and weekly AI anomaly reports."
7
+ version = "0.4.0"
8
+ description = "Self-hosted API monitoring for FastAPI, Flask, Django, Starlette and Litestar in two lines: latency percentiles, error rates and release tracking per route, with deploy-aware alerts and weekly AI anomaly reports."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
11
11
  license = "GPL-3.0-or-later"
12
12
  authors = [{ name = "Tanish Poddar" }]
13
13
  keywords = [
14
- "observability", "apm", "monitoring", "fastapi", "flask", "telemetry",
14
+ "observability", "apm", "monitoring", "fastapi", "flask", "django",
15
+ "starlette", "litestar", "telemetry",
15
16
  "metrics", "latency", "percentiles", "alerting", "anomaly-detection",
16
17
  "release-tracking", "self-hosted", "timescaledb"
17
18
  ]
@@ -26,6 +27,7 @@ classifiers = [
26
27
  "Programming Language :: Python :: 3.13",
27
28
  "Topic :: Software Development :: Libraries :: Python Modules",
28
29
  "Topic :: System :: Monitoring",
30
+ "Framework :: Django",
29
31
  "Framework :: FastAPI",
30
32
  "Framework :: Flask",
31
33
  ]
@@ -43,11 +45,16 @@ Changelog = "https://github.com/tanisheesh/reqly/blob/main/sdk/CHANGELOG.md"
43
45
 
44
46
  [project.optional-dependencies]
45
47
  fastapi = ["fastapi>=0.100"]
48
+ starlette = ["starlette>=0.27"]
49
+ litestar = ["litestar>=2.0"]
46
50
  flask = ["flask>=2.3"]
51
+ django = ["django>=4.2"]
47
52
  dev = [
48
53
  "pytest>=8.0",
49
54
  "fastapi>=0.100",
55
+ "litestar>=2.0",
50
56
  "flask>=2.3",
57
+ "django>=4.2",
51
58
  "httpx>=0.27",
52
59
  ]
53
60