1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
extern crate log; extern crate term_painter; use std::io; use std::io::Write; use term_painter::{Painted, ToStyle}; use term_painter::Color::{NotSet, Yellow}; use log::{Log, LogLevel, LogLevelFilter, LogRecord, SetLoggerError, LogMetadata}; use std::ascii::AsciiExt; pub struct Logger { level: LogLevelFilter, } impl Logger { pub fn new() -> Logger { Logger { level: LogLevelFilter::Info, } } pub fn set_level(&mut self, level: LogLevel) -> &mut Self { self.level = level.to_log_level_filter(); self } fn format_level(&self, record: &LogRecord) -> Painted<String> { let (level_style, print_level) = match record.level() { LogLevel::Error => (Yellow.to_style(), true), LogLevel::Warn => (Yellow.to_style(), true), LogLevel::Debug => (Yellow.to_style(), true), LogLevel::Trace => (Yellow.dim(), true), _ => (NotSet.to_style(), cfg!(ndebug)), }; if print_level { let level_name = format!("{}", record.level()).to_ascii_lowercase(); let (head, tail) = level_name.split_at(1); let mut level_name2 = String::with_capacity(head.len() + tail.len()); level_name2.push_str(&head.to_ascii_uppercase()); level_name2.push_str(&tail.to_ascii_lowercase()); level_name2.push_str(": "); level_style.paint(level_name2) } else { NotSet.paint(String::new()) } } fn format_location(&self, record: &LogRecord) -> String { if cfg!(ndebug) { format!("{}:{} ", record.location().file(), record.location().line()) } else { String::new() } } pub fn enabled(&self, level: LogLevel, _target: &str) -> bool { level <= self.level } } impl Log for Logger { fn enabled(&self, metadata: &LogMetadata) -> bool { self.enabled(metadata.level(), metadata.target()) } fn log(&self, record: &LogRecord) { if !Log::enabled(self, record.metadata()) { return; } if record.level() <= LogLevel::Warn { let _ = writeln!(&mut io::stderr(), "{}{}{}", self.format_level(record), self.format_location(record), record.args()); } else { let _ = writeln!(&mut io::stdout(), "{}{}{}", self.format_level(record), self.format_location(record), record.args()); }; } } pub fn init(level: LogLevel) -> Result<(), SetLoggerError> { let mut logger = Box::new(Logger::new()); logger.set_level(level); log::set_logger(|max_level| { max_level.set(level.to_log_level_filter()); logger as Box<Log + 'static> }) } #[cfg(test)] mod tests { use log::{Log, LogLevel}; use super::{Logger}; #[test] fn log_level() { let mut logger = Logger::new(); assert!(logger.enabled(LogLevel::Error, "crate1")); assert!(logger.enabled(LogLevel::Info, "crate1")); assert!(!logger.enabled(LogLevel::Debug, "crate1")); assert!(!logger.enabled(LogLevel::Trace, "crate1")); logger.set_level(LogLevel::Debug); assert!(logger.enabled(LogLevel::Error, "crate1")); assert!(logger.enabled(LogLevel::Debug, "crate1")); assert!(!logger.enabled(LogLevel::Trace, "crate1")); logger.set_level(LogLevel::Trace); assert!(logger.enabled(LogLevel::Debug, "crate1")); assert!(logger.enabled(LogLevel::Trace, "crate1")); } }