tracepanel 0.0.1a1__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.
- tracepanel-0.0.1a1/LICENSE +21 -0
- tracepanel-0.0.1a1/PKG-INFO +206 -0
- tracepanel-0.0.1a1/README.md +146 -0
- tracepanel-0.0.1a1/pyproject.toml +272 -0
- tracepanel-0.0.1a1/pyproject.toml.orig +246 -0
- tracepanel-0.0.1a1/tracepanel/__init__.py +10 -0
- tracepanel-0.0.1a1/tracepanel/__main__.py +3 -0
- tracepanel-0.0.1a1/tracepanel/_agent.py +19 -0
- tracepanel-0.0.1a1/tracepanel/_bootstrap/sitecustomize.py +25 -0
- tracepanel-0.0.1a1/tracepanel/_cli.py +156 -0
- tracepanel-0.0.1a1/tracepanel/_compat.py +17 -0
- tracepanel-0.0.1a1/tracepanel/_run.py +173 -0
- tracepanel-0.0.1a1/tracepanel/_setup.py +36 -0
- tracepanel-0.0.1a1/tracepanel/api.py +154 -0
- tracepanel-0.0.1a1/tracepanel/asgi/__init__.py +43 -0
- tracepanel-0.0.1a1/tracepanel/asgi/middleware.py +260 -0
- tracepanel-0.0.1a1/tracepanel/console/__init__.py +1 -0
- tracepanel-0.0.1a1/tracepanel/console/app.py +95 -0
- tracepanel-0.0.1a1/tracepanel/console/messages.py +27 -0
- tracepanel-0.0.1a1/tracepanel/console/proxy.py +84 -0
- tracepanel-0.0.1a1/tracepanel/console/terminal.py +73 -0
- tracepanel-0.0.1a1/tracepanel/console/widget.py +164 -0
- tracepanel-0.0.1a1/tracepanel/core/__init__.py +1 -0
- tracepanel-0.0.1a1/tracepanel/core/conf.py +264 -0
- tracepanel-0.0.1a1/tracepanel/core/context.py +111 -0
- tracepanel-0.0.1a1/tracepanel/core/errors.py +82 -0
- tracepanel-0.0.1a1/tracepanel/core/explain.py +144 -0
- tracepanel-0.0.1a1/tracepanel/core/frames.py +143 -0
- tracepanel-0.0.1a1/tracepanel/core/ids.py +24 -0
- tracepanel-0.0.1a1/tracepanel/core/model.py +154 -0
- tracepanel-0.0.1a1/tracepanel/core/patching.py +63 -0
- tracepanel-0.0.1a1/tracepanel/core/registry.py +81 -0
- tracepanel-0.0.1a1/tracepanel/core/runtime.py +57 -0
- tracepanel-0.0.1a1/tracepanel/core/services.py +104 -0
- tracepanel-0.0.1a1/tracepanel/core/sql.py +163 -0
- tracepanel-0.0.1a1/tracepanel/django/__init__.py +90 -0
- tracepanel-0.0.1a1/tracepanel/django/apps.py +17 -0
- tracepanel-0.0.1a1/tracepanel/django/auth.py +18 -0
- tracepanel-0.0.1a1/tracepanel/django/cache.py +85 -0
- tracepanel-0.0.1a1/tracepanel/django/channels.py +204 -0
- tracepanel-0.0.1a1/tracepanel/django/commands.py +49 -0
- tracepanel-0.0.1a1/tracepanel/django/console.py +49 -0
- tracepanel-0.0.1a1/tracepanel/django/drf.py +223 -0
- tracepanel-0.0.1a1/tracepanel/django/errors.py +17 -0
- tracepanel-0.0.1a1/tracepanel/django/explain.py +29 -0
- tracepanel-0.0.1a1/tracepanel/django/mail.py +105 -0
- tracepanel-0.0.1a1/tracepanel/django/middleware.py +158 -0
- tracepanel-0.0.1a1/tracepanel/django/ninja.py +154 -0
- tracepanel-0.0.1a1/tracepanel/django/services.py +50 -0
- tracepanel-0.0.1a1/tracepanel/django/signals.py +177 -0
- tracepanel-0.0.1a1/tracepanel/django/sql.py +43 -0
- tracepanel-0.0.1a1/tracepanel/django/structure.py +81 -0
- tracepanel-0.0.1a1/tracepanel/django/sync_to_async.py +34 -0
- tracepanel-0.0.1a1/tracepanel/django/tasks.py +174 -0
- tracepanel-0.0.1a1/tracepanel/django/transactions.py +132 -0
- tracepanel-0.0.1a1/tracepanel/django/users.py +145 -0
- tracepanel-0.0.1a1/tracepanel/django/widget.py +24 -0
- tracepanel-0.0.1a1/tracepanel/falcon/__init__.py +138 -0
- tracepanel-0.0.1a1/tracepanel/fastapi/__init__.py +16 -0
- tracepanel-0.0.1a1/tracepanel/flask/__init__.py +141 -0
- tracepanel-0.0.1a1/tracepanel/integrations/__init__.py +6 -0
- tracepanel-0.0.1a1/tracepanel/integrations/_tasks.py +35 -0
- tracepanel-0.0.1a1/tracepanel/integrations/celery.py +248 -0
- tracepanel-0.0.1a1/tracepanel/integrations/dramatiq.py +207 -0
- tracepanel-0.0.1a1/tracepanel/integrations/htmx.py +79 -0
- tracepanel-0.0.1a1/tracepanel/integrations/logging.py +115 -0
- tracepanel-0.0.1a1/tracepanel/integrations/otel.py +176 -0
- tracepanel-0.0.1a1/tracepanel/integrations/rq.py +170 -0
- tracepanel-0.0.1a1/tracepanel/integrations/sqlalchemy.py +213 -0
- tracepanel-0.0.1a1/tracepanel/integrations/strawberry.py +172 -0
- tracepanel-0.0.1a1/tracepanel/py.typed +0 -0
- tracepanel-0.0.1a1/tracepanel/storage/__init__.py +1 -0
- tracepanel-0.0.1a1/tracepanel/storage/agent.py +277 -0
- tracepanel-0.0.1a1/tracepanel/storage/transport.py +133 -0
- tracepanel-0.0.1a1/tracepanel/wsgi/__init__.py +181 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DevRealm OÜ
|
|
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,206 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tracepanel
|
|
3
|
+
Version: 0.0.1a1
|
|
4
|
+
Summary: Traces and logs for Python apps: every request, task and command with its SQL and log lines, N+1 queries found
|
|
5
|
+
Keywords: debug toolbar,django,fastapi,flask,n+1,sql,tracing,profiling
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Environment :: Web Environment
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Framework :: FastAPI
|
|
12
|
+
Classifier: Framework :: Flask
|
|
13
|
+
Classifier: Framework :: Django
|
|
14
|
+
Classifier: Framework :: Django :: 5.2
|
|
15
|
+
Classifier: Framework :: Django :: 6.0
|
|
16
|
+
Classifier: Framework :: Django :: 6.1
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
23
|
+
Classifier: Topic :: Software Development :: Debuggers
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Requires-Dist: tracepanel-agent==0.0.1a1 ; (platform_machine == 'aarch64' and sys_platform == 'linux') or (platform_machine == 'armv6l' and sys_platform == 'linux') or (platform_machine == 'armv7l' and sys_platform == 'linux') or (platform_machine == 'i686' and sys_platform == 'linux') or (platform_machine == 'ppc64le' and sys_platform == 'linux') or (platform_machine == 'riscv64' and sys_platform == 'linux') or (platform_machine == 's390x' and sys_platform == 'linux') or (platform_machine == 'x86_64' and sys_platform == 'linux') or (platform_machine == 'AMD64' and sys_platform == 'win32') or (platform_machine == 'ARM64' and sys_platform == 'win32') or (platform_machine == 'x86' and sys_platform == 'win32') or sys_platform == 'darwin'
|
|
26
|
+
Requires-Dist: celery>=5.3 ; extra == 'celery'
|
|
27
|
+
Requires-Dist: channels>=4.2 ; extra == 'channels'
|
|
28
|
+
Requires-Dist: django>=5.2 ; extra == 'django'
|
|
29
|
+
Requires-Dist: dramatiq>=1.17 ; extra == 'dramatiq'
|
|
30
|
+
Requires-Dist: djangorestframework>=3.15 ; extra == 'drf'
|
|
31
|
+
Requires-Dist: falcon>=4.0 ; extra == 'falcon'
|
|
32
|
+
Requires-Dist: fastapi>=0.142 ; extra == 'fastapi'
|
|
33
|
+
Requires-Dist: flask>=3.0 ; extra == 'flask'
|
|
34
|
+
Requires-Dist: django-ninja>=1.4 ; extra == 'ninja'
|
|
35
|
+
Requires-Dist: opentelemetry-sdk>=1.45 ; extra == 'otel'
|
|
36
|
+
Requires-Dist: rq>=2.0 ; extra == 'rq'
|
|
37
|
+
Requires-Dist: sqlalchemy>=2.0 ; extra == 'sqlalchemy'
|
|
38
|
+
Requires-Dist: starlette>=0.40 ; extra == 'starlette'
|
|
39
|
+
Requires-Dist: strawberry-graphql>=0.250 ; extra == 'strawberry'
|
|
40
|
+
Requires-Python: >=3.11
|
|
41
|
+
Project-URL: Homepage, https://github.com/tracepanel/tracepanel
|
|
42
|
+
Project-URL: Repository, https://github.com/tracepanel/tracepanel-python
|
|
43
|
+
Project-URL: Issues, https://github.com/tracepanel/tracepanel-python/issues
|
|
44
|
+
Project-URL: Changelog, https://github.com/tracepanel/tracepanel-python/blob/main/CHANGELOG.md
|
|
45
|
+
Provides-Extra: celery
|
|
46
|
+
Provides-Extra: channels
|
|
47
|
+
Provides-Extra: django
|
|
48
|
+
Provides-Extra: dramatiq
|
|
49
|
+
Provides-Extra: drf
|
|
50
|
+
Provides-Extra: falcon
|
|
51
|
+
Provides-Extra: fastapi
|
|
52
|
+
Provides-Extra: flask
|
|
53
|
+
Provides-Extra: ninja
|
|
54
|
+
Provides-Extra: otel
|
|
55
|
+
Provides-Extra: rq
|
|
56
|
+
Provides-Extra: sqlalchemy
|
|
57
|
+
Provides-Extra: starlette
|
|
58
|
+
Provides-Extra: strawberry
|
|
59
|
+
Description-Content-Type: text/markdown
|
|
60
|
+
|
|
61
|
+
# Tracepanel
|
|
62
|
+
|
|
63
|
+
**See what your app does on every request.** Tracepanel records each request,
|
|
64
|
+
background task and command of your Python app with its SQL queries, logs and
|
|
65
|
+
errors. It points out N+1 queries, duplicate queries and slow SQL, and names the
|
|
66
|
+
line of code to fix. You read it in a panel on your pages and in a console.
|
|
67
|
+
|
|
68
|
+
It runs while you develop, and everything stays on your machine.
|
|
69
|
+
|
|
70
|
+
## Install
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pip install tracepanel
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Tracepanel is on only in development: when `DEBUG` is on, in a debug server,
|
|
77
|
+
or under `tracepanel run`. Add it to your app:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
# Django: settings.py
|
|
81
|
+
INSTALLED_APPS += ["tracepanel.django"]
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
# FastAPI
|
|
86
|
+
from fastapi import FastAPI
|
|
87
|
+
|
|
88
|
+
import tracepanel.fastapi
|
|
89
|
+
|
|
90
|
+
app = FastAPI(debug=True)
|
|
91
|
+
tracepanel.fastapi.install(app)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
# Flask, started with `flask run --debug`
|
|
96
|
+
from flask import Flask
|
|
97
|
+
|
|
98
|
+
import tracepanel.flask
|
|
99
|
+
|
|
100
|
+
app = Flask(__name__)
|
|
101
|
+
tracepanel.flask.install(app)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Open a page of your app and click the badge in the corner. On a page that
|
|
105
|
+
loads each post's author in its own query, the panel shows
|
|
106
|
+
`SELECT auth_user ×50` marked N+1, the line of code that ran it, and the
|
|
107
|
+
`select_related()` that turns 50 queries into one. The console at
|
|
108
|
+
`/__tracepanel__/` holds every request, task and command the app ran.
|
|
109
|
+
|
|
110
|
+
Tracepanel also traces Celery, Dramatiq, RQ, SQLAlchemy, Strawberry, Django REST
|
|
111
|
+
framework, Django Ninja, Channels and htmx when they are installed. It reads the
|
|
112
|
+
log lines you write with `logging` and attaches each one to the request, task
|
|
113
|
+
or command that wrote it. Your logging configuration stays as it is.
|
|
114
|
+
|
|
115
|
+
For Django, `pip install django-tracepanel` installs the same package.
|
|
116
|
+
|
|
117
|
+
## Run without changing your code
|
|
118
|
+
|
|
119
|
+
`tracepanel run` turns Tracepanel on for one command. The project stays as it
|
|
120
|
+
is:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
tracepanel run python manage.py runserver # Django
|
|
124
|
+
tracepanel run uvicorn app.main:app # FastAPI, Starlette
|
|
125
|
+
tracepanel run flask --app app run # Flask
|
|
126
|
+
tracepanel run gunicorn app:app # Falcon
|
|
127
|
+
tracepanel run celery -A app worker # a worker
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Configure
|
|
131
|
+
|
|
132
|
+
Every setting has a default. To change one, set it in one of these places, and
|
|
133
|
+
a later place wins:
|
|
134
|
+
|
|
135
|
+
1. `tracepanel.toml`, or `[tool.tracepanel]` in `pyproject.toml`
|
|
136
|
+
2. the `TRACEPANEL` dict in Django's settings
|
|
137
|
+
3. a `TRACEPANEL_<NAME>` variable, such as `TRACEPANEL_SLOW_SQL_MS=50`
|
|
138
|
+
|
|
139
|
+
```toml
|
|
140
|
+
# pyproject.toml
|
|
141
|
+
[tool.tracepanel]
|
|
142
|
+
slow_sql_ms = 50
|
|
143
|
+
n_plus_one_threshold = 5
|
|
144
|
+
ignore_paths = ["/health*", "/static/*"]
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
`tracepanel config` prints each setting, its value and where it comes from.
|
|
148
|
+
|
|
149
|
+
## Trace your own code
|
|
150
|
+
|
|
151
|
+
To add your own steps to a request or a task, or to trace a script or a
|
|
152
|
+
scheduled job:
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
import time
|
|
156
|
+
|
|
157
|
+
import tracepanel
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@tracepanel.traced
|
|
161
|
+
def rebuild_index() -> None:
|
|
162
|
+
with tracepanel.span("index.load", source="db") as span:
|
|
163
|
+
time.sleep(0.01)
|
|
164
|
+
span.set("rows", 120)
|
|
165
|
+
tracepanel.event("index rebuilt", rows=120)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
with tracepanel.trace("rebuild-index"):
|
|
169
|
+
tracepanel.set_attr("tenant", "acme", on_trace=True)
|
|
170
|
+
rebuild_index()
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
`span()` adds a step to the current trace, and `@traced` does the same for a
|
|
174
|
+
function. `trace()` starts a new trace. Tracepanel stores keyword attributes
|
|
175
|
+
under `app.`, so `source` becomes `app.source`. SQL, logs and errors inside a
|
|
176
|
+
span belong to that span. Outside development these calls do nothing, so you
|
|
177
|
+
can leave them in.
|
|
178
|
+
|
|
179
|
+
## Command line
|
|
180
|
+
|
|
181
|
+
| Command | What it does |
|
|
182
|
+
| --- | --- |
|
|
183
|
+
| `tracepanel run <command>` | runs a command with Tracepanel on |
|
|
184
|
+
| `tracepanel dev` | runs the agent in the foreground, with the console on http://127.0.0.1:8765/ |
|
|
185
|
+
| `tracepanel config` | prints the settings in effect and where each comes from |
|
|
186
|
+
| `tracepanel clear` | deletes every stored trace, log, query, email and issue |
|
|
187
|
+
| `tracepanel mcp` | gives an AI coding agent your traces over MCP |
|
|
188
|
+
|
|
189
|
+
## Supported versions
|
|
190
|
+
|
|
191
|
+
| Python | Django |
|
|
192
|
+
| --- | --- |
|
|
193
|
+
| 3.11 | 5.2 |
|
|
194
|
+
| 3.12, 3.13, 3.14 | 5.2, 6.0, 6.1 |
|
|
195
|
+
| 3.15 | 6.1 |
|
|
196
|
+
|
|
197
|
+
## Links
|
|
198
|
+
|
|
199
|
+
- [tracepanel](https://github.com/tracepanel/tracepanel): what Tracepanel finds,
|
|
200
|
+
how it works, privacy, and the agent this package installs
|
|
201
|
+
- [CHANGELOG.md](CHANGELOG.md): what changed in each version
|
|
202
|
+
- [CONTRIBUTING.md](CONTRIBUTING.md): developing this package
|
|
203
|
+
|
|
204
|
+
## License
|
|
205
|
+
|
|
206
|
+
Licensed under the [MIT License](LICENSE).
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Tracepanel
|
|
2
|
+
|
|
3
|
+
**See what your app does on every request.** Tracepanel records each request,
|
|
4
|
+
background task and command of your Python app with its SQL queries, logs and
|
|
5
|
+
errors. It points out N+1 queries, duplicate queries and slow SQL, and names the
|
|
6
|
+
line of code to fix. You read it in a panel on your pages and in a console.
|
|
7
|
+
|
|
8
|
+
It runs while you develop, and everything stays on your machine.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install tracepanel
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Tracepanel is on only in development: when `DEBUG` is on, in a debug server,
|
|
17
|
+
or under `tracepanel run`. Add it to your app:
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
# Django: settings.py
|
|
21
|
+
INSTALLED_APPS += ["tracepanel.django"]
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
# FastAPI
|
|
26
|
+
from fastapi import FastAPI
|
|
27
|
+
|
|
28
|
+
import tracepanel.fastapi
|
|
29
|
+
|
|
30
|
+
app = FastAPI(debug=True)
|
|
31
|
+
tracepanel.fastapi.install(app)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
# Flask, started with `flask run --debug`
|
|
36
|
+
from flask import Flask
|
|
37
|
+
|
|
38
|
+
import tracepanel.flask
|
|
39
|
+
|
|
40
|
+
app = Flask(__name__)
|
|
41
|
+
tracepanel.flask.install(app)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Open a page of your app and click the badge in the corner. On a page that
|
|
45
|
+
loads each post's author in its own query, the panel shows
|
|
46
|
+
`SELECT auth_user ×50` marked N+1, the line of code that ran it, and the
|
|
47
|
+
`select_related()` that turns 50 queries into one. The console at
|
|
48
|
+
`/__tracepanel__/` holds every request, task and command the app ran.
|
|
49
|
+
|
|
50
|
+
Tracepanel also traces Celery, Dramatiq, RQ, SQLAlchemy, Strawberry, Django REST
|
|
51
|
+
framework, Django Ninja, Channels and htmx when they are installed. It reads the
|
|
52
|
+
log lines you write with `logging` and attaches each one to the request, task
|
|
53
|
+
or command that wrote it. Your logging configuration stays as it is.
|
|
54
|
+
|
|
55
|
+
For Django, `pip install django-tracepanel` installs the same package.
|
|
56
|
+
|
|
57
|
+
## Run without changing your code
|
|
58
|
+
|
|
59
|
+
`tracepanel run` turns Tracepanel on for one command. The project stays as it
|
|
60
|
+
is:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
tracepanel run python manage.py runserver # Django
|
|
64
|
+
tracepanel run uvicorn app.main:app # FastAPI, Starlette
|
|
65
|
+
tracepanel run flask --app app run # Flask
|
|
66
|
+
tracepanel run gunicorn app:app # Falcon
|
|
67
|
+
tracepanel run celery -A app worker # a worker
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Configure
|
|
71
|
+
|
|
72
|
+
Every setting has a default. To change one, set it in one of these places, and
|
|
73
|
+
a later place wins:
|
|
74
|
+
|
|
75
|
+
1. `tracepanel.toml`, or `[tool.tracepanel]` in `pyproject.toml`
|
|
76
|
+
2. the `TRACEPANEL` dict in Django's settings
|
|
77
|
+
3. a `TRACEPANEL_<NAME>` variable, such as `TRACEPANEL_SLOW_SQL_MS=50`
|
|
78
|
+
|
|
79
|
+
```toml
|
|
80
|
+
# pyproject.toml
|
|
81
|
+
[tool.tracepanel]
|
|
82
|
+
slow_sql_ms = 50
|
|
83
|
+
n_plus_one_threshold = 5
|
|
84
|
+
ignore_paths = ["/health*", "/static/*"]
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`tracepanel config` prints each setting, its value and where it comes from.
|
|
88
|
+
|
|
89
|
+
## Trace your own code
|
|
90
|
+
|
|
91
|
+
To add your own steps to a request or a task, or to trace a script or a
|
|
92
|
+
scheduled job:
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
import time
|
|
96
|
+
|
|
97
|
+
import tracepanel
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@tracepanel.traced
|
|
101
|
+
def rebuild_index() -> None:
|
|
102
|
+
with tracepanel.span("index.load", source="db") as span:
|
|
103
|
+
time.sleep(0.01)
|
|
104
|
+
span.set("rows", 120)
|
|
105
|
+
tracepanel.event("index rebuilt", rows=120)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
with tracepanel.trace("rebuild-index"):
|
|
109
|
+
tracepanel.set_attr("tenant", "acme", on_trace=True)
|
|
110
|
+
rebuild_index()
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`span()` adds a step to the current trace, and `@traced` does the same for a
|
|
114
|
+
function. `trace()` starts a new trace. Tracepanel stores keyword attributes
|
|
115
|
+
under `app.`, so `source` becomes `app.source`. SQL, logs and errors inside a
|
|
116
|
+
span belong to that span. Outside development these calls do nothing, so you
|
|
117
|
+
can leave them in.
|
|
118
|
+
|
|
119
|
+
## Command line
|
|
120
|
+
|
|
121
|
+
| Command | What it does |
|
|
122
|
+
| --- | --- |
|
|
123
|
+
| `tracepanel run <command>` | runs a command with Tracepanel on |
|
|
124
|
+
| `tracepanel dev` | runs the agent in the foreground, with the console on http://127.0.0.1:8765/ |
|
|
125
|
+
| `tracepanel config` | prints the settings in effect and where each comes from |
|
|
126
|
+
| `tracepanel clear` | deletes every stored trace, log, query, email and issue |
|
|
127
|
+
| `tracepanel mcp` | gives an AI coding agent your traces over MCP |
|
|
128
|
+
|
|
129
|
+
## Supported versions
|
|
130
|
+
|
|
131
|
+
| Python | Django |
|
|
132
|
+
| --- | --- |
|
|
133
|
+
| 3.11 | 5.2 |
|
|
134
|
+
| 3.12, 3.13, 3.14 | 5.2, 6.0, 6.1 |
|
|
135
|
+
| 3.15 | 6.1 |
|
|
136
|
+
|
|
137
|
+
## Links
|
|
138
|
+
|
|
139
|
+
- [tracepanel](https://github.com/tracepanel/tracepanel): what Tracepanel finds,
|
|
140
|
+
how it works, privacy, and the agent this package installs
|
|
141
|
+
- [CHANGELOG.md](CHANGELOG.md): what changed in each version
|
|
142
|
+
- [CONTRIBUTING.md](CONTRIBUTING.md): developing this package
|
|
143
|
+
|
|
144
|
+
## License
|
|
145
|
+
|
|
146
|
+
Licensed under the [MIT License](LICENSE).
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "tracepanel"
|
|
3
|
+
version = "0.0.1a1"
|
|
4
|
+
description = "Traces and logs for Python apps: every request, task and command with its SQL and log lines, N+1 queries found"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
requires-python = ">=3.11"
|
|
9
|
+
dependencies = ['tracepanel-agent==0.0.1a1; (sys_platform == "linux" and (platform_machine == "x86_64" or platform_machine == "aarch64" or platform_machine == "armv7l" or platform_machine == "armv6l" or platform_machine == "i686" or platform_machine == "ppc64le" or platform_machine == "s390x" or platform_machine == "riscv64")) or sys_platform == "darwin" or (sys_platform == "win32" and (platform_machine == "AMD64" or platform_machine == "ARM64" or platform_machine == "x86"))']
|
|
10
|
+
keywords = [
|
|
11
|
+
"debug toolbar",
|
|
12
|
+
"django",
|
|
13
|
+
"fastapi",
|
|
14
|
+
"flask",
|
|
15
|
+
"n+1",
|
|
16
|
+
"sql",
|
|
17
|
+
"tracing",
|
|
18
|
+
"profiling",
|
|
19
|
+
]
|
|
20
|
+
classifiers = [
|
|
21
|
+
"Development Status :: 3 - Alpha",
|
|
22
|
+
"Environment :: Web Environment",
|
|
23
|
+
"Intended Audience :: Developers",
|
|
24
|
+
"Framework :: FastAPI",
|
|
25
|
+
"Framework :: Flask",
|
|
26
|
+
"Framework :: Django",
|
|
27
|
+
"Framework :: Django :: 5.2",
|
|
28
|
+
"Framework :: Django :: 6.0",
|
|
29
|
+
"Framework :: Django :: 6.1",
|
|
30
|
+
"Programming Language :: Python :: 3",
|
|
31
|
+
"Programming Language :: Python :: 3.11",
|
|
32
|
+
"Programming Language :: Python :: 3.12",
|
|
33
|
+
"Programming Language :: Python :: 3.13",
|
|
34
|
+
"Programming Language :: Python :: 3.14",
|
|
35
|
+
"Programming Language :: Python :: 3.15",
|
|
36
|
+
"Topic :: Software Development :: Debuggers",
|
|
37
|
+
"Typing :: Typed",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
[project.optional-dependencies]
|
|
41
|
+
django = ["django>=5.2"]
|
|
42
|
+
celery = ["celery>=5.3"]
|
|
43
|
+
dramatiq = ["dramatiq>=1.17"]
|
|
44
|
+
rq = ["rq>=2.0"]
|
|
45
|
+
channels = ["channels>=4.2"]
|
|
46
|
+
strawberry = ["strawberry-graphql>=0.250"]
|
|
47
|
+
fastapi = ["fastapi>=0.142"]
|
|
48
|
+
starlette = ["starlette>=0.40"]
|
|
49
|
+
flask = ["flask>=3.0"]
|
|
50
|
+
falcon = ["falcon>=4.0"]
|
|
51
|
+
sqlalchemy = ["sqlalchemy>=2.0"]
|
|
52
|
+
ninja = ["django-ninja>=1.4"]
|
|
53
|
+
drf = ["djangorestframework>=3.15"]
|
|
54
|
+
otel = ["opentelemetry-sdk>=1.45"]
|
|
55
|
+
|
|
56
|
+
[project.scripts]
|
|
57
|
+
tracepanel = "tracepanel._cli:main"
|
|
58
|
+
|
|
59
|
+
[project.urls]
|
|
60
|
+
Homepage = "https://github.com/tracepanel/tracepanel"
|
|
61
|
+
Repository = "https://github.com/tracepanel/tracepanel-python"
|
|
62
|
+
Issues = "https://github.com/tracepanel/tracepanel-python/issues"
|
|
63
|
+
Changelog = "https://github.com/tracepanel/tracepanel-python/blob/main/CHANGELOG.md"
|
|
64
|
+
|
|
65
|
+
[dependency-groups]
|
|
66
|
+
celery = ["celery>=5.6.3"]
|
|
67
|
+
channels = [
|
|
68
|
+
"channels>=4.3.2",
|
|
69
|
+
"daphne>=4.2.3",
|
|
70
|
+
]
|
|
71
|
+
dev = [
|
|
72
|
+
"django>=5.2",
|
|
73
|
+
"pytest",
|
|
74
|
+
"pytest-django",
|
|
75
|
+
"jsonschema",
|
|
76
|
+
"types-jsonschema",
|
|
77
|
+
"ruff>=0.16",
|
|
78
|
+
"pyrefly>=1.3",
|
|
79
|
+
"poethepoet>=0.48",
|
|
80
|
+
"django-stubs>=5.2",
|
|
81
|
+
"dirty-equals>=0.11",
|
|
82
|
+
"pyyaml>=6.0.3",
|
|
83
|
+
"types-pyyaml>=6.0.12.20260906",
|
|
84
|
+
"celery-types>=0.26.0",
|
|
85
|
+
"import-linter>=2.15",
|
|
86
|
+
]
|
|
87
|
+
django-tasks = [
|
|
88
|
+
"django-tasks>=0.12.0",
|
|
89
|
+
"django-tasks-db>=0.13.0",
|
|
90
|
+
]
|
|
91
|
+
postgres = ["psycopg[binary]>=3.2"]
|
|
92
|
+
strawberry = ["strawberry-graphql>=0.332.0"]
|
|
93
|
+
dramatiq = [
|
|
94
|
+
"dramatiq>=2.2.1",
|
|
95
|
+
"redis>=8.1.0",
|
|
96
|
+
]
|
|
97
|
+
rq = [
|
|
98
|
+
"rq>=2.12.0",
|
|
99
|
+
"fakeredis>=2.39.0",
|
|
100
|
+
]
|
|
101
|
+
fastapi = [
|
|
102
|
+
"fastapi>=0.142.4",
|
|
103
|
+
"httpx2>=2.13.1",
|
|
104
|
+
"sqlalchemy>=2.1.4",
|
|
105
|
+
]
|
|
106
|
+
ninja = ["django-ninja>=1.7.1"]
|
|
107
|
+
drf = ["djangorestframework>=3.18.3"]
|
|
108
|
+
otel = ["opentelemetry-sdk>=1.45.1"]
|
|
109
|
+
flask = [
|
|
110
|
+
"flask>=3.1.3",
|
|
111
|
+
"flask-sqlalchemy>=3.1.1",
|
|
112
|
+
"sqlalchemy>=2.1.4",
|
|
113
|
+
]
|
|
114
|
+
falcon = ["falcon>=4.2"]
|
|
115
|
+
|
|
116
|
+
[build-system]
|
|
117
|
+
requires = ["uv_build>=0.12,<0.13"]
|
|
118
|
+
build-backend = "uv_build"
|
|
119
|
+
|
|
120
|
+
[tool.uv.sources.tracepanel-agent]
|
|
121
|
+
path = "../tracepanel"
|
|
122
|
+
|
|
123
|
+
[tool.uv.build-backend]
|
|
124
|
+
module-name = "tracepanel"
|
|
125
|
+
module-root = ""
|
|
126
|
+
|
|
127
|
+
[tool.ruff]
|
|
128
|
+
line-length = 120
|
|
129
|
+
target-version = "py311"
|
|
130
|
+
|
|
131
|
+
[tool.ruff.lint]
|
|
132
|
+
select = ["ALL"]
|
|
133
|
+
ignore = [
|
|
134
|
+
"COM812",
|
|
135
|
+
"CPY",
|
|
136
|
+
"D1",
|
|
137
|
+
]
|
|
138
|
+
|
|
139
|
+
[tool.ruff.lint.flake8-tidy-imports]
|
|
140
|
+
ban-relative-imports = "all"
|
|
141
|
+
|
|
142
|
+
[tool.ruff.lint.isort]
|
|
143
|
+
known-third-party = [
|
|
144
|
+
"celery",
|
|
145
|
+
"channels",
|
|
146
|
+
"strawberry",
|
|
147
|
+
"ninja",
|
|
148
|
+
"rest_framework",
|
|
149
|
+
"django",
|
|
150
|
+
"fastapi",
|
|
151
|
+
"flask",
|
|
152
|
+
"falcon",
|
|
153
|
+
"starlette",
|
|
154
|
+
"sqlalchemy",
|
|
155
|
+
"opentelemetry",
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
[tool.ruff.lint.pydocstyle]
|
|
159
|
+
convention = "google"
|
|
160
|
+
|
|
161
|
+
[tool.ruff.lint.per-file-ignores]
|
|
162
|
+
"tests/**" = [
|
|
163
|
+
"S101",
|
|
164
|
+
"PLR2004",
|
|
165
|
+
"ARG001",
|
|
166
|
+
"ARG002",
|
|
167
|
+
"S105",
|
|
168
|
+
"S106",
|
|
169
|
+
]
|
|
170
|
+
"**/migrations/**" = ["RUF012"]
|
|
171
|
+
"tracepanel/_cli.py" = ["T201"]
|
|
172
|
+
"tracepanel/_bootstrap/sitecustomize.py" = ["INP001"]
|
|
173
|
+
"scripts/*.py" = ["INP001"]
|
|
174
|
+
"scripts/bench.py" = [
|
|
175
|
+
"INP001",
|
|
176
|
+
"T201",
|
|
177
|
+
]
|
|
178
|
+
"scripts/test_matrix.py" = [
|
|
179
|
+
"INP001",
|
|
180
|
+
"T201",
|
|
181
|
+
]
|
|
182
|
+
|
|
183
|
+
[tool.poe.tasks.build]
|
|
184
|
+
help = "Build the wheel and sdist into dist/, and check what they hold"
|
|
185
|
+
sequence = [
|
|
186
|
+
"uv build",
|
|
187
|
+
"python scripts/check_dist.py",
|
|
188
|
+
]
|
|
189
|
+
default_item_type = "cmd"
|
|
190
|
+
|
|
191
|
+
[tool.poe.tasks.test-matrix]
|
|
192
|
+
help = "Tests on every supported Python x Django pair, as CI runs them (scripts/test_matrix.py)"
|
|
193
|
+
cmd = "python scripts/test_matrix.py"
|
|
194
|
+
|
|
195
|
+
[tool.poe.tasks.bench]
|
|
196
|
+
help = "Overhead per request, memory over many requests (scripts/bench.py)"
|
|
197
|
+
cmd = "python scripts/bench.py"
|
|
198
|
+
|
|
199
|
+
[tool.poe.tasks.test]
|
|
200
|
+
help = "Run the test suite"
|
|
201
|
+
cmd = "pytest"
|
|
202
|
+
|
|
203
|
+
[tool.poe.tasks.fmt]
|
|
204
|
+
help = "Fix lint issues and format code"
|
|
205
|
+
sequence = [
|
|
206
|
+
"ruff check --fix .",
|
|
207
|
+
"ruff format .",
|
|
208
|
+
]
|
|
209
|
+
default_item_type = "cmd"
|
|
210
|
+
|
|
211
|
+
[tool.poe.tasks.lint]
|
|
212
|
+
help = "Check linting, formatting, types, imports and the docs (runs every check, fails if any fails)"
|
|
213
|
+
sequence = [
|
|
214
|
+
"ruff check .",
|
|
215
|
+
"ruff format --check .",
|
|
216
|
+
"pyrefly check",
|
|
217
|
+
"lint-imports",
|
|
218
|
+
"python scripts/lint_docs.py",
|
|
219
|
+
]
|
|
220
|
+
default_item_type = "cmd"
|
|
221
|
+
ignore_fail = "return_non_zero"
|
|
222
|
+
|
|
223
|
+
[tool.pytest.ini_options]
|
|
224
|
+
DJANGO_SETTINGS_MODULE = "tests.project.settings"
|
|
225
|
+
pythonpath = ["."]
|
|
226
|
+
django_debug_mode = true
|
|
227
|
+
|
|
228
|
+
[tool.pyrefly]
|
|
229
|
+
preset = "strict"
|
|
230
|
+
python-version = "3.11"
|
|
231
|
+
project-includes = [
|
|
232
|
+
"tracepanel",
|
|
233
|
+
"tests",
|
|
234
|
+
"scripts",
|
|
235
|
+
]
|
|
236
|
+
|
|
237
|
+
[tool.importlinter]
|
|
238
|
+
root_package = "tracepanel"
|
|
239
|
+
include_external_packages = true
|
|
240
|
+
|
|
241
|
+
[[tool.importlinter.contracts]]
|
|
242
|
+
name = "Only the framework integrations (tracepanel.django, .fastapi, .flask, .falcon) import their framework"
|
|
243
|
+
type = "forbidden"
|
|
244
|
+
source_modules = [
|
|
245
|
+
"tracepanel.core",
|
|
246
|
+
"tracepanel.storage",
|
|
247
|
+
"tracepanel.console",
|
|
248
|
+
"tracepanel.integrations",
|
|
249
|
+
"tracepanel.api",
|
|
250
|
+
"tracepanel.asgi",
|
|
251
|
+
"tracepanel.wsgi",
|
|
252
|
+
]
|
|
253
|
+
forbidden_modules = [
|
|
254
|
+
"django",
|
|
255
|
+
"channels",
|
|
256
|
+
"fastapi",
|
|
257
|
+
"starlette",
|
|
258
|
+
"flask",
|
|
259
|
+
"werkzeug",
|
|
260
|
+
"falcon",
|
|
261
|
+
]
|
|
262
|
+
|
|
263
|
+
[[tool.importlinter.contracts]]
|
|
264
|
+
name = "Layers: the framework integration on top, the core at the bottom"
|
|
265
|
+
type = "layers"
|
|
266
|
+
layers = [
|
|
267
|
+
"tracepanel.django | tracepanel.fastapi | tracepanel.flask | tracepanel.falcon",
|
|
268
|
+
"tracepanel.asgi | tracepanel.wsgi",
|
|
269
|
+
"tracepanel.integrations | tracepanel.console | tracepanel.api",
|
|
270
|
+
"tracepanel.storage",
|
|
271
|
+
"tracepanel.core",
|
|
272
|
+
]
|