Skip to main content

TraceAct

PyPI version Python versions License: MIT

X-ray vision for Python code.

TraceAct is a lightweight Python package for action-level tracing. It records the full story of what happens when a function runs — every step taken, resource touched, event recorded, and failure encountered — so you or your agent can understand what happened.

Install

pip install traceact

Quick start

from traceact import traced_action, configure, JsonlSink

configure(
    project="my-app",
    sinks=[JsonlSink("data/traces.jsonl")],
)

@traced_action(action="note.create", kind="app", actor="user")
def create_note(title, body):
    return {"note_id": "note_123"}

create_note("Hello", "World")

That call appends one JSON object to data/traces.jsonl the instant it finishes:

{
  "trace_id": "trc_ccc9be1639a8",
  "root_trace_id": "trc_ccc9be1639a8",
  "parent_trace_id": null,
  "project": "my-app",
  "action": "note.create",
  "kind": "app",
  "actor": "user",
  "status": "completed",
  "started_at": "2026-08-10T22:39:00.739Z",
  "ended_at": "2026-08-10T22:39:00.740Z",
  "duration_ms": 0.361,
  "steps": [],
  "events": [],
  "touches": [],
  "errors": []
}

Writes are immediate by default, so the viewer (next) shows traces as your app runs. Without any configure() at all, traces print to stdout instead of vanishing.

The viewer

TraceAct includes a local web viewer. No extra install — it ships with the package.

traceact view data/traces.jsonl

This starts a server at http://127.0.0.1:8765 and opens your browser. The viewer tails the file live: traces appear as your app writes them.

See it in the map

--map opens the browser straight onto the animated trace map for the newest trace, instead of the log. Save this as demo.py:

import time
from traceact import ActionTrace, configure, JsonlSink

configure(project="quickstart", sinks=[JsonlSink("demo_traces.jsonl")])

with ActionTrace.start(action="order.checkout", kind="app", actor="user") as trace:
    trace.step("Validated cart")
    trace.event(kind="db", operation="select", target="inventory")
    time.sleep(0.05)
    trace.step("Reserved stock")
    trace.event(kind="http", operation="POST", target="payments-api")
    time.sleep(0.05)
    trace.step("Charged card")
    trace.event(kind="db", operation="insert", target="orders")
    trace.output({"order_id": "ord_789"})

Then, one line:

python3 demo.py && traceact view demo_traces.jsonl --map

No account, no config file, no auth — the viewer has none by default. Full write-up (with what each part of the record means): USAGE.md's Quickstart.

Source types

What you pass What happens
A .jsonl file Tails that file live
A folder Merges all .jsonl files inside (e.g. per-process shards)
A SQLite database (SqliteSink output) Reads the traces table and tails new rows live — detected by file content, any extension works
Nothing Opens empty; use the in-app modal to add a source

CLI flags

traceact view [SOURCE] [--port N] [--host HOST] [--no-browser] [--new]
traceact show [SOURCE] ...   # identical alias of view
Flag Default Effect
--port N 8765 Port to serve on
--host HOST 127.0.0.1 Interface to bind (localhost only by default)
--no-browser off Start the server without opening a browser tab
--new off Force a fresh instance even if one is already running
--base-path PATH (none) Mount at a subpath for reverse-proxy deployments
--require-token off Require a random token on every API request — keeps other OS users on a shared machine out
--map off Open straight onto the trace map for SOURCE's newest trace, instead of the log

Port selection

The viewer auto-increments the port if the requested one is taken. If you ask for 8765 and it's in use, it tries 8766, 8767, and so on up to 20 times before giving up. Pass --port to start from a different base.

Single-instance behaviour

Running traceact view a second time reuses an already-running viewer rather than starting a second server. The new source (if given) is added to the running viewer and a browser tab is opened on it. This means you can call traceact view path/to/new-file.jsonl from multiple terminal tabs during a session and they all feed into one viewer.

Pass --new to bypass this and force a second independent instance.

macOS launcher

Double-click launch.command in the repo root to open TraceAct from Finder without a terminal. It checks for a running instance first, then creates a .venv/, installs or upgrades traceact, and opens the browser.

Adding sources in the app

The source modal (click the source name in the header) lets you:

  • Choose file / Choose folder — opens a native macOS picker; returns the filesystem path for live tailing
  • Drag and drop a .jsonl file — saved as a static snapshot in ~/.traceact/imports/
  • Type a path — collapsible fallback for pasting an absolute path

Health checks

traceact doctor [SOURCE]

Checks Python version, that ~/.traceact is writable, whether a viewer is already running, and (if SOURCE is given) that the file or folder parses as valid trace data. Useful for ruling out setup problems before debugging your own code. The same checks are also available from the viewer itself — Settings > Run diagnostics. See USAGE.md for full output and exit-code details.

Manual tracing

from traceact import ActionTrace

with ActionTrace.start(action="note.create", kind="app") as trace:
    trace.input({"title": "Hello"})
    trace.step("Validated input")
    trace.event(kind="db", operation="insert", target="notes")
    trace.output({"note_id": "note_123"})

Concepts

Concept Meaning
Trace The full record of one action (function call)
Step A human-readable timeline marker within a trace
Event A structured operation: db, http, file, model, job, etc.
Touch A resource involved in the trace (auto-derived from events)
Sink Where completed traces are written (JsonlSink, ConsoleSink, AsyncSink)

Design principle: observable by choice, never forced blind

TraceAct exists to give you X-ray vision for your code. That means nothing TraceAct does itself should take that vision away.

Wherever TraceAct might skip, drop, or truncate data, there's a signal for it. Events truncated by a budget limit set the budget_hit flag. Records dropped by AsyncSink under backpressure increment AsyncSink.dropped. A failure inside a sampled-out trace is recorded anyway (with always_trace_errors, on by default) and marked sampled_out so you know its detail wasn't captured. The one deliberate silence is a sampled-out success — that's what sampling is for — and it's opt-in through sample_rate.

The design choice is always: silent by default, observable by choice. You decide whether to log, alert on, or ignore those signals. TraceAct never makes that decision for you.

Tracing AI agents

One agent turn is a model call plus tool calls, and telling those apart is the point of tracing an agent. TraceAct ships a "tool" event kind, explicit parenting for callback-style frameworks, and a LangChain adapter:

from traceact.integrations.langchain import TraceActCallbackHandler

handler = TraceActCallbackHandler()
chain.invoke(inputs, config={"callbacks": [handler]})

Chains, model calls, tool runs, and retrievers each become traces with the right parent links and one shared correlation ID per run. Prompt text isn't recorded unless you opt in, and opted-in content still passes through redaction. The adapter imports langchain-core only when you import it — import traceact stays zero-dependency.

Captured values are guarded twice: field-name redaction (password, api_key, …) plus default-on content scanning that catches credential formats (AWS keys, sk- tokens, JWTs, PEM blocks) wherever they appear — even in a field named location or mid-sentence in free text. traceact doctor --scan runs the same registry over trace files already on disk.

For long-running work, opt-in in-flight streaming (TraceConfig(stream_progress=True)) shows a running row that fills in as the trace progresses — and a process that crashes mid-trace leaves its last snapshot on disk as evidence instead of losing the trace entirely.

Background jobs and queues

A queue boundary breaks ambient context: the worker runs in a different process with a fresh, empty context, so there's nothing for it to inherit. TraceAct sends the context across as job data instead — inject_context() on the producer, the reserved traceact_context kwarg on the worker:

from traceact import inject_context, traced_action

# Producer
export_report.delay(user_id=42, traceact_context=inject_context())

# Worker
@shared_task(name="export_report")
@traced_action(action="report.export", kind="job", actor="worker")
def export_report(user_id: int):
    ...

The decorator consumes the kwarg — your function never sees it — and links the job's trace to the enqueuing trace via upstream_trace_id and correlation_id. Works with Celery, RQ, or any queue that carries a dict. trace.queue() records the publish and consume events on either side.

Wiring into a web app

If your app has its own UI, add a backend route to launch or connect to the viewer, then call it from a button:

# FastAPI — launch_or_connect is blocking, so use run_in_executor
import asyncio
from traceact.viewer.instance import launch_or_connect

@router.get("/api/launch-viewer")
async def launch_viewer():
    loop = asyncio.get_event_loop()
    url = await loop.run_in_executor(None, launch_or_connect,
                                     "data/traces/traces.jsonl")
    return {"url": url}
// Frontend button
document.getElementById("btn-viewer").addEventListener("click", async () => {
    const btn = document.getElementById("btn-viewer");
    btn.disabled = true;
    try {
        const { url } = await fetch("/api/launch-viewer").then(r => r.json());
        window.open(url, "_blank", "noopener");
    } finally {
        btn.disabled = false;
    }
});

launch_or_connect checks for a running viewer first (via ~/.traceact/viewer.json + a health probe). If one is found, it adds your source to it and returns the URL immediately — no new process. If nothing is running, it spawns the viewer as a background subprocess and waits up to 3 s for it to be ready.

Requirements

Python 3.10+. No runtime dependencies.

Development

pip install -e ".[dev]"
pytest

Full reference

See ARCHITECTURE.md for the recording-pipeline and viewer diagrams with per-component contracts.

See USAGE.md for complete API documentation: all decorator and context manager parameters, helper methods (trace.db, trace.http, trace.file, trace.model, trace.tool, trace.queue), input capture, queue and background job tracing, parent/child traces, sinks, budget configuration, the trace record schema, test isolation, and the full viewer server API.

License

MIT


Built by Mo Shehu.

Release files for traceact 0.14.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for traceact 0.14.4
File Size Uploaded
traceact-0.14.4.tar.gz 284.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for traceact 0.14.4
File Interpreter ABI Platform
traceact-0.14.4-py3-none-any.whl Python 3 none any Details

Total release size: 477.6 kB

Release files / traceact-0.14.4.tar.gz

Download URL traceact-0.14.4.tar.gz
Size 284.1 kB
Tags Source
SHA-256 checksum
How to use checksums
ae7897baa3be9f73da0f5757bffcc8c17f23d2613d50fd708fbf9cacf4d08770
BLAKE2b-256 checksum
How to use checksums
9d5dd936ac08b04c5fd478331b5f9bfd432161e1bff9b81cc1f1bef3bb0b3070
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 11, 2026.

Transparency log

Release files / traceact-0.14.4-py3-none-any.whl

Download URL traceact-0.14.4-py3-none-any.whl
Size 193.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
20870b80361c3e5930081815ee7282cbe325f48c26366d562b47e176891fd368
BLAKE2b-256 checksum
How to use checksums
7412b450b3669f7d506163bfe99fec04aba1b5640deb52b22ceb089d19933d5f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 11, 2026.

Transparency log

Release history Release notifications | RSS feed

1.5.0

2 release files

1.4.0

2 release files

1.3.0

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.0

2 release files

This release

0.14.4 This release

2 release files

0.14.3

2 release files

0.14.2

2 release files

0.14.1

2 release files

0.12.0

2 release files

0.11.0

2 release files

0.10.0

2 release files

0.9.0

2 release files

0.8.1

2 release files

0.8.0

2 release files

0.6.2

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.1

2 release files

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