use std::io::Write; use log::{Level, LevelFilter, Log, Metadata, Record}; /// Reads one level name from `RUST_LOG` -- `off`, `error`, `warn`, /// `info`, `debug`, `trace`, case-insensitively. **Not env_logger's /// per-module filter syntax**: anything else is ignored and the default /// stands, rather than being silently read as "off", since a typo that /// turned logging off would be indistinguishable from a quiet program. fn level_from_env(default: LevelFilter) -> LevelFilter { match std::env::var("RUST_LOG") { Ok(text) => text.trim().parse().unwrap_or(default), Err(_) => default, } } struct StderrLogger { level: LevelFilter, } impl Log for StderrLogger { fn enabled(&self, metadata: &Metadata) -> bool { metadata.level() <= self.level } fn log(&self, record: &Record) { if !self.enabled(record.metadata()) { return; } // One write, not a `writeln!` per part: two threads logging at // once interleave otherwise, and the frame and input traces are // both written from whichever thread produced them. let line = format!( "{level:<5} {target}: {args}\n", level = match record.level() { Level::Error => "ERROR", Level::Warn => "WARN", Level::Info => "INFO", Level::Debug => "DEBUG", Level::Trace => "TRACE", }, target = record.target(), args = record.args(), ); let _ = std::io::stderr().write_all(line.as_bytes()); } fn flush(&self) { let _ = std::io::stderr().flush(); } } pub fn install(default: LevelFilter) { let level = level_from_env(default); let logger = Box::leak(Box::new(StderrLogger { level })); if log::set_logger(logger).is_ok() { log::set_max_level(level); } }