1use core::fmt::Arguments;
2use std::io::{self, IsTerminal, Write};
3use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
4use std::sync::{Arc, Mutex as StdMutex};
5use std::time::Instant;
6
7use crate::EMIT_LOCK;
9#[cfg(feature = "color")]
10use crate::{color, level_color};
11use crate::{ct_enabled, write_level, write_timestamp, ColorMode, HumanDuration, Level, Target};
12
13pub struct Logger {
15 level: AtomicU8,
16 show_tid: AtomicBool,
17 show_time: AtomicBool,
18 show_group: AtomicBool,
19 show_file_line: AtomicBool,
20 color_mode: AtomicU8,
21 sink: StdMutex<Sink>,
22}
23
24struct Sink {
25 target: Target,
26 writer: Option<Arc<StdMutex<Box<dyn Write + Send>>>>,
27}
28
29impl Default for Logger {
30 fn default() -> Self {
31 Self {
32 level: AtomicU8::new(Level::Info as u8),
33 show_tid: AtomicBool::new(cfg!(feature = "thread-id")),
34 show_time: AtomicBool::new(cfg!(feature = "timestamp")),
35 show_group: AtomicBool::new(true),
36 show_file_line: AtomicBool::new(cfg!(feature = "file-line")),
37 color_mode: AtomicU8::new(ColorMode::Auto as u8),
38 sink: StdMutex::new(Sink {
39 target: Target::Stderr,
40 writer: None,
41 }),
42 }
43 }
44}
45
46impl Logger {
47 #[inline]
48 #[must_use]
49 pub fn builder() -> LoggerBuilder {
51 LoggerBuilder::default()
52 }
53
54 #[inline]
56 pub fn set_level(&self, l: Level) {
58 self.level.store(l as u8, Ordering::Relaxed);
59 }
60 #[inline]
61 pub fn set_show_thread_id(&self, on: bool) {
63 self.show_tid.store(on, Ordering::Relaxed);
64 }
65 #[inline]
66 pub fn set_show_time(&self, on: bool) {
68 self.show_time.store(on, Ordering::Relaxed);
69 }
70 #[inline]
71 pub fn set_show_group(&self, on: bool) {
73 self.show_group.store(on, Ordering::Relaxed);
74 }
75 #[inline]
76 pub fn set_show_file_line(&self, on: bool) {
78 self.show_file_line.store(on, Ordering::Relaxed);
79 }
80 #[inline]
81 pub fn set_color_mode(&self, m: ColorMode) {
83 self.color_mode.store(m as u8, Ordering::Relaxed);
84 }
85
86 #[inline]
87 pub fn set_target(&self, t: Target) {
91 self.sink.lock().unwrap().target = t;
92 }
93 pub fn set_writer(&self, w: Box<dyn Write + Send>) {
97 let arc = Arc::new(StdMutex::new(w));
98 let mut s = self.sink.lock().unwrap();
99 s.writer = Some(arc);
100 s.target = Target::Writer;
101 }
102 pub fn set_file(&self, path: impl AsRef<std::path::Path>) -> io::Result<()> {
106 let f = std::fs::OpenOptions::new()
107 .create(true)
108 .append(true)
109 .open(path)?;
110 self.set_writer(Box::new(f));
111 Ok(())
112 }
113
114 #[inline]
115 fn enabled(&self, l: Level) -> bool {
116 (l as u8) >= self.level.load(Ordering::Relaxed)
117 }
118
119 pub fn emit_to(
123 &self,
124 l: Level,
125 group: Option<&'static str>,
126 file: &'static str,
127 line_no: u32,
128 args: Arguments,
129 ) {
130 if !self.enabled(l) || !ct_enabled(l) {
131 return;
132 }
133
134 let (target, writer) = {
135 let s = self.sink.lock().unwrap();
136 (s.target, s.writer.clone())
137 };
138
139 let mut buf = Vec::<u8>::new();
140 let use_color = self.use_color_for_target(target);
141
142 if self.show_time.load(Ordering::Relaxed) {
143 write_timestamp(&mut buf);
144 }
145 write_level(&mut buf, l, use_color);
146
147 if self.show_tid.load(Ordering::Relaxed) {
148 #[cfg(feature = "thread-id")]
149 let _ = write!(&mut buf, " [{:?}]", std::thread::current().id());
150 }
151 if self.show_file_line.load(Ordering::Relaxed) {
152 let _ = write!(&mut buf, " <{file}:{line_no}>");
153 }
154
155 if self.show_group.load(Ordering::Relaxed) {
156 if let Some(g) = group {
157 #[cfg(feature = "color")]
158 if use_color {
159 let _ = write!(
160 &mut buf,
161 " [{}{}{}{}]",
162 color::BOLD,
163 level_color(l),
164 g,
165 color::RST
166 );
167 } else {
168 let _ = write!(&mut buf, " [{g}]");
169 }
170 #[cfg(not(feature = "color"))]
171 {
172 let _ = write!(&mut buf, " [{g}]");
173 }
174 }
175 }
176
177 let _ = buf.write_all(b" ");
178 let _ = buf.write_fmt(args);
179 let _ = buf.write_all(b"\n");
180
181 let _g = EMIT_LOCK.lock().unwrap();
182 match target {
183 Target::Stdout => {
184 let _ = io::stdout().lock().write_all(&buf);
185 }
186 Target::Stderr => {
187 let _ = io::stderr().lock().write_all(&buf);
188 }
189 Target::Writer => {
190 if let Some(w) = writer {
191 let _ = w.lock().unwrap().write_all(&buf);
192 }
193 }
194 }
195 }
196
197 #[inline]
198 fn use_color_for_target(&self, target: Target) -> bool {
199 #[cfg(not(feature = "color"))]
200 {
201 return false;
202 }
203 #[cfg(feature = "color")]
204 match ColorMode::from(self.color_mode.load(Ordering::Relaxed)) {
205 ColorMode::Always => true,
206 ColorMode::Never => false,
207 ColorMode::Auto => match target {
208 Target::Stdout => io::stdout().is_terminal(),
209 Target::Stderr => io::stderr().is_terminal(),
210 Target::Writer => false,
211 },
212 }
213 }
214}
215
216pub struct TimerGuard<'a> {
218 logger: &'a Logger,
219 label: &'static str,
220 start: Instant,
221 file: &'static str,
222 line: u32,
223}
224impl<'a> TimerGuard<'a> {
225 #[inline]
227 #[must_use]
228 pub fn new_at(logger: &'a Logger, label: &'static str, file: &'static str, line: u32) -> Self {
229 Self {
230 logger,
231 label,
232 start: Instant::now(),
233 file,
234 line,
235 }
236 }
237}
238impl Drop for TimerGuard<'_> {
239 fn drop(&mut self) {
240 let elapsed = self.start.elapsed();
241 self.logger.emit_to(
242 Level::Info,
243 Some(self.label),
244 self.file,
245 self.line,
246 format_args!("took {}", HumanDuration(elapsed)),
247 );
248 }
249}
250#[macro_export]
251macro_rules! __rustlog_local_scope_time {
253 ($lg:expr, $label:expr) => {
254 let _rustlog_scope_time_guard =
255 $crate::local::TimerGuard::new_at($lg, $label, file!(), line!());
256 };
257 ($lg:expr, $label:expr, $body:block) => {{
258 let _rustlog_scope_time_guard =
259 $crate::local::TimerGuard::new_at($lg, $label, file!(), line!());
260 $body
261 }};
262}
263
264impl From<u8> for ColorMode {
266 fn from(x: u8) -> Self {
267 match x {
268 1 => Self::Always,
269 2 => Self::Never,
270 _ => Self::Auto,
271 }
272 }
273}
274
275pub struct LoggerBuilder {
277 level: Level,
278 show_tid: Option<bool>,
279 show_time: Option<bool>,
280 show_group: Option<bool>,
281 show_file_line: Option<bool>,
282 color_mode: Option<ColorMode>,
283 target: Target,
284 writer: Option<Arc<StdMutex<Box<dyn Write + Send>>>>,
285 file_path: Option<std::path::PathBuf>,
286}
287impl Default for LoggerBuilder {
288 fn default() -> Self {
289 Self {
290 level: Level::Info,
291 show_tid: None,
292 show_time: None,
293 show_group: None,
294 show_file_line: None,
295 color_mode: None,
296 target: Target::Stderr,
297 writer: None,
298 file_path: None,
299 }
300 }
301}
302
303impl LoggerBuilder {
304 #[inline]
305 #[must_use]
306 pub const fn set_level(mut self, l: Level) -> Self {
308 self.level = l;
309 self
310 }
311 #[inline]
312 #[must_use]
313 pub const fn set_show_thread_id(mut self, on: bool) -> Self {
315 self.show_tid = Some(on);
316 self
317 }
318 #[inline]
319 #[must_use]
320 pub const fn set_show_time(mut self, on: bool) -> Self {
322 self.show_time = Some(on);
323 self
324 }
325 #[inline]
326 #[must_use]
327 pub const fn set_show_group(mut self, on: bool) -> Self {
329 self.show_group = Some(on);
330 self
331 }
332 #[inline]
333 #[must_use]
334 pub const fn set_show_file_line(mut self, on: bool) -> Self {
336 self.show_file_line = Some(on);
337 self
338 }
339 #[inline]
340 #[must_use]
341 pub const fn set_color_mode(mut self, m: ColorMode) -> Self {
343 self.color_mode = Some(m);
344 self
345 }
346 #[inline]
347 #[must_use]
348 pub const fn stdout(mut self) -> Self {
350 self.target = Target::Stdout;
351 self
352 }
353 #[inline]
354 #[must_use]
355 pub const fn stderr(mut self) -> Self {
357 self.target = Target::Stderr;
358 self
359 }
360 #[inline]
361 #[must_use]
362 pub fn set_writer(mut self, w: Box<dyn Write + Send>) -> Self {
364 self.target = Target::Writer;
365 self.writer = Some(Arc::new(StdMutex::new(w)));
366 self
367 }
368 #[inline]
369 #[must_use]
370 pub fn file(mut self, p: impl AsRef<std::path::Path>) -> Self {
372 self.target = Target::Writer;
373 self.file_path = Some(p.as_ref().to_owned());
374 self
375 }
376
377 pub fn build(self) -> io::Result<Logger> {
381 let writer = match (self.target, self.file_path) {
382 (Target::Writer, Some(p)) => {
383 let f = std::fs::OpenOptions::new()
384 .create(true)
385 .append(true)
386 .open(p)?;
387 Some(Arc::new(
388 StdMutex::new(Box::new(f) as Box<dyn Write + Send>),
389 ))
390 }
391 _ => self.writer,
392 };
393 let lg = Logger {
394 sink: StdMutex::new(Sink {
395 target: self.target,
396 writer,
397 }),
398 ..Logger::default()
399 };
400 lg.set_level(self.level);
401 if let Some(x) = self.show_tid {
402 lg.set_show_thread_id(x);
403 }
404 if let Some(x) = self.show_time {
405 lg.set_show_time(x);
406 }
407 if let Some(x) = self.show_group {
408 lg.set_show_group(x);
409 }
410 if let Some(x) = self.show_file_line {
411 lg.set_show_file_line(x);
412 }
413 if let Some(x) = self.color_mode {
414 lg.set_color_mode(x);
415 }
416 Ok(lg)
417 }
418
419 pub fn build_static(self) -> io::Result<&'static Logger> {
423 Ok(Box::leak(Box::new(self.build()?)))
424 }
425}
426
427#[macro_export]
433macro_rules! __rustlog_local_log {
435 ($lg:expr, $lvl:expr, $grp:expr, $($t:tt)+) => {{
436 let __lg = $lg; if $crate::ct_enabled($lvl) { __lg.emit_to($lvl, $grp, file!(), line!(), format_args!($($t)+)); }
438 }}
439}
440
441#[macro_export]
442macro_rules! __rustlog_local_trace { ($lg:expr, $($t:tt)+) => { $crate::__rustlog_local_log!($lg, $crate::Level::Trace, None, $($t)+) } }
444#[macro_export]
445macro_rules! __rustlog_local_debug { ($lg:expr, $($t:tt)+) => { $crate::__rustlog_local_log!($lg, $crate::Level::Debug, None, $($t)+) } }
447#[macro_export]
448macro_rules! __rustlog_local_info { ($lg:expr, $($t:tt)+) => { $crate::__rustlog_local_log!($lg, $crate::Level::Info, None, $($t)+) } }
450#[macro_export]
451macro_rules! __rustlog_local_warn { ($lg:expr, $($t:tt)+) => { $crate::__rustlog_local_log!($lg, $crate::Level::Warn, None, $($t)+) } }
453#[macro_export]
454macro_rules! __rustlog_local_error { ($lg:expr, $($t:tt)+) => { $crate::__rustlog_local_log!($lg, $crate::Level::Error, None, $($t)+) } }
456#[macro_export]
457macro_rules! __rustlog_local_fatal { ($lg:expr, $($t:tt)+) => { $crate::__rustlog_local_log!($lg, $crate::Level::Fatal, None, $($t)+) } }
459
460#[macro_export]
461macro_rules! __rustlog_local_trace_group { ($lg:expr, $grp:expr, $($t:tt)+) => { $crate::__rustlog_local_log!($lg, $crate::Level::Trace, Some($grp), $($t)+) } }
463#[macro_export]
464macro_rules! __rustlog_local_debug_group { ($lg:expr, $grp:expr, $($t:tt)+) => { $crate::__rustlog_local_log!($lg, $crate::Level::Debug, Some($grp), $($t)+) } }
466#[macro_export]
467macro_rules! __rustlog_local_info_group { ($lg:expr, $grp:expr, $($t:tt)+) => { $crate::__rustlog_local_log!($lg, $crate::Level::Info, Some($grp), $($t)+) } }
469#[macro_export]
470macro_rules! __rustlog_local_warn_group { ($lg:expr, $grp:expr, $($t:tt)+) => { $crate::__rustlog_local_log!($lg, $crate::Level::Warn, Some($grp), $($t)+) } }
472#[macro_export]
473macro_rules! __rustlog_local_error_group { ($lg:expr, $grp:expr, $($t:tt)+) => { $crate::__rustlog_local_log!($lg, $crate::Level::Error, Some($grp), $($t)+) } }
475#[macro_export]
476macro_rules! __rustlog_local_fatal_group { ($lg:expr, $grp:expr, $($t:tt)+) => { $crate::__rustlog_local_log!($lg, $crate::Level::Fatal, Some($grp), $($t)+) } }
478
479pub use crate::__rustlog_local_debug as debug;
484pub use crate::__rustlog_local_error as error;
485pub use crate::__rustlog_local_fatal as fatal;
486pub use crate::__rustlog_local_info as info;
487pub use crate::__rustlog_local_trace as trace;
488pub use crate::__rustlog_local_warn as warn;
489
490pub use crate::__rustlog_local_debug_group as debug_group;
491pub use crate::__rustlog_local_error_group as error_group;
492pub use crate::__rustlog_local_fatal_group as fatal_group;
493pub use crate::__rustlog_local_info_group as info_group;
494pub use crate::__rustlog_local_trace_group as trace_group;
495pub use crate::__rustlog_local_warn_group as warn_group;
496
497pub use crate::__rustlog_local_scope_time as scope_time;