emoji_logger/
lib.rs

1#![deny(warnings)]
2#![deny(missing_docs)]
3
4//! A logger configured via an environment variable which writes cancer to
5//! standard error with colored output for log levels.
6//!
7//! ## Example
8//!
9//! ```
10//! extern crate emoji_logger;
11//! #[macro_use] extern crate log;
12//!
13//! fn main() {
14//!     emoji_logger::init();
15//!
16//!     trace!("this is trace level");
17//!     debug!("mom get the rubber duck");
18//!     info!("heck, our disk is full of logs again...");
19//!     warn!("should we worry");
20//!     error!("pls help");
21//! }
22//! ```
23
24extern crate ansi_term;
25extern crate env_logger;
26extern crate log;
27
28use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
29use std::time::Instant;
30
31use ansi_term::{Color, Style};
32use env_logger::Builder;
33use log::Level;
34
35static MAX_MODULE_WIDTH: AtomicUsize = ATOMIC_USIZE_INIT;
36static SINCE_WIDTH_INCREASE: AtomicUsize = ATOMIC_USIZE_INIT;
37
38/// Initializes the global logger with an emoji logger.
39///
40/// This should be called early in the execution of a Rust program, and the
41/// global logger may only be initialized once. Future initialization attempts
42/// will return an error.
43///
44/// # Panics
45///
46/// This function fails to set the global logger if one has already been set.
47#[inline]
48pub fn init() {
49    try_init().unwrap();
50}
51
52/// Initializes the global logger with an emoji logger.
53///
54/// This should be called early in the execution of a Rust program, and the
55/// global logger may only be initialized once. Future initialization attempts
56/// will return an error.
57///
58/// # Errors
59///
60/// This function fails to set the global logger if one has already been set.
61pub fn try_init() -> Result<(), log::SetLoggerError> {
62    try_init_custom_env("RUST_LOG")
63}
64
65/// Initialized the global logger with an emoji logger, with a custom variable
66/// name.
67///
68/// This should be called early in the execution of a Rust program, and the
69/// global logger may only be initialized once. Future initialization attempts
70/// will return an error.
71///
72/// # Panics
73///
74/// This function fails to set the global logger if one has already been set.
75pub fn init_custom_env(environment_variable_name: &str) {
76    try_init_custom_env(environment_variable_name).unwrap();
77}
78
79/// Initialized the global logger with an emoji logger, with a custom variable
80/// name.
81///
82/// This should be called early in the execution of a Rust program, and the
83/// global logger may only be initialized once. Future initialization attempts
84/// will return an error.
85///
86/// # Errors
87///
88/// This function fails to set the global logger if one has already been set.
89pub fn try_init_custom_env(environment_variable_name: &str) -> Result<(), log::SetLoggerError> {
90    let mut builder = formatted_builder()?;
91
92    if let Ok(s) = ::std::env::var(environment_variable_name) {
93        builder.parse(&s);
94    }
95
96    builder.try_init()
97}
98
99/// Returns a `env_logger::Builder` for further customization.
100///
101/// This method will return a colored and formatted) `env_logger::Builder`
102/// for further customization. Refer to env_logger::Build crate documentation
103/// for further details and usage.
104///
105/// This should be called early in the execution of a Rust program, and the
106/// global logger may only be initialized once. Future initialization attempts
107/// will return an error.
108///
109/// # Errors
110///
111/// This function fails to set the global logger if one has already been set.
112pub fn formatted_builder() -> Result<Builder, log::SetLoggerError> {
113    let mut builder = Builder::new();
114    let start_time = Instant::now();
115
116    builder.format(move |f, record| {
117        use std::io::Write;
118        let target = record.target();
119
120        let time = start_time.elapsed();
121        let (color, level) = match record.level() {
122            Level::Trace => (Color::Purple, " 🤓 T "),
123            Level::Debug => (Color::Blue, " 🤔 D "),
124            Level::Info => (Color::Green, " 😋 I "),
125            Level::Warn => (Color::Yellow, " 😥 W "),
126            Level::Error => (Color::Red, " 😡 E "),
127        };
128
129        let mut module_iter = target.split("::");
130        let krate = module_iter.next();
131        let path = module_iter.last();
132        let target = format!(
133            " {}{} ",
134            krate.unwrap_or_default(),
135            path.map(|s| format!(":{}", s)).unwrap_or_default()
136        );
137
138        SINCE_WIDTH_INCREASE.fetch_add(1, Ordering::Relaxed);
139        let mut max_width = MAX_MODULE_WIDTH.load(Ordering::Relaxed);
140        if max_width <= target.len() {
141            MAX_MODULE_WIDTH.store(target.len(), Ordering::Relaxed);
142            max_width = target.len();
143            SINCE_WIDTH_INCREASE.store(0, Ordering::Relaxed);
144        } else if SINCE_WIDTH_INCREASE.load(Ordering::Relaxed) > 5 {
145            MAX_MODULE_WIDTH.store(target.len(), Ordering::Relaxed);
146            max_width = target.len();
147        }
148
149        writeln!(
150            f,
151            "{}{}{}> {}",
152            Style::new().on(color).fg(Color::White).paint(level),
153            Style::new()
154                .on(Color::White)
155                .fg(Color::Black)
156                .dimmed()
157                .bold()
158                .paint(format!(
159                    " {:.2} ",
160                    time.as_secs() as f64 + (f64::from(time.subsec_millis()) / 1000f64)
161                )),
162            Style::new()
163                .bold()
164                .paint(format!("{: <width$}", target, width = max_width)),
165            format!("{}", record.args()).replace(
166                "\n",
167                &format!(
168                    "\n{} ",
169                    Style::new().on(color).fg(Color::White).paint(level)
170                )
171            )
172        )
173    });
174
175    Ok(builder)
176}