Skip to main content

outlog/
lib.rs

1//! # Outlog
2//!
3//! Outlog is a logging implementation based on the [log] crate.\
4//! It is designed to be as simple as possible, while still providing good looking log output.
5//!
6//! # Usage
7//!
8//! ```rust
9//! #[macro_use]
10//! extern crate outlog;
11//!
12//! # fn main() {
13//! outlog::init_with_default().expect("Failed to initialize logging!");
14//!
15//! info!("Hello, World");
16//! # }
17//! ```
18//!
19//! # Cargo Features
20//!
21//! The following features can be enabled when including outlog as a dependency:
22//!
23//! * `color`: Enables colored output using [SGR Sequences][SGR] and the [atty] crate.
24//! * `chrono`: Enables outputting the local time using the [chrono] crate.
25//! * `panic`: Enables a custom panic hook that prints panic messages with `error!` (Backtraces aren't supported).
26//! * `config-serde`: Enables serializing/deserializing the [`Config`] struct with [serde].
27//! * `all`: Enables all of the above.
28//!
29//! [log]: https://docs.rs/log
30//! [SGR]: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters
31//! [atty]: https://docs.rs/atty
32//! [chrono]: https://docs.rs/chrono
33//! [`Config`]: ./struct.Config.html
34//! [serde]: https://serde.rs
35
36#[cfg(feature = "color")]
37mod color;
38#[cfg(feature = "panic")]
39mod panic;
40mod config;
41
42pub use config::Config;
43pub use log::LevelFilter;
44pub use log::{error, warn, info, debug, trace};
45
46/// Initializes the logging system with the given configuration.
47pub fn init(config: Config) -> Result<(), log::SetLoggerError> {
48    let level = config.level;
49
50    let logger = Box::new(Logger { config });
51
52    log::set_boxed_logger(logger).map(|()| {
53        log::set_max_level(level);
54        #[cfg(feature = "panic")]
55        panic::install_panic_hook();
56    })
57}
58
59/// Initializes the logging system with the given level.
60pub fn init_with_level(level: log::LevelFilter) -> Result<(), log::SetLoggerError> {
61    init(Config { level, .. Default::default() })
62}
63
64/// Initializes the logging system with the default configuration.
65pub fn init_with_default() -> Result<(), log::SetLoggerError> {
66    init(Default::default())
67}
68
69struct Logger {
70    config: Config
71}
72
73impl log::Log for Logger {
74    #[inline]
75    fn enabled(&self, metadata: &log::Metadata) -> bool {
76        self.config.level >= metadata.level()
77    }
78
79    fn log(&self, record: &log::Record) {
80        if !self.enabled(record.metadata()) {
81            return;
82        }
83
84        #[cfg(feature = "chrono")]
85        let time = chrono::Local::now().format(&self.config.datetime_format);
86
87        #[cfg(not(feature = "chrono"))]
88        let time = "";
89
90        #[cfg(feature = "color")]
91        let (sgr_in, sgr_out) = if color::color_enabled(&self.config) {
92            color::get_colors(record.level())
93        } else {
94            ("", "")
95        };
96
97        #[cfg(not(feature = "color"))]
98        let (sgr_in, sgr_out) = ("", "");
99
100        println!("{}[{}{:5}{}] {}", time, sgr_in, record.level(), sgr_out, record.args());
101    }
102
103    #[inline]
104    fn flush(&self) {
105        use std::io::Write;
106
107        std::io::stdout().flush().unwrap()
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use log::*;
114    use super::*;
115
116    #[test]
117    fn it_works() {
118        init_with_level(log::LevelFilter::max()).unwrap();
119
120        error!("Error!");
121        warn!("Warn!");
122        info!("Info!");
123        debug!("Debug!");
124        trace!("Trace!");
125    }
126}