blob: aa2661f4c2158c1a69f5013d49a1604d899336e2 [file] [log] [blame]
// Copyright 2016 Victor Brekenfeld
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
//!
//! `simplelog` provides a series of logging facilities, that can be easily combined.
//!
//! - `SimpleLogger` (very basic logger that logs to stdout)
//! - `TermLogger` (advanced terminal logger, that splits to stdout/err and has color support) (can be excluded on unsupported platforms)
//! - `WriteLogger` (logs to a given struct implementing `Write`, e.g. a file)
//! - `CombinedLogger` (can be used to form combinations of the above loggers)
//! - `TestLogger` (specialized logger for tests. Uses print!() / println!() for tests to be able to capture the output)
//!
//! Only one Logger should be initialized of the start of your program
//! through the `Logger::init(...)` method. For the actual calling syntax
//! take a look at the documentation of the specific implementation(s) you wanna use.
//!
#![deny(missing_docs, rust_2018_idioms)]
mod config;
mod loggers;
pub use self::config::{Config, ConfigBuilder, LevelPadding, ThreadPadding, ThreadLogMode};
#[cfg(feature = "test")]
pub use self::loggers::TestLogger;
pub use self::loggers::{CombinedLogger, SimpleLogger, WriteLogger};
#[cfg(feature = "termcolor")]
pub use self::loggers::{TermLogError, TermLogger, TerminalMode};
pub use log::{Level, LevelFilter};
use log::Log;
#[cfg(test)]
use log::*;
/// Trait to have a common interface to obtain the Level of Loggers
///
/// Necessary for CombinedLogger to calculate
/// the lowest used Level.
///
pub trait SharedLogger: Log {
/// Returns the set Level for this Logger
///
/// # Examples
///
/// ```
/// # extern crate simplelog;
/// # use simplelog::*;
/// # fn main() {
/// let logger = SimpleLogger::new(LevelFilter::Info, Config::default());
/// println!("{}", logger.level());
/// # }
/// ```
fn level(&self) -> LevelFilter;
/// Inspect the config of a running Logger
///
/// An Option is returned, because some Logger may not contain a Config
///
/// # Examples
///
/// ```
/// # extern crate simplelog;
/// # use simplelog::*;
/// # fn main() {
/// let logger = SimpleLogger::new(LevelFilter::Info, Config::default());
/// println!("{:?}", logger.config());
/// # }
/// ```
fn config(&self) -> Option<&Config>;
/// Returns the logger as a Log trait object
fn as_log(self: Box<Self>) -> Box<dyn Log>;
}
#[cfg(test)]
mod tests {
use std::fs::File;
use std::io::Read;
use super::*;
#[test]
fn test() {
let mut i = 0;
CombinedLogger::init({
let mut vec = Vec::new();
let mut conf_builder = ConfigBuilder::new();
let conf_thread_name = ConfigBuilder::new()
.set_time_level(LevelFilter::Off)
.set_thread_level(LevelFilter::Error)
.set_thread_mode(ThreadLogMode::Names)
.build();
vec.push(
WriteLogger::new(LevelFilter::Error, conf_thread_name, File::create("thread_naming.log").unwrap()) as Box<dyn SharedLogger>
);
for elem in vec![
LevelFilter::Off,
LevelFilter::Trace,
LevelFilter::Debug,
LevelFilter::Info,
LevelFilter::Warn,
LevelFilter::Error,
] {
let conf = conf_builder
.set_location_level(elem)
.set_target_level(elem)
.set_max_level(elem)
.set_time_level(elem)
.build();
i += 1;
//error
vec.push(
SimpleLogger::new(LevelFilter::Error, conf.clone()) as Box<dyn SharedLogger>
);
#[cfg(feature = "termcolor")]
vec.push(
TermLogger::new(LevelFilter::Error, conf.clone(), TerminalMode::Mixed)
as Box<dyn SharedLogger>,
);
vec.push(WriteLogger::new(
LevelFilter::Error,
conf.clone(),
File::create(&format!("error_{}.log", i)).unwrap(),
) as Box<dyn SharedLogger>);
#[cfg(feature = "test")]
vec.push(TestLogger::new(LevelFilter::Error, conf.clone()));
//warn
vec.push(
SimpleLogger::new(LevelFilter::Warn, conf.clone()) as Box<dyn SharedLogger>
);
#[cfg(feature = "termcolor")]
vec.push(
TermLogger::new(LevelFilter::Warn, conf.clone(), TerminalMode::Mixed)
as Box<dyn SharedLogger>,
);
vec.push(WriteLogger::new(
LevelFilter::Warn,
conf.clone(),
File::create(&format!("warn_{}.log", i)).unwrap(),
) as Box<dyn SharedLogger>);
#[cfg(feature = "test")]
vec.push(TestLogger::new(LevelFilter::Warn, conf.clone()));
//info
vec.push(
SimpleLogger::new(LevelFilter::Info, conf.clone()) as Box<dyn SharedLogger>
);
#[cfg(feature = "termcolor")]
vec.push(
TermLogger::new(LevelFilter::Info, conf.clone(), TerminalMode::Mixed)
as Box<dyn SharedLogger>,
);
vec.push(WriteLogger::new(
LevelFilter::Info,
conf.clone(),
File::create(&format!("info_{}.log", i)).unwrap(),
) as Box<dyn SharedLogger>);
#[cfg(feature = "test")]
vec.push(TestLogger::new(LevelFilter::Info, conf.clone()));
//debug
vec.push(
SimpleLogger::new(LevelFilter::Debug, conf.clone()) as Box<dyn SharedLogger>
);
#[cfg(feature = "termcolor")]
vec.push(
TermLogger::new(LevelFilter::Debug, conf.clone(), TerminalMode::Mixed)
as Box<dyn SharedLogger>,
);
vec.push(WriteLogger::new(
LevelFilter::Debug,
conf.clone(),
File::create(&format!("debug_{}.log", i)).unwrap(),
) as Box<dyn SharedLogger>);
#[cfg(feature = "test")]
vec.push(TestLogger::new(LevelFilter::Debug, conf.clone()));
//trace
vec.push(
SimpleLogger::new(LevelFilter::Trace, conf.clone()) as Box<dyn SharedLogger>
);
#[cfg(feature = "termcolor")]
vec.push(
TermLogger::new(LevelFilter::Trace, conf.clone(), TerminalMode::Mixed)
as Box<dyn SharedLogger>,
);
vec.push(WriteLogger::new(
LevelFilter::Trace,
conf.clone(),
File::create(&format!("trace_{}.log", i)).unwrap(),
) as Box<dyn SharedLogger>);
#[cfg(feature = "test")]
vec.push(TestLogger::new(LevelFilter::Trace, conf.clone()));
}
vec
})
.unwrap();
error!("Test Error");
warn!("Test Warning");
info!("Test Information");
debug!("Test Debug");
trace!("Test Trace");
let mut thread_naming = String::new();
File::open("thread_naming.log").unwrap().read_to_string(&mut thread_naming).unwrap();
if let Some(name) = std::thread::current().name() {
assert!(thread_naming.contains(&format!("({})", name)));
}
for j in 1..i {
let mut error = String::new();
File::open(&format!("error_{}.log", j))
.unwrap()
.read_to_string(&mut error)
.unwrap();
let mut warn = String::new();
File::open(&format!("warn_{}.log", j))
.unwrap()
.read_to_string(&mut warn)
.unwrap();
let mut info = String::new();
File::open(&format!("info_{}.log", j))
.unwrap()
.read_to_string(&mut info)
.unwrap();
let mut debug = String::new();
File::open(&format!("debug_{}.log", j))
.unwrap()
.read_to_string(&mut debug)
.unwrap();
let mut trace = String::new();
File::open(&format!("trace_{}.log", j))
.unwrap()
.read_to_string(&mut trace)
.unwrap();
assert!(error.contains("Test Error"));
assert!(!error.contains("Test Warning"));
assert!(!error.contains("Test Information"));
assert!(!error.contains("Test Debug"));
assert!(!error.contains("Test Trace"));
assert!(warn.contains("Test Error"));
assert!(warn.contains("Test Warning"));
assert!(!warn.contains("Test Information"));
assert!(!warn.contains("Test Debug"));
assert!(!warn.contains("Test Trace"));
assert!(info.contains("Test Error"));
assert!(info.contains("Test Warning"));
assert!(info.contains("Test Information"));
assert!(!info.contains("Test Debug"));
assert!(!info.contains("Test Trace"));
assert!(debug.contains("Test Error"));
assert!(debug.contains("Test Warning"));
assert!(debug.contains("Test Information"));
assert!(debug.contains("Test Debug"));
assert!(!debug.contains("Test Trace"));
assert!(trace.contains("Test Error"));
assert!(trace.contains("Test Warning"));
assert!(trace.contains("Test Information"));
assert!(trace.contains("Test Debug"));
assert!(trace.contains("Test Trace"));
}
}
}