Skip to main content

Crate trace_tally

Crate trace_tally 

Source
Expand description

§trace-tally

Crates.io Docs.rs

A tracing layer for rendering hierarchical task trees to the terminal.

Trace Tally Demo

§Usage

Using trace-tally requires implementing two traits to control how your data is processed and displayed:

  1. TraceMapper: Extracts the relevant data from spans and events.
  2. Renderer: Dictates exactly how that extracted data is formatted and printed to the terminal.

§Complete Example

use std::io::Write;
use trace_tally::*;

// Define how to display spans and events
struct MyRenderer;
impl Renderer for MyRenderer {
    type EventData = String;
    type TaskData = String;

    fn render_task_line(
        &mut self,
        f: &mut FrameWriter<'_>,
        task: &TaskView<'_, Self>,
    ) -> std::io::Result<()> {
        write!(f, "{}", " ".repeat(task.depth()))?;
        if task.completed() {
            write!(f, "✓ ")?;
        }
        writeln!(f, "{}", task.data())
    }

    fn render_event_line(
        &mut self,
        f: &mut FrameWriter<'_>,
        event: &EventView<'_, Self>,
    ) -> std::io::Result<()> {
        writeln!(f, "{}  -> {}", " ".repeat(event.depth()), event.data())
    }
}

// Define how to extract data from tracing primitives
struct MyMapper;
impl TraceMapper for MyMapper {
    type EventData = String;
    type TaskData = String;

    fn map_event(event: &tracing::Event<'_>) -> String {
        let mut message = String::new();
        event.record(&mut MessageVisitor(&mut message));
        message
    }
    fn map_span(attrs: &tracing::span::Attributes<'_>) -> String {
        attrs.metadata().name().to_string()
    }
}

struct MessageVisitor<'a>(&'a mut String);
impl<'a> tracing::field::Visit for MessageVisitor<'a> {
    fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
        if field.name() == "message" {
            *self.0 = format!("{:?}", value);
        }
    }
}

// Setup render loop
fn main() {
    use tracing::{info, info_span};
    use tracing_subscriber::layer::SubscriberExt;
    use tracing_subscriber::util::SubscriberInitExt;

    // Create tracing subscriber layer
    let layer = MyMapper::inline_layer(MyRenderer, std::io::stderr());

    // Setup tracing subscriber
    tracing_subscriber::registry().with(layer).init();

    // Traced work
    let span = info_span!("my_task");
    span.in_scope(|| {
        info!("working...");
        std::thread::sleep(std::time::Duration::from_millis(1000));
        info!("done");
    });
}

§Channel vs Inline

trace-tally provides two layer constructors:

  • inline_layer renders synchronously on every span/event. No background thread needed. Best for short-lived CLI tools where simplicity matters.
  • channel_layer sends actions over an mpsc channel to a separate render loop. This decouples tracing from rendering, enabling timed redraws and spinner animations.

The complete example above uses inline_layer. See examples/render_loop.rs for a channel_layer setup with animated output.

Both require that the TraceMapper associated types match the Renderer associated types (TaskData and EventData). A mismatch produces a compile error on the inline_layer / channel_layer call.

channel_layer accepts any ActionTransport implementation, not just std::sync::mpsc::Sender. Implement ActionTransport to use crossbeam, tokio, or other channel backends.

§Customizing Rendering

Override Renderer::render_task to change how the task tree is walked. The default renders the task line, then buffered events (skipped for completed tasks), then recurses into subtasks:

fn render_task(
    &mut self, f: &mut FrameWriter<'_>, task: &TaskView<'_, Self>,
) -> Result<(), std::io::Error> {
    self.render_task_line(f, task)?;
    if task.active() {
        for event in task.events().rev().take(3).rev() {
            self.render_event_line(f, &event)?;
        }
    }
    for subtask in task.subtasks() {
        self.render_task(f, &subtask)?;
    }
    Ok(())
}

§API

TypeRole
RendererTrait — define TaskData/EventData types and rendering callbacks.
TraceMapperTrait — extract custom data from tracing spans and events.
TaskRendererReceives Actions, manages the task tree state, and drives rendering.
FrameWriterTerminal writer with ANSI cursor control for frame clearing.
TaskView / EventViewRead-only views passed to renderer callbacks to access underlying data.
ActionEnum representing state changes: TaskStart, Event, TaskEnd, CancelAll.
ActionTransportTrait for channel backends — implemented for mpsc::Sender by default.

Re-exports§

pub use crate::prelude::*;

Modules§

prelude
Re-exports of all public types and traits.
widgets
Rendering utilities for task output.

Enums§

Action
A state change in the task tree.

Traits§

Renderer
Defines how tasks and events are rendered to the terminal.