🦀 Logging

September 25, 2024 by CryptoPatrick Engineering Rust

Introduction

Logging in software engineering is very important to get a sense of what’s going on when the code is executing. There are many ways to log - from the simple print to stdout, to more sophisticated ways using crates. Let’s have a look at two crates which help us do logging in Rust.

Crate 01: log

log is a logging facade that simply provides the API. The actual logging implementation has to be added using another crate - of which we can find a list on the website of log:

use log;

pub fn shave_the_yak(yak: &mut Yak) {
    log::trace!("Commencing yak shaving");

    loop {
        match find_a_razor() {
            Ok(razor) => {
                log::info!("Razor located: {}", razor);
                yak.shave(razor);
                break;
            }
            Err(err) => {
                log::warn!("Unable to locate a razor: {}, retrying", err);
            }
        }
    }
}

Crate 02: tracing

tracing is a crate that calls itself “a framework for instrumenting Rust programs to collect structured, event-based diagnostic information”. It requires its logger counterpart tracing-subscriber to be used, or a custom type that implements the tracing::Subscriber function. Developed by the Tokio team, it’s fully built from the ground up for async, which makes it perfect for web applications with Rust.

tracing uses the concept of “spans” which are used to record the flow of execution through a program. Events can happen inside or outside of a span and can also be used similarly to unstructured logging (i.e., just recording the event any which way) but can also represent a point in time within a span. See below:

use tracing::Level;

// records an event outside of any span context:
tracing::event!(Level::DEBUG, "something happened");

// create the span while entering it
let span = tracing::span!(Level::INFO, "my_span").entered();

// records an event within "my_span".
tracing::event!(Level::DEBUG, "something happened inside my_span");

Spans can form a tree structure, and the entire subtree is represented by its children - therefore, a parent span will always last as long as its longest-lived child span, if not longer.

Because all of this can be a bit excessive, tracing has also included the regular macros that would be in other log facade libraries for logging - namely, info!, error!, debug!, warn! and trace!. There’s also a span version of each of these macros - but if you’re coming from log and want to try tracing out without getting lost in the complexity of trying to make sure everything is in a span, tracing’s got your back.

'I write to understand as much as to be understood.' —Elie Wiesel
(c) 2024 CryptoPatrick