simplelog/
lib.rs

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//!
9//! `simplelog` provides a series of logging facilities, that can be easily combined.
10//!
11//! - `SimpleLogger` (very basic logger that logs to stdout)
12//! - `TermLogger` (advanced terminal logger, that splits to stdout/err and has color support) (can be excluded on unsupported platforms)
13//! - `WriteLogger` (logs to a given struct implementing `Write`, e.g. a file)
14//! - `CombinedLogger` (can be used to form combinations of the above loggers)
15//! - `TestLogger` (specialized logger for tests. Uses print!() / println!() for tests to be able to capture the output)
16//!
17//! Only one Logger should be initialized of the start of your program
18//! through the `Logger::init(...)` method. For the actual calling syntax
19//! take a look at the documentation of the specific implementation(s) you wanna use.
20//!
21
22#![deny(missing_docs, rust_2018_idioms)]
23
24mod config;
25mod loggers;
26
27pub use self::config::{
28    format_description, Config, ConfigBuilder, FormatItem, LevelPadding, TargetPadding,
29    ThreadLogMode, ThreadPadding,
30};
31#[cfg(feature = "test")]
32pub use self::loggers::TestLogger;
33pub use self::loggers::{CombinedLogger, SimpleLogger, WriteLogger};
34#[cfg(feature = "termcolor")]
35pub use self::loggers::{TermLogger, TerminalMode};
36#[cfg(feature = "termcolor")]
37pub use termcolor::{Color, ColorChoice};
38
39pub use log::{Level, LevelFilter};
40
41use log::Log;
42#[cfg(all(test, not(feature = "paris")))]
43use log::*;
44
45#[cfg(feature = "paris")]
46pub(crate) mod paris_macros;
47#[cfg(feature = "paris")]
48#[doc(hidden)]
49pub mod __private {
50    pub use log;
51    pub use paris;
52}
53
54/// Trait to have a common interface to obtain the Level of Loggers
55///
56/// Necessary for CombinedLogger to calculate
57/// the lowest used Level.
58///
59pub trait SharedLogger: Log {
60    /// Returns the set Level for this Logger
61    ///
62    /// # Examples
63    ///
64    /// ```
65    /// # extern crate simplelog;
66    /// # use simplelog::*;
67    /// # fn main() {
68    /// let logger = SimpleLogger::new(LevelFilter::Info, Config::default());
69    /// println!("{}", logger.level());
70    /// # }
71    /// ```
72    fn level(&self) -> LevelFilter;
73
74    /// Inspect the config of a running Logger
75    ///
76    /// An Option is returned, because some Logger may not contain a Config
77    ///
78    /// # Examples
79    ///
80    /// ```
81    /// # extern crate simplelog;
82    /// # use simplelog::*;
83    /// # fn main() {
84    /// let logger = SimpleLogger::new(LevelFilter::Info, Config::default());
85    /// println!("{:?}", logger.config());
86    /// # }
87    /// ```
88    fn config(&self) -> Option<&Config>;
89
90    /// Returns the logger as a Log trait object
91    fn as_log(self: Box<Self>) -> Box<dyn Log>;
92}
93
94#[cfg(test)]
95mod tests {
96    use std::fs::File;
97    use std::io::Read;
98
99    use super::*;
100
101    #[test]
102    fn test() {
103        let mut i = 0;
104
105        CombinedLogger::init({
106            let mut vec = Vec::new();
107            let mut conf_builder = ConfigBuilder::new();
108
109            let conf_thread_name = ConfigBuilder::new()
110                .set_time_level(LevelFilter::Off)
111                .set_thread_level(LevelFilter::Error)
112                .set_thread_mode(ThreadLogMode::Names)
113                .build();
114
115            vec.push(WriteLogger::new(
116                LevelFilter::Error,
117                conf_thread_name,
118                File::create("thread_naming.log").unwrap(),
119            ) as Box<dyn SharedLogger>);
120
121            for elem in vec![
122                LevelFilter::Off,
123                LevelFilter::Trace,
124                LevelFilter::Debug,
125                LevelFilter::Info,
126                LevelFilter::Warn,
127                LevelFilter::Error,
128            ] {
129                let conf = conf_builder
130                    .set_location_level(elem)
131                    .set_target_level(elem)
132                    .set_max_level(elem)
133                    .set_time_level(elem)
134                    .build();
135                i += 1;
136
137                //error
138                vec.push(
139                    SimpleLogger::new(LevelFilter::Error, conf.clone()) as Box<dyn SharedLogger>
140                );
141                #[cfg(feature = "termcolor")]
142                vec.push(TermLogger::new(
143                    LevelFilter::Error,
144                    conf.clone(),
145                    TerminalMode::Mixed,
146                    ColorChoice::Auto,
147                ) as Box<dyn SharedLogger>);
148                vec.push(WriteLogger::new(
149                    LevelFilter::Error,
150                    conf.clone(),
151                    File::create(&format!("error_{}.log", i)).unwrap(),
152                ) as Box<dyn SharedLogger>);
153                #[cfg(feature = "test")]
154                vec.push(TestLogger::new(LevelFilter::Error, conf.clone()));
155
156                //warn
157                vec.push(
158                    SimpleLogger::new(LevelFilter::Warn, conf.clone()) as Box<dyn SharedLogger>
159                );
160                #[cfg(feature = "termcolor")]
161                vec.push(TermLogger::new(
162                    LevelFilter::Warn,
163                    conf.clone(),
164                    TerminalMode::Mixed,
165                    ColorChoice::Auto,
166                ) as Box<dyn SharedLogger>);
167                vec.push(WriteLogger::new(
168                    LevelFilter::Warn,
169                    conf.clone(),
170                    File::create(&format!("warn_{}.log", i)).unwrap(),
171                ) as Box<dyn SharedLogger>);
172                #[cfg(feature = "test")]
173                vec.push(TestLogger::new(LevelFilter::Warn, conf.clone()));
174
175                //info
176                vec.push(
177                    SimpleLogger::new(LevelFilter::Info, conf.clone()) as Box<dyn SharedLogger>
178                );
179                #[cfg(feature = "termcolor")]
180                vec.push(TermLogger::new(
181                    LevelFilter::Info,
182                    conf.clone(),
183                    TerminalMode::Mixed,
184                    ColorChoice::Auto,
185                ) as Box<dyn SharedLogger>);
186                vec.push(WriteLogger::new(
187                    LevelFilter::Info,
188                    conf.clone(),
189                    File::create(&format!("info_{}.log", i)).unwrap(),
190                ) as Box<dyn SharedLogger>);
191                #[cfg(feature = "test")]
192                vec.push(TestLogger::new(LevelFilter::Info, conf.clone()));
193
194                //debug
195                vec.push(
196                    SimpleLogger::new(LevelFilter::Debug, conf.clone()) as Box<dyn SharedLogger>
197                );
198                #[cfg(feature = "termcolor")]
199                vec.push(TermLogger::new(
200                    LevelFilter::Debug,
201                    conf.clone(),
202                    TerminalMode::Mixed,
203                    ColorChoice::Auto,
204                ) as Box<dyn SharedLogger>);
205                vec.push(WriteLogger::new(
206                    LevelFilter::Debug,
207                    conf.clone(),
208                    File::create(&format!("debug_{}.log", i)).unwrap(),
209                ) as Box<dyn SharedLogger>);
210                #[cfg(feature = "test")]
211                vec.push(TestLogger::new(LevelFilter::Debug, conf.clone()));
212
213                //trace
214                vec.push(
215                    SimpleLogger::new(LevelFilter::Trace, conf.clone()) as Box<dyn SharedLogger>
216                );
217                #[cfg(feature = "termcolor")]
218                vec.push(TermLogger::new(
219                    LevelFilter::Trace,
220                    conf.clone(),
221                    TerminalMode::Mixed,
222                    ColorChoice::Auto,
223                ) as Box<dyn SharedLogger>);
224                vec.push(WriteLogger::new(
225                    LevelFilter::Trace,
226                    conf.clone(),
227                    File::create(&format!("trace_{}.log", i)).unwrap(),
228                ) as Box<dyn SharedLogger>);
229                #[cfg(feature = "test")]
230                vec.push(TestLogger::new(LevelFilter::Trace, conf.clone()));
231            }
232
233            vec
234        })
235        .unwrap();
236
237        error!("Test Error");
238        warn!("Test Warning");
239        info!("Test Information");
240        debug!("Test Debug");
241        trace!("Test Trace");
242
243        let mut thread_naming = String::new();
244        File::open("thread_naming.log")
245            .unwrap()
246            .read_to_string(&mut thread_naming)
247            .unwrap();
248
249        if let Some(name) = std::thread::current().name() {
250            assert!(thread_naming.contains(&format!("({})", name)));
251        }
252
253        for j in 1..i {
254            let mut error = String::new();
255            File::open(&format!("error_{}.log", j))
256                .unwrap()
257                .read_to_string(&mut error)
258                .unwrap();
259            let mut warn = String::new();
260            File::open(&format!("warn_{}.log", j))
261                .unwrap()
262                .read_to_string(&mut warn)
263                .unwrap();
264            let mut info = String::new();
265            File::open(&format!("info_{}.log", j))
266                .unwrap()
267                .read_to_string(&mut info)
268                .unwrap();
269            let mut debug = String::new();
270            File::open(&format!("debug_{}.log", j))
271                .unwrap()
272                .read_to_string(&mut debug)
273                .unwrap();
274            let mut trace = String::new();
275            File::open(&format!("trace_{}.log", j))
276                .unwrap()
277                .read_to_string(&mut trace)
278                .unwrap();
279
280            assert!(error.contains("Test Error"));
281            assert!(!error.contains("Test Warning"));
282            assert!(!error.contains("Test Information"));
283            assert!(!error.contains("Test Debug"));
284            assert!(!error.contains("Test Trace"));
285
286            assert!(warn.contains("Test Error"));
287            assert!(warn.contains("Test Warning"));
288            assert!(!warn.contains("Test Information"));
289            assert!(!warn.contains("Test Debug"));
290            assert!(!warn.contains("Test Trace"));
291
292            assert!(info.contains("Test Error"));
293            assert!(info.contains("Test Warning"));
294            assert!(info.contains("Test Information"));
295            assert!(!info.contains("Test Debug"));
296            assert!(!info.contains("Test Trace"));
297
298            assert!(debug.contains("Test Error"));
299            assert!(debug.contains("Test Warning"));
300            assert!(debug.contains("Test Information"));
301            assert!(debug.contains("Test Debug"));
302            assert!(!debug.contains("Test Trace"));
303
304            assert!(trace.contains("Test Error"));
305            assert!(trace.contains("Test Warning"));
306            assert!(trace.contains("Test Information"));
307            assert!(trace.contains("Test Debug"));
308            assert!(trace.contains("Test Trace"));
309        }
310    }
311}