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
use log::{ Log, Record, Metadata, SetLoggerError, LevelFilter }; use std::time::Instant; mod target; pub use target::{ Target, IgnoreList, stdout::StdoutTarget, file::FileTarget }; struct Logger { start_time: Instant, stdout_target: Option<StdoutTarget>, file_target: Option<FileTarget> } impl Logger { pub fn new(stdout_target: Option<StdoutTarget>, file_target: Option<FileTarget>) -> Self { Logger { start_time: Instant::now(), stdout_target, file_target } } fn create_timestamp(&self) -> String { let now = Instant::now(); let duration_since_start = now.duration_since(self.start_time); let minutes = duration_since_start.as_secs() / 60; let seconds = duration_since_start.as_secs() % 60; let millis = duration_since_start.subsec_millis(); format!("+{:0>3}:{:0>2}.{:0>4}", minutes, seconds, millis) } fn create_log_line(stamp: &str, record: &Record) -> String { format!("[{}][{}] ({}) {}", stamp, record.level(), record.target(), record.args()) } } impl Log for Logger { fn enabled(&self, metadata: &Metadata) -> bool { macro_rules! check_enabled { ($id: ident) => { match self.$id.as_ref() { None => false, Some(t) => metadata.level() <= t.level() } } } check_enabled!(stdout_target) || check_enabled!(file_target) } fn log(&self, record: &Record) { let stamp = self.create_timestamp(); let log_string = Logger::create_log_line(&stamp, record); macro_rules! check_log { ($id: ident) => { match self.$id.as_ref() { None => (), Some(t) => if !t.ignore(record) { match t.write(&log_string) { Err(e) => eprintln!("Error, could not write to target: {}", e), Ok(_) => () } } } } } check_log!(stdout_target); check_log!(file_target); } fn flush(&self) { macro_rules! check_flush { ($id: ident) => { match self.$id.as_ref() { None => (), Some(t) => match t.flush() { Err(e) => eprintln!("Error, could not flush target: {}", e), Ok(_) => () } } } } check_flush!(stdout_target); check_flush!(file_target); } } pub fn init(stdout: Option<StdoutTarget>, file: Option<FileTarget>) -> Result<(), SetLoggerError> { let max_level = { let mut max = LevelFilter::Off; if let Some(ref stdout) = stdout { if stdout.level() > max { max = stdout.level().to_level_filter(); } } if let Some(ref file) = file { if file.level() > max { max = file.level().to_level_filter(); } } max }; let logger = Box::new(Logger::new(stdout, file)); log::set_boxed_logger(logger)?; log::set_max_level(max_level); Ok(()) }