Expand description
A tracing-subscriber layer for beautifully printing spans and events to the
terminal.
This crate provides a ConsoleLayer that can be used with tracing to format
and display log messages in a hierarchical and easy-to-read format. It is
designed to make command line applications more pleasant by providing clear
visual cues for different log levels, tracking the duration of spans, and
indicating success or failure states.
§Features
- Pretty Printing: Spans and events are displayed with icons, colors, and indentation to create a clear visual hierarchy.
- Automatic
ResultHandling: Theconsole!macro automatically logs the outcome of aResult, indicating success or failure. - Span Timings: The layer automatically tracks and displays the execution time of each span.
- Error Propagation: Errors inside spans are propagated to parent spans, marking them as failed.
§API Documentation
§Usage
To use ConsoleLayer, add it to your tracing subscriber registry. The
console! macro can then be used to log events with different status levels.
§Example
Here is a complete example demonstrating a successful execution with nested
spans and #[tracing::instrument].
use tracing_scribe::console;
use tracing::info_span;
use tracing_subscriber::prelude::*;
use tracing_subscriber::registry::Registry;
#[tracing::instrument]
fn outer_task() {
inner_task();
}
#[tracing::instrument]
fn inner_task() {
console!(notice, "Generating unicorns");
std::thread::sleep(std::time::Duration::from_millis(100));
console!(info, "Unicorns generation completed");
}
let subscriber = Registry::default().with(tracing_scribe::ConsoleLayer::default());
tracing::subscriber::with_default(subscriber, || {
let _span = info_span!("main").entered();
outer_task();
});This will produce the following output:
╭─ ⚙ main
│ ╭─ ⚙ outer_task
│ │ ╭─ ⚙ inner_task
│ │ │ ├─ ! Generating unicorns
│ │ │ ├─ ✔ Unicorns generation completed
│ │ ╰─ ✔ inner_task ⏱ 100.2ms
│ ╰─ ✔ outer_task ⏱ 100.3ms
╰─ ✔ main succeeded ⏱ 100.3ms§Error Handling
The ConsoleLayer also handles errors gracefully. When a span instrumented
with #[tracing::instrument(err)] returns an error, the span and its parents
are marked as failed.
use tracing_scribe::console;
use tracing::info_span;
use tracing_subscriber::prelude::*;
use tracing_subscriber::registry::Registry;
use std::error::Error;
use std::fmt;
#[derive(Debug)]
struct MyError;
impl Error for MyError {}
impl fmt::Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Something went wrong")
}
}
#[tracing::instrument(err)]
fn failing_task() -> Result<(), MyError> {
console!(error, "This task is about to fail");
Err(MyError)
}
let subscriber = Registry::default().with(tracing_scribe::ConsoleLayer::default());
tracing::subscriber::with_default(subscriber, || {
let _span = info_span!("main").entered();
failing_task().ok();
});This will produce the following output:
╭─ ⚙ main
│ ╭─ ⚙ failing_task
│ │ ├─ ⨯ This task is about to fail
│ │ ├─ ⨯ Something went wrong
│ ╰─ ⨯ failing_task ⏱ 63.8µs
╰─ ⨯ main failed ⏱ 111.4µs§Asynchronous tasks
Asynchronous tasks are handled their own way by tracing. To make sure they end up with the right indentation, you might have to wrap them with a tracing instrumentation call.
use tracing_scribe::console;
use tracing::Instrument;
#[tracing::instrument]
async fn outer_task() {
tokio::spawn(
inner_task().instrument(tracing::Span::current())
);
}
#[tracing::instrument]
async fn inner_task() {
console!(notice, "Generating async unicorns");
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
console!(info, "Async unicorns generation completed");
}This will produce the following output:
╭─ ⚙ main
│ ╭─ ⚙ outer_task
│ │ ╭─ ⚙ inner_task
│ │ │ ├─ ! Generating async unicorns
│ │ │ ├─ ✔ Async unicorns generation completed
│ │ ╰─ ✔ inner_task ⏱ 100.2ms
│ ╰─ ✔ outer_task ⏱ 100.3ms
╰─ ✔ main succeeded ⏱ 100.3ms§Attaching Extra Information
The extra field can be used to attach additional context to a span, which
will be displayed alongside the span name.
use tracing_scribe::console;
use tracing::info_span;
use tracing_subscriber::prelude::*;
use tracing_subscriber::registry::Registry;
#[tracing::instrument(fields(extra = "some extra info"))]
fn task_with_extra() {
console!(info, "This task has extra information");
}
let subscriber = Registry::default().with(tracing_scribe::ConsoleLayer::default());
tracing::subscriber::with_default(subscriber, || {
let _span = info_span!("main").entered();
task_with_extra();
});This will produce the following output:
╭─ ⚙ main
│ ╭─ ⚙ task_with_extra some extra info
│ │ ├─ ✔ This task has extra information
│ ╰─ ✔ task_with_extra some extra info ⏱ 2.8µs
╰─ ✔ main succeeded ⏱ 23.3µs§Custom Status
The with_status method allows you to customize the icons and colors used
for the different statuses.
use tracing_scribe::{console, ConsoleLayer, StatusFactory, Status};
use tracing::info_span;
use tracing_subscriber::prelude::*;
use tracing_subscriber::registry::Registry;
use owo_colors::OwoColorize;
use std::io::{self, Write};
#[derive(Clone, Copy)]
pub struct CustomStatusFactory;
impl StatusFactory<CustomStatus> for CustomStatusFactory {
fn group(&self) -> CustomStatus {
CustomStatus::Group
}
fn success(&self) -> CustomStatus {
CustomStatus::Success
}
fn failure(&self) -> CustomStatus {
CustomStatus::Failure
}
}
#[derive(Debug, Clone, Copy)]
pub enum CustomStatus {
Success,
Failure,
Group,
Pass,
}
impl Status for CustomStatus {
fn from_str(s: &str) -> Self {
match s {
"success" => Self::Success,
_ => Self::Failure,
}
}
fn write_icon<W: Write>(&self, mut out: W, colors: &tracing_scribe::ColorScheme) -> io::Result<()> {
match self {
Self::Success => write!(out, "{}", colors.success.style("●")),
Self::Failure => write!(out, "{}", colors.error.style("●")),
Self::Group => write!(out, "{}", colors.group.style("●")),
Self::Pass => write!(out, ""),
}
}
fn is_passthrough(&self) -> bool {
matches!(self, Self::Pass)
}
}
let subscriber = Registry::default().with(
ConsoleLayer::default().with_status(CustomStatusFactory)
);
tracing::subscriber::with_default(subscriber, || {
let _span = info_span!("main").entered();
console!(warn, "This is a custom status");
});This will produce the following output:
╭─ ● main
│ ├─ ● This is a custom status
╰─ ● main succeededMacros§
- console
- Creates a tracing event with the required fields for our
ConsoleLayer.
Structs§
- Color
Scheme - A color scheme for customizing the appearance of console output.
- Console
Layer - A
tracing-subscriberlayer that formats and prints events to the console. - Default
Status Factory - The default status factory.
Enums§
- Default
Status - The default status implementation.
Traits§
- Status
- A trait that defines the behavior of a status.
- Status
Factory - A trait that is responsible for creating statuses.