1 // Copyright 2016 Victor Brekenfeld
2 //
3 // Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4 // http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5 // http://opensource.org/licenses/MIT>, at your option. This file may not be
6 // copied, modified, or distributed except according to those terms.
7 
8 //! Module providing the TestLogger Implementation
9 
10 use super::logging::should_skip;
11 use crate::{Config, LevelPadding, SharedLogger};
12 use log::{set_boxed_logger, set_max_level, LevelFilter, Log, Metadata, Record, SetLoggerError};
13 
14 use std::thread;
15 
16 /// The TestLogger struct. Provides a very basic Logger implementation that may be captured by cargo.
17 pub struct TestLogger {
18     level: LevelFilter,
19     config: Config,
20 }
21 
22 impl TestLogger {
23     /// init function. Globally initializes the TestLogger as the one and only used log facility.
24     ///
25     /// Takes the desired `Level` and `Config` as arguments. They cannot be changed later on.
26     /// Fails if another Logger was already initialized.
27     ///
28     /// # Examples
29     /// ```
30     /// # extern crate simplelog;
31     /// # use simplelog::*;
32     /// # fn main() {
33     /// #[cfg(not(test))]
34     /// // another logger
35     /// # let _ = TestLogger::init(LevelFilter::Info, Config::default());
36     /// #[cfg(test)]
37     /// let _ = TestLogger::init(LevelFilter::Info, Config::default());
38     /// # }
39     /// ```
init(log_level: LevelFilter, config: Config) -> Result<(), SetLoggerError>40     pub fn init(log_level: LevelFilter, config: Config) -> Result<(), SetLoggerError> {
41         set_max_level(log_level);
42         set_boxed_logger(TestLogger::new(log_level, config))
43     }
44 
45     /// allows to create a new logger, that can be independently used, no matter what is globally set.
46     ///
47     /// no macros are provided for this case and you probably
48     /// dont want to use this function, but `init()`, if you dont want to build a `CombinedLogger`.
49     ///
50     /// Takes the desired `Level` and `Config` as arguments. They cannot be changed later on.
51     ///
52     /// # Examples
53     /// ```
54     /// # extern crate simplelog;
55     /// # use simplelog::*;
56     /// # fn main() {
57     /// #[cfg(not(test))]
58     /// // another logger
59     /// # let test_logger = TestLogger::new(LevelFilter::Info, Config::default());
60     /// #[cfg(test)]
61     /// let test_logger = TestLogger::new(LevelFilter::Info, Config::default());
62     /// # }
63     /// ```
new(log_level: LevelFilter, config: Config) -> Box<TestLogger>64     pub fn new(log_level: LevelFilter, config: Config) -> Box<TestLogger> {
65         Box::new(TestLogger {
66             level: log_level,
67             config,
68         })
69     }
70 }
71 
72 impl Log for TestLogger {
enabled(&self, metadata: &Metadata<'_>) -> bool73     fn enabled(&self, metadata: &Metadata<'_>) -> bool {
74         metadata.level() <= self.level
75     }
76 
log(&self, record: &Record<'_>)77     fn log(&self, record: &Record<'_>) {
78         if self.enabled(record.metadata()) {
79             let _ = log(&self.config, record);
80         }
81     }
82 
flush(&self)83     fn flush(&self) {}
84 }
85 
86 impl SharedLogger for TestLogger {
level(&self) -> LevelFilter87     fn level(&self) -> LevelFilter {
88         self.level
89     }
90 
config(&self) -> Option<&Config>91     fn config(&self) -> Option<&Config> {
92         Some(&self.config)
93     }
94 
as_log(self: Box<Self>) -> Box<dyn Log>95     fn as_log(self: Box<Self>) -> Box<dyn Log> {
96         Box::new(*self)
97     }
98 }
99 
100 #[inline(always)]
log(config: &Config, record: &Record<'_>)101 pub fn log(config: &Config, record: &Record<'_>) {
102     if should_skip(&config, &record) {
103         return;
104     }
105 
106     if config.time <= record.level() && config.time != LevelFilter::Off {
107         write_time(config);
108     }
109 
110     if config.level <= record.level() && config.level != LevelFilter::Off {
111         write_level(record, config);
112     }
113 
114     if config.thread < record.level() && config.thread != LevelFilter::Off {
115         write_thread_id();
116     }
117 
118     if config.target <= record.level() && config.target != LevelFilter::Off {
119         write_target(record);
120     }
121 
122     if config.location <= record.level() && config.location != LevelFilter::Off {
123         write_location(record);
124     }
125 
126     write_args(record);
127 }
128 
129 #[inline(always)]
write_time(config: &Config)130 pub fn write_time(config: &Config) {
131     let cur_time = if config.time_local {
132         chrono::Local::now().naive_local() + config.time_offset
133     } else {
134         chrono::Utc::now().naive_utc() + config.time_offset
135     };
136     print!("{} ", cur_time.format(&*config.time_format));
137 }
138 
139 #[inline(always)]
write_level(record: &Record<'_>, config: &Config)140 pub fn write_level(record: &Record<'_>, config: &Config) {
141     match config.level_padding {
142         LevelPadding::Left => print!("[{: >5}] ", record.level()),
143         LevelPadding::Right => print!("[{: <5}] ", record.level()),
144         LevelPadding::Off => print!("[{}] ", record.level()),
145     };
146 }
147 
148 #[inline(always)]
write_thread_id()149 pub fn write_thread_id() {
150     let id = format!("{:?}", thread::current().id());
151     let id = id.replace("ThreadId(", "");
152     let id = id.replace(")", "");
153     print!("({}) ", id);
154 }
155 
156 #[inline(always)]
write_target(record: &Record<'_>)157 pub fn write_target(record: &Record<'_>) {
158     print!("{}: ", record.target());
159 }
160 
161 #[inline(always)]
write_location(record: &Record<'_>)162 pub fn write_location(record: &Record<'_>) {
163     let file = record.file().unwrap_or("<unknown>");
164     if let Some(line) = record.line() {
165         print!("[{}:{}] ", file, line);
166     } else {
167         print!("[{}:<unknown>] ", file);
168     }
169 }
170 
171 #[inline(always)]
write_args(record: &Record<'_>)172 pub fn write_args(record: &Record<'_>) {
173     println!("{}", record.args());
174 }
175