Tracepanel
See what your Python app is really doing. Tracepanel records each request, background task and command of your app with its SQL queries, Redis commands, outgoing HTTP calls, templates, logs and errors. It points out N+1 queries and calls, duplicates, slow SQL and slow HTTP calls, and names the line of code to fix. You read it in a panel on your pages and in a console.
It runs while you develop, and everything stays on your machine.
Works with
| Supported | |
|---|---|
| Python | 3.11 to 3.15. With Django: 5.2 on Python 3.11 to 3.14, 6.0 on 3.12 to 3.14, 6.1 on 3.12 to 3.15, each pair tested in CI. |
| Frameworks | Django 5.2, 6.0 and 6.1, FastAPI, Starlette, Flask, Falcon (WSGI and ASGI). Any other ASGI or WSGI app, such as Litestar, Quart, Bottle or Pyramid, at the request level. Under any ASGI or WSGI server, such as uvicorn or gunicorn, or the framework's own. In Django also signals, cache, email, file storages, collectstatic, migrate and management commands. |
| Templates | Django's and Jinja2. |
| Databases | SQLAlchemy 2 and the Django ORM, or a driver directly: sqlite3, aiosqlite, psycopg, psycopg2, asyncpg, PyMySQL, aiomysql, asyncmy, mysqlclient, MySQL Connector, oracledb, pyodbc, pymssql and Snowflake's connector (sync and asyncio). Opening a connection shows as its own span: a Snowflake login, a PostgreSQL or MySQL handshake. EXPLAIN on SQLite, PostgreSQL, MySQL and SQL Server. MongoDB through PyMongo. |
| APIs | Django REST framework, Django Ninja, Strawberry, Graphene (through graphene-django), htmx. |
| Background work | Celery, Dramatiq, RQ, Huey, Taskiq, FastStream. In Django also django-q2, Django Tasks and Channels. |
| Clients | requests and urllib3, httpx, aiohttp, Redis and Valkey, AWS through boto3 and aiobotocore, the OpenAI and Anthropic SDKs. Spans from OpenTelemetry instrumentations join the trace. |
| Logs | The lines you write with logging, each on the request, task or command that wrote it. |
| Profiling | Where a request's or a task's time goes, and its memory when you ask for it. |
| Tests | pytest --tracepanel fails a test that makes an N+1 or duplicate queries. |
The libraries' versions, the settings and what each integration records are in the Python docs.
Install
pip install tracepanel
An integration turns on by itself when its library is installed. An extra
installs the library with the lowest version Tracepanel supports, such as
pip install "tracepanel[fastapi,sqlalchemy]": django, fastapi,
starlette, flask, falcon, sqlalchemy, mongodb, drf, ninja,
strawberry, channels, celery, dramatiq, rq, huey, taskiq,
faststream, django-q2 and otel. For Django,
pip install django-tracepanel is the same as tracepanel[django].
Setup
Tracepanel is on only in development: when DEBUG is on, in a debug server,
or under tracepanel run. Add it to your app:
# Django: settings.py
INSTALLED_APPS += ["tracepanel.django"]
# FastAPI
from fastapi import FastAPI
import tracepanel.fastapi
app = FastAPI(debug=True)
tracepanel.fastapi.install(app)
# Flask, started with `flask run --debug`
from flask import Flask
import tracepanel.flask
app = Flask(__name__)
tracepanel.flask.install(app)
Open a page of your app and click the badge in the corner to open the panel.
The console at /__tracepanel__/ holds every request, task and command the
app ran.
What it looks like
Each request shows its call tree, with the SQL queries, the calls and the issues in it. Here a FastAPI and SQLAlchemy store lists its products from Swagger UI, and loads each product's category, brand and reviews in a query of its own:
The panel also lists the requests your pages and tools make with fetch(),
here Swagger UI's Try it out. Each issue names the line that ran it and the fix
in your library's terms, such as SQLAlchemy's joinedload() or Django's
select_related():
Run without changing your code
tracepanel run turns Tracepanel on for one command. The project stays as it
is:
tracepanel run python manage.py runserver # Django
tracepanel run uvicorn app.main:app # FastAPI, Starlette
tracepanel run flask --app app run # Flask
tracepanel run gunicorn app:app # Falcon (WSGI)
tracepanel run uvicorn app:app # Falcon (ASGI)
tracepanel run celery -A app worker # a worker
tracepanel run taskiq worker app:broker # a Taskiq worker
Configure
Every setting has a default. To change one, set it in one of these places, and a later place wins:
tracepanel.toml, or[tool.tracepanel]inpyproject.toml- the
TRACEPANELdict in Django's settings - a
TRACEPANEL_<NAME>variable, such asTRACEPANEL_SLOW_SQL_MS=50
# pyproject.toml
[tool.tracepanel]
slow_sql_ms = 50
n_plus_one_threshold = 5
ignore_paths = ["/health*", "/static/*"]
tracepanel config prints each setting, its value and where it comes from.
Profile a request
A profile shows where a request's time went in code, as a flame graph in the
console. Tracepanel profiles a request that sends an X-Tracepanel-Profile: 1
header, and every request while the panel's Profile button is on. The tasks it
queues and the calls it makes to your other services are profiled too.
profile = true profiles every trace, and profile_enabled = false turns
profiling off.
A profile counts memory too when asked: X-Tracepanel-Profile: memory, the
button's memory mode, or profile_memory = true. It slows down the whole
process while it runs, so leave it off when you measure time. The
Profile tab shows what a profile
holds.
Trace your own code
To add your own steps to a request or a task, or to trace a script or a scheduled job:
import time
import tracepanel
@tracepanel.traced
def rebuild_index() -> None:
with tracepanel.span("index.load", source="db") as span:
time.sleep(0.01)
span.set("rows", 120)
tracepanel.event("index rebuilt", rows=120)
with tracepanel.trace("rebuild-index"):
tracepanel.set_attr("tenant", "acme", on_trace=True)
rebuild_index()
span() adds a step to the current trace, and @traced does the same for a
function. trace() starts a new trace. Tracepanel stores keyword attributes
under app., so source becomes app.source. SQL, logs and errors inside a
span belong to that span. Outside development these calls do nothing, so you
can leave them in.
In tests
The pytest plugin turns what Tracepanel finds into test failures, so an N+1 query fails CI before it reaches production. It comes with the package and stays off until you turn it on:
pytest --tracepanel
# pyproject.toml
[tool.pytest.ini_options]
tracepanel = true
tracepanel_fail_on = ["n+1", "duplicates"]
Each test runs as a trace named after the test. A request the test makes
through Django's test client, Starlette's or FastAPI's TestClient, httpx's
ASGI transport or Flask's test client is a trace of its own, linked to the
test's in the console. Fixtures run outside the test's trace, so the test
database's setup doesn't count.
When the test passes, the agent looks at its traces. An issue of a kind in
tracepanel_fail_on (n+1 and duplicates by default) fails the test with
the issue, its query, the line of code that ran it and a link to the trace:
tracepanel found 1 issue in this test:
N+1: 5 similar queries · SELECT auth_group
SELECT "auth_group"."name" FROM "auth_group" ... WHERE "user_id" = %s
at blog/views.py:9 in users
in GET /users/ → http://127.0.0.1:52811/#/t/01M5VH6BFMFPA7X692DKQV2ANZ
Load the related rows in one extra query: add `prefetch_related()` ...
A mark changes the rules for a test, a class or a module. A fixture and a context manager count queries inside the test:
import pytest
import tracepanel
@pytest.mark.tracepanel(max_queries=10, allow=["n+1"])
def test_feed(client, tracepanel_trace):
client.get("/feed/")
assert tracepanel_trace.queries <= 10
with tracepanel.max_queries(2):
client.get("/feed/?page=2")
fail_on replaces the kinds that fail the test, allow takes kinds out of
them, and max_queries caps the queries of the test and its requests.
tracepanel_trace.issues lists what the agent found in the test's requests
so far.
The plugin starts the project's agent when none is running, as
tracepanel run does. Without an agent (agent = "off", or none for the
platform) the tests run without findings, and one warning says so. Query
counts need no agent. Under pytest-xdist every worker sends its tests to the
same agent, and async tests under anyio or pytest-asyncio work the same way.
Command line
| Command | What it does |
|---|---|
tracepanel run <command> |
runs a command with Tracepanel on |
tracepanel dev |
runs the agent in the foreground, with the console on http://127.0.0.1:8765/ |
tracepanel config |
prints the settings in effect and where each comes from |
tracepanel clear |
deletes every stored trace, log, query, email and issue |
tracepanel mcp |
gives an AI coding agent your traces over MCP |
Documentation
The documentation is at tracepanel.dev/docs/python: installing, the integrations, settings, tracing your own code and the command line. The panel, the console and the issues are the same in every framework, under tracepanel.dev/docs. Django has a guide of its own at tracepanel.dev/docs/django.
Links
- tracepanel: what Tracepanel finds, how it works, privacy, and the agent this package installs
- CHANGELOG.md: what changed in each version
- CONTRIBUTING.md: developing this package
License
Licensed under the MIT License. The agent it installs,
tracepanel-agent, is free to use under the
FSL-1.1-ALv2:
source available, and Apache-2.0 two years after each release.
Metadata
Release files for tracepanel 0.9.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| tracepanel-0.9.0.tar.gz | 285.9 kB | Without provenance | Details for tracepanel-0.9.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| tracepanel-0.9.0-py3-none-any.whl | Python 3 | none | any | Details for tracepanel-0.9.0-py3-none-any.whl |
Total release size: 618.2 kB
Release files / tracepanel-0.9.0.tar.gz
| Download URL | tracepanel-0.9.0.tar.gz |
|---|---|
| Size | 285.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
45ecd7ab4ba6ed6e8dcfc8d164c287cf07476b08d7539d2e64ae1e497f6729a3
|
|
BLAKE2b-256 checksum How to use checksums |
c52e6cfb3d21a38edad0f42bad1f3fa9a02dba720a6bd3e39e78d4e1e1c2df44
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / tracepanel-0.9.0-py3-none-any.whl
| Download URL | tracepanel-0.9.0-py3-none-any.whl |
|---|---|
| Size | 332.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
83f09dbfc32b0f3183d9f4bd835f2492c057ad2029f61de45308bc667cb3daf4
|
|
BLAKE2b-256 checksum How to use checksums |
39470656c9500605dd6ec758052f5be5c2d4640e5fdfc268f72a3e0a510a21bf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for tracepanel 0.9.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.