redline-core 0.2.0a1__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.
- redline_core-0.2.0a1/.gitignore +21 -0
- redline_core-0.2.0a1/LICENSE +21 -0
- redline_core-0.2.0a1/PKG-INFO +121 -0
- redline_core-0.2.0a1/README.md +71 -0
- redline_core-0.2.0a1/pyproject.toml +106 -0
- redline_core-0.2.0a1/src/redline_core/__init__.py +66 -0
- redline_core-0.2.0a1/src/redline_core/access.py +1091 -0
- redline_core-0.2.0a1/src/redline_core/api.py +900 -0
- redline_core-0.2.0a1/src/redline_core/blocking.py +538 -0
- redline_core-0.2.0a1/src/redline_core/callsite.py +173 -0
- redline_core-0.2.0a1/src/redline_core/cli.py +826 -0
- redline_core-0.2.0a1/src/redline_core/conf.py +670 -0
- redline_core-0.2.0a1/src/redline_core/config.py +686 -0
- redline_core-0.2.0a1/src/redline_core/contrib/__init__.py +13 -0
- redline_core-0.2.0a1/src/redline_core/contrib/_resolver.py +143 -0
- redline_core-0.2.0a1/src/redline_core/contrib/asgi.py +31 -0
- redline_core-0.2.0a1/src/redline_core/contrib/asyncpg.py +80 -0
- redline_core-0.2.0a1/src/redline_core/contrib/graphene.py +22 -0
- redline_core-0.2.0a1/src/redline_core/contrib/httpx.py +85 -0
- redline_core-0.2.0a1/src/redline_core/contrib/marshmallow.py +74 -0
- redline_core-0.2.0a1/src/redline_core/contrib/psycopg.py +103 -0
- redline_core-0.2.0a1/src/redline_core/contrib/redis.py +144 -0
- redline_core-0.2.0a1/src/redline_core/contrib/requests.py +65 -0
- redline_core-0.2.0a1/src/redline_core/contrib/sqlalchemy/__init__.py +350 -0
- redline_core-0.2.0a1/src/redline_core/contrib/sqlalchemy/suggest.py +566 -0
- redline_core-0.2.0a1/src/redline_core/contrib/strawberry.py +29 -0
- redline_core-0.2.0a1/src/redline_core/detect.py +438 -0
- redline_core-0.2.0a1/src/redline_core/detectors/__init__.py +29 -0
- redline_core-0.2.0a1/src/redline_core/detectors/_common.py +22 -0
- redline_core-0.2.0a1/src/redline_core/detectors/blocking.py +119 -0
- redline_core-0.2.0a1/src/redline_core/detectors/external.py +195 -0
- redline_core-0.2.0a1/src/redline_core/detectors/results.py +341 -0
- redline_core-0.2.0a1/src/redline_core/detectors/serializers.py +60 -0
- redline_core-0.2.0a1/src/redline_core/detectors/transactions.py +123 -0
- redline_core-0.2.0a1/src/redline_core/explain.py +161 -0
- redline_core-0.2.0a1/src/redline_core/histogram.py +231 -0
- redline_core-0.2.0a1/src/redline_core/index_advisor.py +840 -0
- redline_core-0.2.0a1/src/redline_core/linefix.py +42 -0
- redline_core-0.2.0a1/src/redline_core/load/__init__.py +59 -0
- redline_core-0.2.0a1/src/redline_core/load/analysis.py +187 -0
- redline_core-0.2.0a1/src/redline_core/load/control.py +295 -0
- redline_core-0.2.0a1/src/redline_core/load/engine.py +544 -0
- redline_core-0.2.0a1/src/redline_core/load/generate.py +445 -0
- redline_core-0.2.0a1/src/redline_core/load/profile.py +478 -0
- redline_core-0.2.0a1/src/redline_core/load/scenario.py +413 -0
- redline_core-0.2.0a1/src/redline_core/load/sinks.py +273 -0
- redline_core-0.2.0a1/src/redline_core/load/worker.py +319 -0
- redline_core-0.2.0a1/src/redline_core/model.py +515 -0
- redline_core-0.2.0a1/src/redline_core/plan_analysis.py +750 -0
- redline_core-0.2.0a1/src/redline_core/predicates.py +228 -0
- redline_core-0.2.0a1/src/redline_core/profiler.py +168 -0
- redline_core-0.2.0a1/src/redline_core/py.typed +0 -0
- redline_core-0.2.0a1/src/redline_core/recorder.py +1097 -0
- redline_core-0.2.0a1/src/redline_core/redact.py +574 -0
- redline_core-0.2.0a1/src/redline_core/runs.py +102 -0
- redline_core-0.2.0a1/src/redline_core/sampling.py +150 -0
- redline_core-0.2.0a1/src/redline_core/scan.py +520 -0
- redline_core-0.2.0a1/src/redline_core/sql.py +349 -0
- redline_core-0.2.0a1/src/redline_core/storage/__init__.py +53 -0
- redline_core-0.2.0a1/src/redline_core/storage/base.py +561 -0
- redline_core-0.2.0a1/src/redline_core/storage/derive.py +242 -0
- redline_core-0.2.0a1/src/redline_core/storage/private.py +80 -0
- redline_core-0.2.0a1/src/redline_core/storage/sqlite.py +1809 -0
- redline_core-0.2.0a1/src/redline_core/stream.py +200 -0
- redline_core-0.2.0a1/src/redline_core/transactions.py +101 -0
- redline_core-0.2.0a1/src/redline_core/ui/__init__.py +220 -0
- redline_core-0.2.0a1/src/redline_core/ui/static/assets/Flamegraph-pOyiCXpy.js +1 -0
- redline_core-0.2.0a1/src/redline_core/ui/static/assets/index-WiSpvf2f.css +1 -0
- redline_core-0.2.0a1/src/redline_core/ui/static/assets/index-rF9G2W0X.js +24 -0
- redline_core-0.2.0a1/src/redline_core/ui/static/badge.js +93 -0
- redline_core-0.2.0a1/src/redline_core/ui/static/index.html +25 -0
- redline_core-0.2.0a1/tests/__init__.py +0 -0
- redline_core-0.2.0a1/tests/conftest.py +94 -0
- redline_core-0.2.0a1/tests/contrib/__init__.py +0 -0
- redline_core-0.2.0a1/tests/contrib/conftest.py +30 -0
- redline_core-0.2.0a1/tests/contrib/test_drivers.py +129 -0
- redline_core-0.2.0a1/tests/contrib/test_http.py +104 -0
- redline_core-0.2.0a1/tests/contrib/test_marshmallow.py +62 -0
- redline_core-0.2.0a1/tests/contrib/test_redis.py +131 -0
- redline_core-0.2.0a1/tests/contrib/test_sqlalchemy.py +87 -0
- redline_core-0.2.0a1/tests/contrib/test_sqlalchemy_data.py +172 -0
- redline_core-0.2.0a1/tests/fixtures/mysql_full_scan.json +37 -0
- redline_core-0.2.0a1/tests/fixtures/pg_seq_scan.json +78 -0
- redline_core-0.2.0a1/tests/load/__init__.py +0 -0
- redline_core-0.2.0a1/tests/load/conftest.py +83 -0
- redline_core-0.2.0a1/tests/load/test_cli.py +475 -0
- redline_core-0.2.0a1/tests/load/test_engine.py +153 -0
- redline_core-0.2.0a1/tests/load/test_generate_routes.py +36 -0
- redline_core-0.2.0a1/tests/load/test_run_redaction.py +104 -0
- redline_core-0.2.0a1/tests/load/test_scenario.py +205 -0
- redline_core-0.2.0a1/tests/load/test_schedule_analysis.py +224 -0
- redline_core-0.2.0a1/tests/test_access.py +723 -0
- redline_core-0.2.0a1/tests/test_api.py +188 -0
- redline_core-0.2.0a1/tests/test_api_v2.py +405 -0
- redline_core-0.2.0a1/tests/test_badge_stream.py +221 -0
- redline_core-0.2.0a1/tests/test_blocking.py +432 -0
- redline_core-0.2.0a1/tests/test_cli_config.py +159 -0
- redline_core-0.2.0a1/tests/test_conf.py +58 -0
- redline_core-0.2.0a1/tests/test_config.py +390 -0
- redline_core-0.2.0a1/tests/test_contrib_graphql.py +263 -0
- redline_core-0.2.0a1/tests/test_detect.py +145 -0
- redline_core-0.2.0a1/tests/test_detectors_v02.py +281 -0
- redline_core-0.2.0a1/tests/test_explain.py +515 -0
- redline_core-0.2.0a1/tests/test_hardening.py +268 -0
- redline_core-0.2.0a1/tests/test_histogram.py +152 -0
- redline_core-0.2.0a1/tests/test_index_advisor.py +618 -0
- redline_core-0.2.0a1/tests/test_logs_locals.py +624 -0
- redline_core-0.2.0a1/tests/test_model.py +86 -0
- redline_core-0.2.0a1/tests/test_overhead.py +50 -0
- redline_core-0.2.0a1/tests/test_recorder.py +389 -0
- redline_core-0.2.0a1/tests/test_redact.py +151 -0
- redline_core-0.2.0a1/tests/test_review_fixes.py +262 -0
- redline_core-0.2.0a1/tests/test_sampling_callsite.py +108 -0
- redline_core-0.2.0a1/tests/test_scan.py +255 -0
- redline_core-0.2.0a1/tests/test_security_access.py +274 -0
- redline_core-0.2.0a1/tests/test_security_redaction.py +271 -0
- redline_core-0.2.0a1/tests/test_sql.py +156 -0
- redline_core-0.2.0a1/tests/test_storage.py +345 -0
- redline_core-0.2.0a1/tests/test_storage_v2.py +715 -0
- redline_core-0.2.0a1/tests/test_transactions.py +289 -0
- redline_core-0.2.0a1/tests/test_ui_profiler.py +120 -0
- redline_core-0.2.0a1/tests/test_v02_model_conf.py +297 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
.venv/
|
|
4
|
+
venv/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
site/
|
|
8
|
+
.redline/
|
|
9
|
+
*.db
|
|
10
|
+
*.sqlite3
|
|
11
|
+
.pytest_cache/
|
|
12
|
+
.ruff_cache/
|
|
13
|
+
.coverage
|
|
14
|
+
htmlcov/
|
|
15
|
+
node_modules/
|
|
16
|
+
frontend/dist/
|
|
17
|
+
.DS_Store
|
|
18
|
+
frontend/screenshots/
|
|
19
|
+
|
|
20
|
+
# agent worktrees
|
|
21
|
+
.claude/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mahmoud Ahmed
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: redline-core
|
|
3
|
+
Version: 0.2.0a1
|
|
4
|
+
Summary: Framework-agnostic request profiler engine: span tracing, SQL fingerprinting, N+1 detection, EXPLAIN analysis, storage and the Redline web UI.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Pythondeveloper6/redline
|
|
6
|
+
Project-URL: Documentation, https://pythondeveloper6.github.io/redline/
|
|
7
|
+
Project-URL: Source, https://github.com/Pythondeveloper6/redline
|
|
8
|
+
Project-URL: Issues, https://github.com/Pythondeveloper6/redline/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/Pythondeveloper6/redline/blob/main/CHANGELOG.md
|
|
10
|
+
Author-email: Mahmoud Ahmed <pythondeveloper6@gmail.com>
|
|
11
|
+
Maintainer-email: Mahmoud Ahmed <pythondeveloper6@gmail.com>
|
|
12
|
+
License-Expression: MIT
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Keywords: apm,explain,flamegraph,load-testing,n+1,performance,profiler,profiling,sql
|
|
15
|
+
Classifier: Development Status :: 3 - Alpha
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
25
|
+
Classifier: Topic :: Software Development :: Debuggers
|
|
26
|
+
Classifier: Topic :: System :: Monitoring
|
|
27
|
+
Classifier: Typing :: Typed
|
|
28
|
+
Requires-Python: >=3.10
|
|
29
|
+
Provides-Extra: asyncpg
|
|
30
|
+
Requires-Dist: asyncpg>=0.29; extra == 'asyncpg'
|
|
31
|
+
Provides-Extra: graphene
|
|
32
|
+
Requires-Dist: graphene>=3.3; extra == 'graphene'
|
|
33
|
+
Provides-Extra: httpx
|
|
34
|
+
Requires-Dist: httpx>=0.27; extra == 'httpx'
|
|
35
|
+
Provides-Extra: load
|
|
36
|
+
Requires-Dist: httpx>=0.27; extra == 'load'
|
|
37
|
+
Provides-Extra: profile
|
|
38
|
+
Requires-Dist: pyinstrument<6.0,>=5.0; extra == 'profile'
|
|
39
|
+
Provides-Extra: psycopg
|
|
40
|
+
Requires-Dist: psycopg>=3.1; extra == 'psycopg'
|
|
41
|
+
Provides-Extra: redis
|
|
42
|
+
Requires-Dist: redis>=5.0; extra == 'redis'
|
|
43
|
+
Provides-Extra: requests
|
|
44
|
+
Requires-Dist: requests>=2.31; extra == 'requests'
|
|
45
|
+
Provides-Extra: sqlalchemy
|
|
46
|
+
Requires-Dist: sqlalchemy<3.0,>=2.0; extra == 'sqlalchemy'
|
|
47
|
+
Provides-Extra: strawberry
|
|
48
|
+
Requires-Dist: strawberry-graphql>=0.240; extra == 'strawberry'
|
|
49
|
+
Description-Content-Type: text/markdown
|
|
50
|
+
|
|
51
|
+
<p align="center">
|
|
52
|
+
<a href="https://pythondeveloper6.github.io/redline/">
|
|
53
|
+
<picture>
|
|
54
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/Pythondeveloper6/redline/main/docs/assets/logo-dark.svg">
|
|
55
|
+
<img alt="Redline" src="https://raw.githubusercontent.com/Pythondeveloper6/redline/main/docs/assets/logo-light.svg" height="56">
|
|
56
|
+
</picture>
|
|
57
|
+
</a>
|
|
58
|
+
</p>
|
|
59
|
+
|
|
60
|
+
# redline-core
|
|
61
|
+
|
|
62
|
+
The framework-agnostic engine behind **Redline**, a self-hosted request profiler and
|
|
63
|
+
backend analyzer and load tester for Django, FastAPI, Flask and Litestar.
|
|
64
|
+
|
|
65
|
+
`redline-core` holds everything that does not depend on a web framework:
|
|
66
|
+
|
|
67
|
+
- the trace/span data model and a `contextvars` recorder that is correct across threads
|
|
68
|
+
and `asyncio` tasks and costs next to nothing when no request is being recorded;
|
|
69
|
+
- SQL fingerprinting, project-only callsite capture and secret redaction;
|
|
70
|
+
- detectors for N+1 queries, duplicate queries, slow queries and query budgets, plus
|
|
71
|
+
event-loop blocking, long transactions, huge and unbounded results, `len(qs)` vs `.exists()`,
|
|
72
|
+
slow serializers and slow, duplicated or retried external calls;
|
|
73
|
+
- an on-demand `EXPLAIN` runner with plan analysis for PostgreSQL, MySQL and SQLite;
|
|
74
|
+
- an optional `pyinstrument` flamegraph profiler (`redline-core[profile]`);
|
|
75
|
+
- a separate SQLite store with a background writer, retention, hourly rollups and release
|
|
76
|
+
history;
|
|
77
|
+
- a load tester (`redline load`, extra `load`) with an open-model scheduler, knee detection
|
|
78
|
+
and per-step bottlenecks;
|
|
79
|
+
- optional integrations in `redline_core.contrib` (SQLAlchemy, httpx, requests, redis,
|
|
80
|
+
asyncpg, psycopg, Marshmallow, Strawberry, Graphene);
|
|
81
|
+
- the access policy and layered configuration shared by every adapter;
|
|
82
|
+
- the framework-neutral JSON API and the prebuilt web UI.
|
|
83
|
+
|
|
84
|
+
It has **no runtime dependencies**.
|
|
85
|
+
|
|
86
|
+
## You probably want an adapter
|
|
87
|
+
|
|
88
|
+
Install the package for your framework; it pulls in `redline-core` for you:
|
|
89
|
+
|
|
90
|
+
Redline is not on PyPI yet: install from GitHub, naming the adapter **and** `redline-core` in
|
|
91
|
+
one command (the adapter pins the core exactly). Adapters: `django-redline`,
|
|
92
|
+
`fastapi-redline`, `flask-redline`, `litestar-redline`.
|
|
93
|
+
|
|
94
|
+
```console
|
|
95
|
+
pip install \
|
|
96
|
+
"django-redline @ git+https://github.com/Pythondeveloper6/redline#subdirectory=packages/django-redline" \
|
|
97
|
+
"redline-core @ git+https://github.com/Pythondeveloper6/redline#subdirectory=packages/redline-core"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Extras on the core requirement: `profile` (flamegraphs), `load` (the load tester), and
|
|
101
|
+
`httpx`, `requests`, `redis`, `asyncpg`, `psycopg`, `strawberry`, `graphene`. Documentation:
|
|
102
|
+
<https://pythondeveloper6.github.io/redline/>.
|
|
103
|
+
|
|
104
|
+
## Timing your own code
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
import redline_core
|
|
108
|
+
|
|
109
|
+
@redline_core.span("pricing.compute")
|
|
110
|
+
def compute_price(order): ...
|
|
111
|
+
|
|
112
|
+
async def handler():
|
|
113
|
+
async with redline_core.span("fetch-rates", kind="custom", provider="ecb"):
|
|
114
|
+
...
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`span()` does nothing (and costs almost nothing) outside a recorded request.
|
|
118
|
+
|
|
119
|
+
## License
|
|
120
|
+
|
|
121
|
+
MIT
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://pythondeveloper6.github.io/redline/">
|
|
3
|
+
<picture>
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/Pythondeveloper6/redline/main/docs/assets/logo-dark.svg">
|
|
5
|
+
<img alt="Redline" src="https://raw.githubusercontent.com/Pythondeveloper6/redline/main/docs/assets/logo-light.svg" height="56">
|
|
6
|
+
</picture>
|
|
7
|
+
</a>
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
# redline-core
|
|
11
|
+
|
|
12
|
+
The framework-agnostic engine behind **Redline**, a self-hosted request profiler and
|
|
13
|
+
backend analyzer and load tester for Django, FastAPI, Flask and Litestar.
|
|
14
|
+
|
|
15
|
+
`redline-core` holds everything that does not depend on a web framework:
|
|
16
|
+
|
|
17
|
+
- the trace/span data model and a `contextvars` recorder that is correct across threads
|
|
18
|
+
and `asyncio` tasks and costs next to nothing when no request is being recorded;
|
|
19
|
+
- SQL fingerprinting, project-only callsite capture and secret redaction;
|
|
20
|
+
- detectors for N+1 queries, duplicate queries, slow queries and query budgets, plus
|
|
21
|
+
event-loop blocking, long transactions, huge and unbounded results, `len(qs)` vs `.exists()`,
|
|
22
|
+
slow serializers and slow, duplicated or retried external calls;
|
|
23
|
+
- an on-demand `EXPLAIN` runner with plan analysis for PostgreSQL, MySQL and SQLite;
|
|
24
|
+
- an optional `pyinstrument` flamegraph profiler (`redline-core[profile]`);
|
|
25
|
+
- a separate SQLite store with a background writer, retention, hourly rollups and release
|
|
26
|
+
history;
|
|
27
|
+
- a load tester (`redline load`, extra `load`) with an open-model scheduler, knee detection
|
|
28
|
+
and per-step bottlenecks;
|
|
29
|
+
- optional integrations in `redline_core.contrib` (SQLAlchemy, httpx, requests, redis,
|
|
30
|
+
asyncpg, psycopg, Marshmallow, Strawberry, Graphene);
|
|
31
|
+
- the access policy and layered configuration shared by every adapter;
|
|
32
|
+
- the framework-neutral JSON API and the prebuilt web UI.
|
|
33
|
+
|
|
34
|
+
It has **no runtime dependencies**.
|
|
35
|
+
|
|
36
|
+
## You probably want an adapter
|
|
37
|
+
|
|
38
|
+
Install the package for your framework; it pulls in `redline-core` for you:
|
|
39
|
+
|
|
40
|
+
Redline is not on PyPI yet: install from GitHub, naming the adapter **and** `redline-core` in
|
|
41
|
+
one command (the adapter pins the core exactly). Adapters: `django-redline`,
|
|
42
|
+
`fastapi-redline`, `flask-redline`, `litestar-redline`.
|
|
43
|
+
|
|
44
|
+
```console
|
|
45
|
+
pip install \
|
|
46
|
+
"django-redline @ git+https://github.com/Pythondeveloper6/redline#subdirectory=packages/django-redline" \
|
|
47
|
+
"redline-core @ git+https://github.com/Pythondeveloper6/redline#subdirectory=packages/redline-core"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Extras on the core requirement: `profile` (flamegraphs), `load` (the load tester), and
|
|
51
|
+
`httpx`, `requests`, `redis`, `asyncpg`, `psycopg`, `strawberry`, `graphene`. Documentation:
|
|
52
|
+
<https://pythondeveloper6.github.io/redline/>.
|
|
53
|
+
|
|
54
|
+
## Timing your own code
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
import redline_core
|
|
58
|
+
|
|
59
|
+
@redline_core.span("pricing.compute")
|
|
60
|
+
def compute_price(order): ...
|
|
61
|
+
|
|
62
|
+
async def handler():
|
|
63
|
+
async with redline_core.span("fetch-rates", kind="custom", provider="ecb"):
|
|
64
|
+
...
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`span()` does nothing (and costs almost nothing) outside a recorded request.
|
|
68
|
+
|
|
69
|
+
## License
|
|
70
|
+
|
|
71
|
+
MIT
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "redline-core"
|
|
7
|
+
version = "0.2.0a1"
|
|
8
|
+
description = "Framework-agnostic request profiler engine: span tracing, SQL fingerprinting, N+1 detection, EXPLAIN analysis, storage and the Redline web UI."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "Mahmoud Ahmed", email = "pythondeveloper6@gmail.com" }]
|
|
14
|
+
maintainers = [{ name = "Mahmoud Ahmed", email = "pythondeveloper6@gmail.com" }]
|
|
15
|
+
keywords = [
|
|
16
|
+
"profiler",
|
|
17
|
+
"profiling",
|
|
18
|
+
"performance",
|
|
19
|
+
"apm",
|
|
20
|
+
"n+1",
|
|
21
|
+
"sql",
|
|
22
|
+
"explain",
|
|
23
|
+
"flamegraph",
|
|
24
|
+
"load-testing",
|
|
25
|
+
]
|
|
26
|
+
classifiers = [
|
|
27
|
+
"Development Status :: 3 - Alpha",
|
|
28
|
+
"Intended Audience :: Developers",
|
|
29
|
+
"Operating System :: OS Independent",
|
|
30
|
+
"Programming Language :: Python :: 3",
|
|
31
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
32
|
+
"Programming Language :: Python :: 3.10",
|
|
33
|
+
"Programming Language :: Python :: 3.11",
|
|
34
|
+
"Programming Language :: Python :: 3.12",
|
|
35
|
+
"Programming Language :: Python :: 3.13",
|
|
36
|
+
"Programming Language :: Python :: 3.14",
|
|
37
|
+
"Topic :: Software Development :: Debuggers",
|
|
38
|
+
"Topic :: System :: Monitoring",
|
|
39
|
+
"Typing :: Typed",
|
|
40
|
+
]
|
|
41
|
+
# Deliberately empty. The core is imported into the request path of an application and
|
|
42
|
+
# must never pull a framework (or anything else) into it uninvited. Storage is stdlib
|
|
43
|
+
# sqlite3; the flamegraph profiler is an opt-in extra.
|
|
44
|
+
dependencies = []
|
|
45
|
+
|
|
46
|
+
[project.optional-dependencies]
|
|
47
|
+
profile = ["pyinstrument>=5.0,<6.0"]
|
|
48
|
+
sqlalchemy = ["sqlalchemy>=2.0,<3.0"]
|
|
49
|
+
httpx = ["httpx>=0.27"]
|
|
50
|
+
requests = ["requests>=2.31"]
|
|
51
|
+
redis = ["redis>=5.0"]
|
|
52
|
+
asyncpg = ["asyncpg>=0.29"]
|
|
53
|
+
psycopg = ["psycopg>=3.1"]
|
|
54
|
+
strawberry = ["strawberry-graphql>=0.240"]
|
|
55
|
+
graphene = ["graphene>=3.3"]
|
|
56
|
+
load = ["httpx>=0.27"]
|
|
57
|
+
|
|
58
|
+
[project.scripts]
|
|
59
|
+
redline = "redline_core.cli:main"
|
|
60
|
+
|
|
61
|
+
[project.urls]
|
|
62
|
+
Homepage = "https://github.com/Pythondeveloper6/redline"
|
|
63
|
+
Documentation = "https://pythondeveloper6.github.io/redline/"
|
|
64
|
+
Source = "https://github.com/Pythondeveloper6/redline"
|
|
65
|
+
Issues = "https://github.com/Pythondeveloper6/redline/issues"
|
|
66
|
+
Changelog = "https://github.com/Pythondeveloper6/redline/blob/main/CHANGELOG.md"
|
|
67
|
+
|
|
68
|
+
[dependency-groups]
|
|
69
|
+
dev = [
|
|
70
|
+
"pytest>=8.3",
|
|
71
|
+
"pytest-asyncio>=0.24",
|
|
72
|
+
"ruff>=0.8",
|
|
73
|
+
"pyinstrument>=5.0",
|
|
74
|
+
"sqlalchemy[asyncio]>=2.0,<3.0",
|
|
75
|
+
"aiosqlite>=0.20",
|
|
76
|
+
"greenlet>=3.0",
|
|
77
|
+
"httpx>=0.27",
|
|
78
|
+
"requests>=2.31",
|
|
79
|
+
"marshmallow>=3.20",
|
|
80
|
+
"redis>=5.0",
|
|
81
|
+
"fakeredis>=2.20",
|
|
82
|
+
"asyncpg>=0.29",
|
|
83
|
+
"psycopg>=3.1",
|
|
84
|
+
"strawberry-graphql>=0.240",
|
|
85
|
+
"graphene>=3.3",
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
[tool.hatch.build.targets.wheel]
|
|
89
|
+
packages = ["src/redline_core"]
|
|
90
|
+
exclude = ["**/__pycache__", "**/*.py[cod]", "**/.DS_Store"]
|
|
91
|
+
|
|
92
|
+
[tool.hatch.build.targets.sdist]
|
|
93
|
+
include = ["/src/redline_core", "/tests", "/README.md", "/LICENSE"]
|
|
94
|
+
exclude = ["**/__pycache__", "**/*.py[cod]", "**/.DS_Store"]
|
|
95
|
+
|
|
96
|
+
[tool.pytest.ini_options]
|
|
97
|
+
testpaths = ["tests"]
|
|
98
|
+
addopts = "--strict-markers -q"
|
|
99
|
+
asyncio_mode = "auto"
|
|
100
|
+
asyncio_default_fixture_loop_scope = "function"
|
|
101
|
+
markers = [
|
|
102
|
+
"slow: spawns load-test worker processes (a few seconds each)",
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
[tool.ruff]
|
|
106
|
+
extend = "../../pyproject.toml"
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""redline-core: the framework-agnostic engine of Redline.
|
|
2
|
+
|
|
3
|
+
Redline is a self-hosted request profiler and backend analyzer for Django and FastAPI.
|
|
4
|
+
This package holds everything that does not depend on a web framework – the data model,
|
|
5
|
+
the ``contextvars`` recorder, SQL fingerprinting, callsite capture, redaction, the N+1 /
|
|
6
|
+
duplicate / slow-query detectors, EXPLAIN plan analysis, the optional pyinstrument
|
|
7
|
+
profiler, the SQLite store and the JSON API. It has no runtime dependencies.
|
|
8
|
+
|
|
9
|
+
Applications install ``django-redline`` or ``fastapi-redline``; the only thing most user
|
|
10
|
+
code touches directly is :func:`span`::
|
|
11
|
+
|
|
12
|
+
import redline_core
|
|
13
|
+
|
|
14
|
+
@redline_core.span("pricing.compute")
|
|
15
|
+
def compute_price(order): ...
|
|
16
|
+
|
|
17
|
+
async with redline_core.span("fetch-rates", provider="ecb"):
|
|
18
|
+
...
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
__version__ = "0.2.0a1"
|
|
24
|
+
|
|
25
|
+
from .conf import Settings, SettingsError
|
|
26
|
+
from .detect import Detector, FixSuggester, GenericSuggester
|
|
27
|
+
from .model import Callsite, Frame, Issue, Span, Suggestion, Trace
|
|
28
|
+
from .recorder import (
|
|
29
|
+
Recorder,
|
|
30
|
+
SpanContext,
|
|
31
|
+
TraceBuilder,
|
|
32
|
+
configure,
|
|
33
|
+
current,
|
|
34
|
+
finish_trace,
|
|
35
|
+
get_recorder,
|
|
36
|
+
record_query,
|
|
37
|
+
record_span,
|
|
38
|
+
span,
|
|
39
|
+
start_trace,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
__all__ = [
|
|
43
|
+
"Callsite",
|
|
44
|
+
"Detector",
|
|
45
|
+
"FixSuggester",
|
|
46
|
+
"Frame",
|
|
47
|
+
"GenericSuggester",
|
|
48
|
+
"Issue",
|
|
49
|
+
"Recorder",
|
|
50
|
+
"Settings",
|
|
51
|
+
"SettingsError",
|
|
52
|
+
"Span",
|
|
53
|
+
"SpanContext",
|
|
54
|
+
"Suggestion",
|
|
55
|
+
"Trace",
|
|
56
|
+
"TraceBuilder",
|
|
57
|
+
"__version__",
|
|
58
|
+
"configure",
|
|
59
|
+
"current",
|
|
60
|
+
"finish_trace",
|
|
61
|
+
"get_recorder",
|
|
62
|
+
"record_query",
|
|
63
|
+
"record_span",
|
|
64
|
+
"span",
|
|
65
|
+
"start_trace",
|
|
66
|
+
]
|