outlog 0.1.0

A simple logging system based on the log crate.
Documentation
//! # Outlog
//!
//! Outlog is a logging implementation based on the [log] crate.\
//! It is designed to be as simple as possible, while still providing good looking log output.
//!
//! # Usage
//!
//! ```rust
//! #[macro_use]
//! extern crate outlog;
//!
//! # fn main() {
//! outlog::init_with_default().expect("Failed to initialize logging!");
//!
//! info!("Hello, World");
//! # }
//! ```
//!
//! # Cargo Features
//!
//! The following features can be enabled when including outlog as a dependency:
//!
//! * `color`: Enables colored output using [SGR Sequences][SGR] and the [atty] crate.
//! * `chrono`: Enables outputting the local time using the [chrono] crate.
//! * `panic`: Enables a custom panic hook that prints panic messages with `error!` (Backtraces aren't supported).
//! * `config-serde`: Enables serializing/deserializing the [`Config`] struct with [serde].
//! * `all`: Enables all of the above.
//!
//! [log]: https://docs.rs/log
//! [SGR]: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters
//! [atty]: https://docs.rs/atty
//! [chrono]: https://docs.rs/chrono
//! [`Config`]: ./struct.Config.html
//! [serde]: https://serde.rs

#[cfg(feature = "color")]
mod color;
#[cfg(feature = "panic")]
mod panic;
mod config;

pub use config::Config;
pub use log::LevelFilter;
pub use log::{error, warn, info, debug, trace};

/// Initializes the logging system with the given configuration.
pub fn init(config: Config) -> Result<(), log::SetLoggerError> {
    let level = config.level;

    let logger = Box::new(Logger { config });

    log::set_boxed_logger(logger).map(|()| {
        log::set_max_level(level);
        #[cfg(feature = "panic")]
        panic::install_panic_hook();
    })
}

/// Initializes the logging system with the given level.
pub fn init_with_level(level: log::LevelFilter) -> Result<(), log::SetLoggerError> {
    init(Config { level, .. Default::default() })
}

/// Initializes the logging system with the default configuration.
pub fn init_with_default() -> Result<(), log::SetLoggerError> {
    init(Default::default())
}

struct Logger {
    config: Config
}

impl log::Log for Logger {
    #[inline]
    fn enabled(&self, metadata: &log::Metadata) -> bool {
        self.config.level >= metadata.level()
    }

    fn log(&self, record: &log::Record) {
        if !self.enabled(record.metadata()) {
            return;
        }

        #[cfg(feature = "chrono")]
        let time = chrono::Local::now().format(&self.config.datetime_format);

        #[cfg(not(feature = "chrono"))]
        let time = "";

        #[cfg(feature = "color")]
        let (sgr_in, sgr_out) = if color::color_enabled(&self.config) {
            color::get_colors(record.level())
        } else {
            ("", "")
        };

        #[cfg(not(feature = "color"))]
        let (sgr_in, sgr_out) = ("", "");

        println!("{}[{}{:5}{}] {}", time, sgr_in, record.level(), sgr_out, record.args());
    }

    #[inline]
    fn flush(&self) {
        use std::io::Write;

        std::io::stdout().flush().unwrap()
    }
}

#[cfg(test)]
mod tests {
    use log::*;
    use super::*;

    #[test]
    fn it_works() {
        init_with_level(log::LevelFilter::max()).unwrap();

        error!("Error!");
        warn!("Warn!");
        info!("Info!");
        debug!("Debug!");
        trace!("Trace!");
    }
}