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