Skip to main content

Tracepanel

CI PyPI Python CodSpeed

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.

A 40-second demo: Swagger UI's Try it out on an endpoint, and the panel docked under it with the request's call tree, an N+1 with its query plan, a background task, its profile, and the same call after the fix

▶ Watch the 40-second demo

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:

A FastAPI app's Swagger UI with an endpoint run with Try it out and the start of its 200 response, and the panel docked under it with the same request's call tree and its three N+1 queries

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():

The same Swagger UI with the panel docked under it on that request's Issues tab: each N+1 with the line it comes from and the fix

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:

  1. tracepanel.toml, or [tool.tracepanel] in pyproject.toml
  2. the TRACEPANEL dict in Django's settings
  3. a TRACEPANEL_<NAME> variable, such as TRACEPANEL_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.

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)

Source distribution for tracepanel 0.9.0
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)

Table of built distributions (wheels) for tracepanel 0.9.0
File Interpreter ABI Platform Actions
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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page