1#![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
54pub trait SharedLogger: Log {
60 fn level(&self) -> LevelFilter;
73
74 fn config(&self) -> Option<&Config>;
89
90 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 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 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 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 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 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}