err_trail

Crates.ioerr_trail
lib.rserr_trail
version0.11.0
created_at2024-12-06 15:27:32.551055+00
updated_at2025-12-30 05:42:49.167158+00
descriptionAdd context to errors through logging
homepage
repositoryhttps://github.com/mcmah309/err_trail
max_upload_size
id1474380
size52,247
Henry (mcmah309)

documentation

https://docs.rs/err_trail

README

err_trail

github crates.io docs.rs test status

A generic logging interface for libraries and binaries. Libraries remain generic and binaries pick the logging implementation(s).

Current backends enabled by feature flags:

If no backend is selected by the binary, since all operations are inlined, they get compiled away during compilation. No overhead or downstream lock-in. Libraries can also easily enable logs for tests only.

Convenience methods are also added on Result and Option for ergonomic logging when an Err or None is encountered. No need to match or inspect. Similar to how context is handled in libraries like eros or anyhow while moving up the call stack, but for logging.

In Action

All methods and macros work with the generic backends. Like previously mentioned, if no backend is selected they are compiled away.

Macros

Familiar error!, warn!, info!, debug!, trace! macros exist to log in a way similar to the built in rust format! macro.

use err_trail::{error, warn, info, debug, trace};

fn main() {
    error!("An error occurred: {}", "disk full");
    warn!("This is a warning: {}", "high memory usage");
    info!("Some info: {}", "service started");
    debug!("Debugging value: {:?}", vec![1, 2, 3]);
    trace!("Trace log: {}", "function entered");
}

New Result and Option methods

New methods are added to Result and Option types - error, warn, info, debug, trace. These apply logs are various log levels

use err_trail::ErrContext;

fn main() {
    let value: Result<(), String> = result().error("If `Err`, this message is logged as error");
    let value: Result<(), String> = result().warn("If `Err`, this message is logged as warn");
    // Notice these methods can also accept closures for lazy evaluation
    let value: Result<(), String> = result().error(|err: &String| format!("If `Err`, this message is logged as error: {}", err));
    // If the error type implements `Display` then `()` can be passed to log the error directly if `Err`
    let value: Result<(), String> = result().error(());
}
fn result() -> Result<(), String> { Ok(()) }

The same methods exist for Option too.

Note: Due to some limitations of Rust's type inferencing on closures, for closures, usually the input type needs to be specified - e.g. : &String.

Guide

Opinionated guide on how to log if you are new to logging or would like a refresher:

flowchart TD
    Start{Is this log for debugging?}

    Start -->|Yes| SessionType
    Start -->|No| UnwantedState
    Start -->|I'm not sure| SessionType
    SessionType --> |Yes| Debug
    SessionType --> |No| Trace
    SessionType -->|I'm not sure| Trace
    UnwantedState -->|Yes| ProcessContinue
    UnwantedState -->|No| Info    
    ProcessContinue -->|Yes| Warning
    ProcessContinue -->|No| AppContinue 
    AppContinue -->|Yes| Error
    AppContinue -->|No| Fatal

    UnwantedState{Is the log the result of an unwanted state?}
    SessionType{"Is <b>any</b> true:<br/><br/>• This is temporary (I'm printf debugging now)<br/>• I am <b>reasonably certain</b> a developer will care about and won't get annoyed if hit during debugging"}
    ProcessContinue{Can the operation<br/>continue with<br/>unwanted state?}
    AppContinue{Can the<br/>process<br/>continue?}
    
    classDef normal fill:#ffffff,stroke:#374151,color:#000;
    classDef trace  fill:#e6d9ff,stroke:#7c3aed,color:#000;
    classDef debug  fill:#d1fae5,stroke:#059669,color:#000;
    classDef info   fill:#e0f2fe,stroke:#0284c7,color:#000;
    classDef warning fill:#fde68a,stroke:#d97706,color:#000;
    classDef error  fill:#f8b4b4,stroke:#dc2626,color:#000;
    classDef fatal  fill:#b91c1c,stroke:#7f1d1d,color:#fff;

    class Start,SessionType,UnwantedState,ProcessContinue,AppContinue normal;
    class Trace trace;
    class Debug debug;
    class Info info;
    class Warning warning;
    class Error error;
    class Fatal fatal;

    Trace["Trace<br/><br/>• Often verbose (e.g. large variable states or hit frequently)<br/>• Usually noise during most debug sessions"]
    Debug[Debug]
    Info["Info<br/><br/>• For System Operators<br/>• Human readable<br/>• Usually actionable (e.g. alerts, incidents, performance, health, stability)"]
    Warning["Warning<br/><br/>• Unwanted state/error encountered, but continuing the operation.<br/>• This is the last handler in an error chain and decided to continue the operation despite the error."]
    Error["Error<br/><br/>• Operation had to be aborted.<br/>• This is the last handler in an error chain and decided to abort the operation because of the error."]
    Fatal[Fatal<br/><br/>• Panic or abort]

Note: Returning the error to the calling is not considered a warning or an error - if anything, a trace.

no_std

This crate supports #![no_std].

Commit count: 0

cargo fmt