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}