๐ŸŽ“ A1Learner
๐Ÿ”

Rust Observability in Production

Observability lets a program explain itself from the outside. In production, a log is data a machine reads, so production events need a shape for the machine reader.

Ask for JSON

The json feature swaps sentences for data. Each event becomes one line of JSON, as the closing program shows:

tracing_subscriber::fmt().json().with_target(false).init();

Fields stay fields

bill is not glued into the sentence here. In the JSON line it stays a number, and a machine can filter on it:

warn!(bill, "large bill: double-check the order");

One span per job

Every event inside the span carries it in its own JSON fields. A tool can group the whole shift with one search:

let _span = info_span!("shift", staff = 3).entered();

Quiet today, loud on the bad day

A debug line like this one costs almost nothing while its level is off. One restart with a louder filter brings it to life:

debug!(rows = rows.len(), "loaded the table");

Example

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

#[instrument]
fn serve(table: u32) -> u32 {
    info!("order taken");
    let bill = table * 10;
    if bill > 25 {
        warn!(bill, "large bill: double-check the order");
    }
    bill
}

fn main() {
    tracing_subscriber::fmt().json().with_target(false).init();

    let total = serve(4);
    info!(total, "table settled");
    let _span = info_span!("shift", staff = 3).entered();
    info!("shift started");
}

Key points

  • Observability: the program can explain itself from the outside.
  • Events, spans and fields are the raw material
  • Production logs are read by machines first.
  • JSON keeps each field a separate value.
  • So a tool can find events on its own.
  • It can find every event with bill over 25.
  • Nobody must read sentences at 3 a.m. for that.
  • A span gives each job an identity: one order, one request, one shift.
  • Its events stay together across modules and across awaits
  • Filters are the volume knob in production.
  • Restart the sick service with RUST_LOG=debug.
  • Leave the healthy ones quiet
  • Logs are one of three signals.
  • Metrics count things, like how many and how long.
  • Traces follow work across programs.
  • Logs tell the story โ€” this course covers the story

In this example, the same events from the earlier examples come out as JSON. One honest frame first. Each line starts with a timestamp. The real one shows the current time. So it differs on every run. Everything after it is exact. Read the second line closely. The message is one field. bill is its own field, with the number 40, not the text "40". A log tool can now ask for every event with a bill over 25, across every table and every day. The span fields do the grouping. The first two lines carry the serve span. The last line carries the shift span. The third line stands alone, because it fired outside any span. This is the whole bargain of observability. You pay a little structure at write time. You get questions you can answer at read time, on a machine you are not sitting at.

Note

None of this needs an agent or a runtime inside the language.

The facades are ordinary Rust macros, and the subscriber is ordinary Rust code that your program links.

Try it Yourself

Edit the code, then press Run.

Running is simulated ยท Rust Playground

Exercise

A server writes its events as JSON with fields. What can you do that plain sentences make hard?