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}