simplelog/loggers/
termlog.rs1use log::{
4 set_boxed_logger, set_max_level, Level, LevelFilter, Log, Metadata, Record, SetLoggerError,
5};
6use std::io::{Error, Write};
7use std::sync::Mutex;
8use termcolor::{BufferedStandardStream, ColorChoice};
9#[cfg(not(feature = "ansi_term"))]
10use termcolor::{ColorSpec, WriteColor};
11
12use super::logging::*;
13
14use crate::{Config, SharedLogger, ThreadLogMode};
15
16struct OutputStreams {
17 err: BufferedStandardStream,
18 out: BufferedStandardStream,
19}
20
21#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
23pub enum TerminalMode {
24 Stdout,
26 Stderr,
28 Mixed,
30}
31
32impl Default for TerminalMode {
33 fn default() -> TerminalMode {
34 TerminalMode::Mixed
35 }
36}
37
38pub struct TermLogger {
42 level: LevelFilter,
43 config: Config,
44 streams: Mutex<OutputStreams>,
45}
46
47impl TermLogger {
48 pub fn init(
67 log_level: LevelFilter,
68 config: Config,
69 mode: TerminalMode,
70 color_choice: ColorChoice,
71 ) -> Result<(), SetLoggerError> {
72 let logger = TermLogger::new(log_level, config, mode, color_choice);
73 set_max_level(log_level);
74 set_boxed_logger(logger)?;
75 Ok(())
76 }
77
78 pub fn new(
101 log_level: LevelFilter,
102 config: Config,
103 mode: TerminalMode,
104 color_choice: ColorChoice,
105 ) -> Box<TermLogger> {
106 let streams = match mode {
107 TerminalMode::Stdout => OutputStreams {
108 err: BufferedStandardStream::stdout(color_choice),
109 out: BufferedStandardStream::stdout(color_choice),
110 },
111 TerminalMode::Stderr => OutputStreams {
112 err: BufferedStandardStream::stderr(color_choice),
113 out: BufferedStandardStream::stderr(color_choice),
114 },
115 TerminalMode::Mixed => OutputStreams {
116 err: BufferedStandardStream::stderr(color_choice),
117 out: BufferedStandardStream::stdout(color_choice),
118 },
119 };
120
121 Box::new(TermLogger {
122 level: log_level,
123 config,
124 streams: Mutex::new(streams),
125 })
126 }
127
128 fn try_log_term(
129 &self,
130 record: &Record<'_>,
131 term_lock: &mut BufferedStandardStream,
132 ) -> Result<(), Error> {
133 #[cfg(not(feature = "ansi_term"))]
134 let color = self.config.level_color[record.level() as usize];
135
136 if self.config.time <= record.level() && self.config.time != LevelFilter::Off {
137 write_time(term_lock, &self.config)?;
138 }
139
140 if self.config.level <= record.level() && self.config.level != LevelFilter::Off {
141 #[cfg(not(feature = "ansi_term"))]
142 if !self.config.write_log_enable_colors {
143 term_lock.set_color(ColorSpec::new().set_fg(color))?;
144 }
145
146 write_level(record, term_lock, &self.config)?;
147
148 #[cfg(not(feature = "ansi_term"))]
149 if !self.config.write_log_enable_colors {
150 term_lock.reset()?;
151 }
152 }
153
154 if self.config.thread <= record.level() && self.config.thread != LevelFilter::Off {
155 match self.config.thread_log_mode {
156 ThreadLogMode::IDs => {
157 write_thread_id(term_lock, &self.config)?;
158 }
159 ThreadLogMode::Names | ThreadLogMode::Both => {
160 write_thread_name(term_lock, &self.config)?;
161 }
162 }
163 }
164
165 if self.config.target <= record.level() && self.config.target != LevelFilter::Off {
166 write_target(record, term_lock, &self.config)?;
167 }
168
169 if self.config.location <= record.level() && self.config.location != LevelFilter::Off {
170 write_location(record, term_lock)?;
171 }
172
173 write_args(record, term_lock)?;
174
175 term_lock.flush()
181 }
182
183 fn try_log(&self, record: &Record<'_>) -> Result<(), Error> {
184 if self.enabled(record.metadata()) {
185 if should_skip(&self.config, record) {
186 return Ok(());
187 }
188
189 let mut streams = self.streams.lock().unwrap();
190
191 if record.level() == Level::Error {
192 self.try_log_term(record, &mut streams.err)
193 } else {
194 self.try_log_term(record, &mut streams.out)
195 }
196 } else {
197 Ok(())
198 }
199 }
200}
201
202impl Log for TermLogger {
203 fn enabled(&self, metadata: &Metadata<'_>) -> bool {
204 metadata.level() <= self.level
205 }
206
207 fn log(&self, record: &Record<'_>) {
208 let _ = self.try_log(record);
209 }
210
211 fn flush(&self) {
212 let mut streams = self.streams.lock().unwrap();
213 let _ = streams.out.flush();
214 let _ = streams.err.flush();
215 }
216}
217
218impl SharedLogger for TermLogger {
219 fn level(&self) -> LevelFilter {
220 self.level
221 }
222
223 fn config(&self) -> Option<&Config> {
224 Some(&self.config)
225 }
226
227 fn as_log(self: Box<Self>) -> Box<dyn Log> {
228 Box::new(*self)
229 }
230}