🎓 A1Learner
🔍

Rust Tracing EnvFilter

An EnvFilter is a tracing filter that can match a crate, a span, or one field value. A field rule can follow one order through a whole program.

Read the variable, or fall back

try_from_default_env reads RUST_LOG. A broken filter comes back as an error, and the fallback keeps the program running:

let filter = EnvFilter::try_from_default_env()
    .unwrap_or_else(|_| EnvFilter::new("info"));

Turn one span up

Run the closing program with this filter. Outside the checkout span, only warn prints. Inside it, debug joins in:

$ RUST_LOG='warn,envfilter[checkout]=debug' cargo run

Match a field value

Add a field rule. Now only order 42 opens up. Order 7 stays silent, and so does the shop opened line:

$ RUST_LOG='warn,envfilter[checkout{order_id=42}]' cargo run

The anatomy of a directive

Every directive follows one shape. Leave the level off, and a match prints at every level:

target[span{field=value}]=level

Example

// Cargo.toml: tracing = "0.1" (resolved 0.1.44),
// tracing-subscriber = { version = "0.3", features = ["env-filter"] } (resolved 0.3.23)
use tracing::{debug, info, info_span, warn};

fn checkout(order_id: u32) {
    let span = info_span!("checkout", order_id);
    let _g = span.enter();
    debug!("scanning the items");
    info!("checkout done");
}

fn main() {
    let filter = tracing_subscriber::EnvFilter::try_from_default_env()
        .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info"));
    tracing_subscriber::fmt()
        .with_env_filter(filter)
        .with_target(false)
        .without_time()
        .with_ansi(false)
        .init();

    info!("shop opened");
    checkout(42);
    checkout(7);
    warn!("till is low on coins");
}

Key points

  • EnvFilter: the tracing twin of the log filter — directives in one string, read from the environment or built in code
  • A target rule names a crate or a module.
  • envfilter=debug turns this one crate up.
  • It leaves the rest at the global rule
  • A span rule in [ ] applies only inside that span: warn plus [checkout]=debug keeps the world quiet and one span loud
  • A field rule in { } matches one field value: [checkout{order_id=42}] follows a single order through the whole program
  • EnvFilter lives behind the env-filter feature of tracing-subscriber.
  • Ask for the feature in Cargo.toml

In this example, the output above is the quiet run. RUST_LOG is not set, so the fallback filter info applies, and both debug lines stay hidden. The program itself is small on purpose. checkout builds one span per order, and each span writes a debug line and an info line. The snippets above show two louder runs of this same binary. The first keeps the world at warn and opens the checkout span to debug. The second adds a field rule and opens only order 42. One honest limit: the filter is read once, at startup. A running program does not notice a changed variable until it restarts.

Note

env_logger rules match module paths only.

EnvFilter also matches spans and field values.

That reach is the biggest reason busy servers pick tracing.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

The filter is warn,shop[checkout{order_id=42}]. A debug! line fires inside order 7's checkout span. Does it print?