Tracing your code
In Python, add your own steps, traces, attributes and events with span(), @traced, trace(), set_attr() and event().
The call tree shows what your framework and libraries do. To see the steps of your own code, wrap them in a span. To trace code that runs outside a request or a task, such as a script or a scheduled job, start a trace.
import tracepanel
def checkout(cart):
with tracepanel.span("price the cart", items=len(cart)):
total = price(cart)
with tracepanel.span("charge"):
charge(cart.owner, total)Tracepanel attaches the SQL, logs and errors inside a span to that span.
Safe to leave in
Outside a trace, and outside development, these calls do nothing. You can keep them in the code you deploy.
Attributes
Keyword arguments become attributes. Tracepanel stores a name without a dot
under app., so items becomes app.items. To choose the full name, such as
order.id, pass a dotted name in attrs:
import tracepanel
order_id = 42
with tracepanel.span("load the order", attrs={"order.id": order_id}):
...Tracepanel keeps strings, numbers, booleans, None and lists of them as they
are. It stores any other value as its repr().
span()
tracepanel.span(name: str, *, attrs: Mapping[str, object] | None = None, **values: object)span() is a context manager. It times the block as a step of the current
trace. It yields a handle with set(key, value), which adds an attribute while
the block runs. The handle's recording property is False when there is no
trace to record into.
import tracepanel
with tracepanel.span("index.load", source="db") as span:
rows = list(range(120))
span.set("rows", len(rows))@traced
tracepanel.traced(name: str | None = None)@traced runs the whole function in a span. By default it names the span
after the function's module and name, such as shop.pricing.price. Pass a
name to choose another one. It works on regular functions and on coroutines.
import tracepanel
@tracepanel.traced
def price(cart): ...
@tracepanel.traced("sync prices")
async def sync_prices(): ...trace()
tracepanel.trace(
name: str,
*,
kind: str = "command",
attrs: Mapping[str, object] | None = None,
**values: object,
)trace() starts a new trace in code that has none, such as a script, a
scheduled job or a consumer of your own. When the block ends, the agent
analyzes the trace and stores it, as it does for a request. An exception that
leaves the block becomes the trace's error, and Tracepanel raises it again.
import tracepanel
def run_import(): ...
with tracepanel.trace("nightly import", kind="command"):
run_import()kind says what the trace is: "command", "task" or "http". Any other
value makes a trace whose root is a plain step. A "command" or "task"
trace ends with a line in the terminal and a link to it.
When trace() starts a trace of its own, the handle's set() adds
attributes to the trace. Inside a trace that already exists, trace() adds a
span instead, like span().
set_attr()
tracepanel.set_attr(key: str, value: object, *, on_trace: bool = False)set_attr() adds an attribute to the current span. With on_trace=True, it
adds the attribute to the whole trace. The same app. rule applies to key.
import tracepanel
tracepanel.set_attr("order.id", 42)
tracepanel.set_attr("tenant", "acme", on_trace=True)event()
tracepanel.event(name: str, *, attrs: Mapping[str, object] | None = None, **values: object)event() marks a moment on the timeline. It records a step with no duration.
import tracepanel
tracepanel.event("cache warmed", keys=120)A full example
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()A script has no framework to turn Tracepanel on, so run it with
tracepanel run:
tracepanel run python rebuild.pyThis records one trace named rebuild-index. It holds the span of
rebuild_index, the index.load span inside it and the index rebuilt
event. In a script, @traced names the span after the __main__ module, so it
shows as __main__.rebuild_index. The script ends with a line in the terminal
and a link to the trace:
tracepanel ▸ command rebuild-index · 13 ms · 0 queries → http://127.0.0.1:52811/#/t/01M4…Under tracepanel run, the libraries your script uses are traced as in an
app. A query with sqlite3 or psycopg, an HTTP call with requests or a Redis
command shows inside the span that made it.