bunt_logger/
lib.rs

1//! A simple logger wrapper around [bunt](https://github.com/LukasKalbertodt/bunt).
2//!
3//! # Usage
4//!
5//! ```rust
6//! use bunt_logger::{
7//!     debug, error, info, trace, warn,
8//!     ColorChoice,
9//!     Level, // re-export of `log::Level`
10//! };
11//!
12//! fn main() {
13//!     bunt_logger::with()
14//!         .level(Level::Trace)
15//!         .stderr(ColorChoice::Always);
16//!
17//!     error!("{$red+bold}A red and bold error message!{/$}");
18//!     warn!("{$yellow}A yellow warning message!{/$}");
19//!     info!("{$green}A green info message!{/$}");
20//!     debug!("{$cyan}A cyan debug message!{/$}");
21//!     trace!("{$white+dimmed}A white and dimmed trace message!{/$}");
22//! }
23//! ```
24
25use std::sync::{Mutex, MutexGuard};
26
27use log::LevelFilter;
28use once_cell::sync::Lazy;
29
30pub use bunt::termcolor::{ColorChoice, StandardStream, WriteColor};
31pub use log::Level;
32
33#[doc(hidden)]
34#[macro_export]
35macro_rules! try_log {
36    ($level:ident, $writer:ident => $b:block) => {{
37        let mut prefs = $crate::with();
38        if prefs.enabled($crate::Level::$level) {
39            let mut $writer = prefs.get_writer();
40            $b
41        }
42    }};
43}
44
45/// Like [`log::error`], but bunt-compatible.
46///
47/// # Example
48/// ```rust
49/// use bunt_logger::error;
50///
51/// # fn main() {
52/// let x = -1;
53/// error!("{$red}Not a positive number:{/$} {[bold]}", x);
54/// # }
55/// ```
56#[macro_export]
57macro_rules! error {
58    ($format_str:literal $(, $arg:expr)* $(,)?) => {
59        $crate::error!([$format_str] $(, $arg )*)
60    };
61    ([$($format_str:literal),+ $(,)?] $(, $arg:expr)* $(,)?) => {
62        $crate::try_log!(Error, writer => {
63            let _ = ::bunt::writeln!(writer, [$($format_str),+] $(, $arg )*);
64        })
65    }
66}
67
68/// Like [`log::warn`], but bunt-compatible.
69///
70/// # Example
71/// ```rust
72/// use bunt_logger::warn;
73///
74/// # fn main() {
75/// warn!("He likes {$yellow}lemons{/$}, like {$blue+italic}a lot{/$}.");
76/// # }
77/// ```
78#[macro_export]
79macro_rules! warn {
80    ($format_str:literal $(, $arg:expr)* $(,)?) => {
81        $crate::warn!([$format_str] $(, $arg )*)
82    };
83    ([$($format_str:literal),+ $(,)?] $(, $arg:expr)* $(,)?) => {
84        $crate::try_log!(Warn, writer => {
85            let _ = ::bunt::writeln!(writer, [$($format_str),+] $(, $arg )*);
86        })
87    }
88}
89
90/// Like [`log::info`], but bunt-compatible.
91///
92/// # Example
93/// ```rust
94/// use bunt_logger::info;
95///
96/// # fn main() {
97/// let v = vec![1, 2, 3];
98/// info!("Here is some data: {[green]:?}.", v);
99/// # }
100/// ```
101#[macro_export]
102macro_rules! info {
103    ($format_str:literal $(, $arg:expr)* $(,)?) => {
104        $crate::info!([$format_str] $(, $arg )*)
105    };
106    ([$($format_str:literal),+ $(,)?] $(, $arg:expr)* $(,)?) => {
107        $crate::try_log!(Info, writer => {
108            let _ = ::bunt::writeln!(writer, [$($format_str),+] $(, $arg )*);
109        })
110    }
111}
112
113/// Like [`log::debug`], but bunt-compatible.
114///
115/// # Example
116/// ```rust
117/// use bunt_logger::debug;
118///
119/// # fn main() {
120/// let v = vec![1, 2, 3];
121/// debug!("{$bold}Length: {[cyan]}{/$}.", v.len());
122/// # }
123/// ```
124#[macro_export]
125macro_rules! debug {
126    ($format_str:literal $(, $arg:expr)* $(,)?) => {
127        $crate::debug!([$format_str] $(, $arg )*)
128    };
129    ([$($format_str:literal),+ $(,)?] $(, $arg:expr)* $(,)?) => {
130        $crate::try_log!(Debug, writer => {
131            let _ = ::bunt::writeln!(writer, [$($format_str),+] $(, $arg )*);
132        })
133    }
134}
135
136/// Like [`log::trace`], but bunt-compatible.
137///
138/// # Example
139/// ```rust
140/// use bunt_logger::trace;
141///
142/// # fn main() {
143/// let v = vec![1, 2, 3];
144/// trace!("{$italic}Watch the mouse!{/$}.");
145/// # }
146/// ```
147#[macro_export]
148macro_rules! trace {
149    ($format_str:literal $(, $arg:expr)* $(,)?) => {
150        $crate::trace!([$format_str] $(, $arg )*)
151    };
152    ([$($format_str:literal),+ $(,)?] $(, $arg:expr)* $(,)?) => {
153        $crate::try_log!(Trace, writer => {
154            let _ = ::bunt::writeln!(writer, [$($format_str),+] $(, $arg )*);
155        })
156    }
157}
158
159static LOGPREFS: Lazy<Mutex<LogPrefs>> = Lazy::new(|| {
160    let prefs = LogPrefs::new();
161    Mutex::new(prefs)
162});
163
164/// Returns a reference to the global preferences object, used for modifying preferences.
165///
166/// # Example
167/// ```rust
168/// use bunt_logger::{ColorChoice, Level};
169///
170/// fn main() {
171///     bunt_logger::with()
172///         .level(Level::Debug)
173///         .stdout(ColorChoice::Never);
174/// }
175/// ```
176#[inline]
177pub fn with() -> MutexGuard<'static, LogPrefs> {
178    LOGPREFS.lock().unwrap()
179}
180
181/// Preferences that dictate logging.
182pub struct LogPrefs {
183    quiet: bool,
184    filter: LevelFilter,
185
186    writer: Box<dyn WriteColor + Send>,
187}
188
189impl LogPrefs {
190    #[inline]
191    fn new() -> Self {
192        Self {
193            quiet: false,
194            filter: LevelFilter::Info,
195            writer: Box::new(StandardStream::stdout(ColorChoice::Auto)),
196        }
197    }
198
199    /// Sets whether all output should be silenced, regardless of log level.
200    ///
201    /// # Example
202    /// ```rust
203    /// # fn main() {
204    /// bunt_logger::with().quiet(true);
205    /// # }
206    /// ```
207    #[inline]
208    pub fn quiet(&mut self, quiet: bool) -> &mut Self {
209        self.quiet = quiet;
210        self
211    }
212
213    /// Sets the log level.
214    ///
215    /// # Example
216    /// ```rust
217    /// use bunt_logger::Level;
218    ///
219    /// # fn main() {
220    /// bunt_logger::with().level(Level::Debug);
221    /// # }
222    /// ```
223    #[inline]
224    pub fn level(&mut self, level: Level) -> &mut Self {
225        self.filter = level.to_level_filter();
226        self
227    }
228
229    /// Sets the logging target.
230    ///
231    /// By default, `StandardStream::stdout(ColorChoice::Auto)` is used.
232    ///
233    /// # Example
234    /// ```rust
235    /// use bunt_logger::{ColorChoice, StandardStream};
236    ///
237    /// # fn main() {
238    /// let stderr_writer = StandardStream::stderr(ColorChoice::Never);
239    /// bunt_logger::with()
240    ///     .writer(Box::new(stderr_writer));
241    /// # }
242    /// ```
243    #[inline]
244    pub fn writer(&mut self, writer: Box<dyn WriteColor + Send + Sync>) -> &mut Self {
245        self.writer = writer;
246        self
247    }
248
249    /// Sets the logging target to stdout with the given [`ColorChoice`].
250    ///
251    /// # Example
252    /// ```rust
253    /// use bunt_logger::ColorChoice;
254    ///
255    /// # fn main() {
256    /// bunt_logger::with()
257    ///     .stdout(ColorChoice::Always);
258    /// # }
259    /// ```
260    #[inline]
261    pub fn stdout(&mut self, color: ColorChoice) -> &mut Self {
262        self.writer(Box::new(StandardStream::stdout(color)))
263    }
264
265    /// Sets the logging target to stderr with the given [`ColorChoice`].
266    ///
267    /// # Example
268    /// ```rust
269    /// use bunt_logger::ColorChoice;
270    ///
271    /// # fn main() {
272    /// bunt_logger::with()
273    ///     .stderr(ColorChoice::Always);
274    /// # }
275    /// ```
276    #[inline]
277    pub fn stderr(&mut self, color: ColorChoice) -> &mut Self {
278        self.writer(Box::new(StandardStream::stderr(color)))
279    }
280
281    #[doc(hidden)]
282    #[inline]
283    pub fn enabled(&self, level: Level) -> bool {
284        !self.quiet && self.filter >= level
285    }
286
287    #[doc(hidden)]
288    #[inline]
289    pub fn get_writer<'a>(&'a mut self) -> &'a mut Box<dyn WriteColor + Send> {
290        &mut self.writer
291    }
292}