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}