Skip to main content

colorized/
colorized.rs

1//! Quickstart example for the `logging` crate (published as `pylogging`).
2//!
3//! Run it with:
4//!
5//! ```sh
6//! cargo run --example quickstart
7//! ```
8//!
9//! It demonstrates the common building blocks:
10//!
11//! - configuring the **root** logger once, so every logger inherits it;
12//! - a **pattern** with field placeholders, padding/alignment, and a custom
13//!   timestamp format;
14//! - a **transformer** that adds ANSI colors per level;
15//! - obtaining a **named** logger and emitting messages at each [`Level`].
16
17use logging::{Formatter, Level, Logger, Record, StreamHandler};
18
19/// Wraps `line` in an ANSI color escape based on the record's level.
20///
21/// Pulled out of `main` to keep the wiring readable and to show that a
22/// transformer is just a plain function `(&Record, &String) -> String`.
23fn colorize(record: &Record, line: &String) -> String {
24    // `\x1b[<code>m` sets a color; `\x1b[0m` resets it.
25    let code = match record.get("level").copied() {
26        Some("DEBUG") => "90",         // bright black / gray
27        Some("INFO") => "34",          // blue
28        Some("WARNING") => "33",       // yellow
29        Some("ERROR") => "31",         // red
30        Some("CRITICAL") => "38;5;88", // deep red (256-color palette)
31        _ => return line.clone(),      // unknown level: leave uncolored
32    };
33    format!("\x1b[{code}m{line}\x1b[0m")
34}
35
36fn main() {
37    // 1. Build a formatter from a pattern.
38    //
39    //    `%(name)` is a field placeholder. A trailing spec like `-20` controls
40    //    width/alignment: a leading `-` left-aligns, the number is the minimum
41    //    width, and an optional `.N` truncates to N characters. So:
42    //      %(level)-8   -> left-aligned, padded to 8 ("INFO    ")
43    //      %(name).12   -> truncated to at most 12 chars
44    let mut formatter = Formatter::new("%(timestamp) [%(level)-8] %(name)-12 | %(message)");
45
46    // 2. Customize the timestamp rendering (chrono's strftime syntax).
47    formatter.set_time_format("%Y-%m-%d %H:%M:%S");
48
49    // 3. Post-process every rendered line (here: add color).
50    formatter.set_transformer(colorize);
51
52    // 4. Configure the ROOT logger once. Every logger created afterwards
53    //    inherits a snapshot of root's handlers and level, so you typically
54    //    set things up here and then just `Logger::get(...)` everywhere else.
55    let root = Logger::root();
56    root.add_handler(StreamHandler::new(formatter, std::io::stdout()))
57        .expect("root handler lock should not be poisoned at startup");
58    root.set_level(Level::Debug); // emit everything Debug and above
59
60    // 5. Grab a named logger and emit one message per level.
61    let logger = Logger::get("quickstart");
62    logger.debug("a debug message (gray)");
63    logger.info("an info message (blue)");
64    logger.warning("a warning message (yellow)");
65    logger.error("an error message (red)");
66    logger.critical("a critical message (deep red)");
67
68    // 6. Levels below a logger's threshold are dropped cheaply (before
69    //    formatting). Raise the bar and show that Debug/Info now vanish.
70    logger.set_level(Level::Warning);
71    logger.debug("you will NOT see this (below threshold)");
72    logger.info("you will NOT see this either");
73    logger.warning("but warnings still get through");
74}