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