How the `[wt-trace]` Logging Mechanism Works: Unified Tracing in Worktrunk
The [wt-trace] logging mechanism is Worktrunk’s unified tracing system that records subprocess commands and in‑process operations using RAII guards, outputting structured data to both human‑readable stderr and machine‑parseable JSON‑L files for performance analysis.
The [wt-trace] logging mechanism powers observability in the Worktrunk repository, capturing every significant event from git command execution to template rendering. This tracing system ensures comprehensive visibility into subprocess lifecycles and internal operations through a centralized emitter architecture implemented across src/trace/emit.rs, src/logging.rs, and src/trace/parse.rs.
Architecture of the [wt-trace] Tracing System
The system consists of three tightly coupled layers that guarantee every spawn path flows through a single guard, preventing untraced gaps in performance timelines.
The Emitter Layer
The emitter in src/trace/emit.rs serves as the centralized creation point for all trace records. It defines the CommandTrace and Span structures used throughout the codebase. This layer ensures that every external command spawn creates a consistent record containing fields such as cmd, argv, ts (timestamp), tid (thread ID), dur_us (duration in microseconds), and a final ok flag indicating success or failure.
The Formatter Layer
Located in src/logging.rs, the formatter transforms structured records into the textual [wt-trace] lines that appear on stderr and in trace.log. This layer also routes records to a JSON‑L stream (trace.jsonl) for programmatic consumption. The formatter operates as a layer within the tracing-subscriber stack, bridging legacy log::* calls into the unified trace pipeline.
The Parser and Consumer Layer
The parser in src/trace/parse.rs reads the JSON‑L output and reconstructs spans for analysis tools. This layer powers the wt-perf timeline subcommand, enabling developers to generate Chrome‑Trace‑compatible timelines or column‑aligned text views of execution performance.
Command Tracing with CommandTrace
External command execution follows a strict tracing protocol centered on the CommandTrace RAII guard. This design pattern ensures that timing measurements begin immediately before process spawning and conclude automatically when the guard drops.
Automatic Tracing via shell_exec::Cmd
The canonical method for running subprocesses uses the Cmd wrapper defined in src/shell_exec.rs. This wrapper automatically instantiates a CommandTrace guard before spawning the process and calls complete() or fail() on drop, eliminating manual instrumentation overhead.
use crate::shell_exec::Cmd;
fn list_worktrees(repo: &Repository) -> Result<()> {
// The `Cmd` wrapper creates a `CommandTrace` automatically.
Cmd::new("git")
.args(["worktree", "list"])
.current_dir(repo.path())
.context("listing worktrees")
.run()?; // On exit the guard logs a `[wt-trace]` record.
Ok(())
}
The resulting output appears in both stderr and trace.log:
[wt-trace] cmd=git argv=worktree list ts=2026-09-14T12:34:56.789Z dur_us=1450 ok=true
Manual CommandTrace Guards
For custom spawn sites that cannot use the Cmd wrapper, developers must construct a CommandTrace manually. The guard carries a #[must_use] annotation, triggering compile‑time or debug‑assertion failures if the trace is not properly completed.
use crate::trace::emit::CommandTrace;
fn spawn_custom() -> Result<()> {
let _trace = CommandTrace::start("my_tool", &["--do", "thing"]);
// … custom spawning logic …
// On success:
_trace.complete(Ok(()));
// On error:
// _trace.fail(anyhow!("failed"));
Ok(())
}
Critical implementation rule: According to the Worktrunk source code, the [wt-trace] command record has exactly one emitter. Any new spawn site running an in‑process command must construct a CommandTrace, starting it just before spawn and calling complete(success) after waiting or fail(err) on spawn/wait errors.
In-Process Span Tracing
For operations that do not involve subprocesses—such as template rendering or hook dispatch—the system uses a Span RAII guard. Dropping the guard emits a [wt-trace] span="name" dur_us=… line that the profiler converts into timeline entries.
use crate::trace::emit::Span;
fn render_template() {
let _span = Span::new("template_render");
// ... heavy rendering work ...
// When `_span` drops, a `[wt-trace] span="template_render" dur_us=…` line is emitted.
}
This mechanism allows the wt-perf tool to attribute latency to specific internal operations, not just external commands.
Output Formats and Structured Logging
The [wt-trace] mechanism produces dual-output streams simultaneously through the subscriber layer in src/logging.rs.
Human-Readable stderr Output
During execution, concise [wt-trace] lines stream to stderr, providing immediate visual feedback on command timing and success states. This output uses a compact key=value format optimized for terminal readability.
Machine-Readable JSON-L Stream
Simultaneously, the system writes to trace.jsonl (JSON Lines format) in the project directory. Each line contains a fully typed JSON object representing the trace record, enabling precise parsing without regex extraction. The src/trace/parse.rs module consumes this file to reconstruct execution timelines.
Profiling and Analysis Tools
The trace output feeds directly into Worktrunk’s performance tooling. The wt-perf timeline subcommand reads trace.jsonl and generates actionable insights.
# Capture a run with verbose tracing enabled
wt -vv list > /dev/null 2> trace.log
# Generate a human-readable timeline
wt-perf timeline --trace trace.log
This workflow parses the structured JSON, sorts spans by duration, and identifies bottlenecks such as slow git operations or redundant cache misses. The tool can also export Chrome‑Trace JSON for visualization in Perfetto.
Practical Implementation Examples
Example 1: Basic Git Command with Auto-Tracing
use crate::shell_exec::Cmd;
fn fetch_remote() -> Result<()> {
Cmd::new("git")
.args(["fetch", "--all"])
.run() // Automatically creates `[wt-trace]` record
}
Example 2: Manual Error Handling
use crate::trace::emit::CommandTrace;
fn risky_operation() -> Result<()> {
let trace = CommandTrace::start("legacy_tool", &["--batch"]);
match run_legacy_code() {
Ok(_) => {
trace.complete(Ok(()));
Ok(())
}
Err(e) => {
trace.fail(e);
Err(e)
}
}
}
Example 3: Template Rendering Span
use crate::trace::emit::Span;
fn generate_config() {
let _span = Span::new("config_generation");
// Expensive operations here are traced automatically
}
Summary
- Unified Emitter: The
CommandTraceandSpanguards insrc/trace/emit.rsguarantee that every subprocess and significant operation produces exactly one trace record. - RAII Safety: Both tracing guards use Rust’s drop semantics and
#[must_use]attributes to prevent untraced execution paths. - Dual Output: The formatter in
src/logging.rsproduces human‑readable stderr lines and machine‑parseabletrace.jsonlsimultaneously. - Performance Integration: The parser in
src/trace/parse.rsenableswt-perf timelineto reconstruct execution flows and identify bottlenecks. - Zero-Cost Abstraction: The
Cmdwrapper insrc/shell_exec.rsprovides automatic tracing without requiring manual instrumentation at every call site.
Frequently Asked Questions
What triggers a [wt-trace] log entry?
A [wt-trace] entry triggers when a CommandTrace or Span RAII guard is dropped at the end of a scope. For subprocesses, this occurs after the command completes or fails. For in‑process spans, this happens when the guarded block exits. The src/logging.rs formatter then writes the record to both stderr and trace.jsonl.
How does Worktrunk prevent untraced command executions?
Worktrunk enforces tracing through the #[must_use] annotation on the CommandTrace guard. Developers must either use the Cmd wrapper—which automatically manages the guard lifecycle—or manually construct a CommandTrace. Forgetting to handle the guard triggers a compile‑time warning or debug assertion, ensuring the rule that all spawn paths flow through the single emitter in src/trace/emit.rs.
Can I consume [wt-trace] output programmatically?
Yes. While stderr provides human‑readable output, the trace.jsonl file contains structured JSON objects with typed fields including cmd, argv, dur_us, and ok. The src/trace/parse.rs module provides the official parser, and the wt-perf timeline command demonstrates how to transform this data into performance reports or Chrome‑Trace visualizations.
What is the performance overhead of the [wt-trace] mechanism?
The tracing system uses RAII guards that record high‑resolution timestamps and write to asynchronous streams, introducing minimal overhead. The -vv flag enables high‑resolution tracing for debugging, while production builds can disable verbose logging levels. The JSON‑L appends are buffered, and the tracing-subscriber architecture ensures that disabled levels incur near‑zero cost through compile‑time filtering.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →