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