1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//!
//! A tiny, simple, thread-safe logging library.
//! No configuration options, take it or leave it.
//!
//! Writes log messages to `stdout`/`stderr`. The writes are thread-safe.
//! If an error occurs when writing to the log file it panics.
//!
//! Provided logging macros:
//!
//! - [`log!()`]
//! - [`info!()`]
//! - [`warn!()`]
//! - [`err!()`]
//!
//! Usage
//! -----
//!
//! ```rust
//! extern crate mhlog;
//!
//! use mhlog::{log,info,warn,err};
//!
//! log!("Log message. Prefixed with a timestamp. It's {}", "thread-safe!");
//! info!("Logging message prefixed by '<timestamp> Info:' ");
//! warn!("Warning message prefixed by '<timestamp> Warning:' ");
//! err!("Error message prefixed by '<timestamp> Error:' ");
//! ```
//! 
//! Features
//! --------
//! 
//! ### Writing to stdout
//! 
//! By default all log messages are printed to **stderr**. To make [`log!()`] and [`info!()`] print to **stdout** instead, enable the `log2stdout` feature.
//! 
//! ```toml
//! [dependencies]
//! mhlog = { version = "*", features = ["log2stdout"] }
//! ```
//! 
//! ### Coloured log messages
//! 
//! Coloured log messages can be enabled with the `colours` feature.
//! 
//! ```toml
//! [dependencies]
//! mhlog = { version = "*", features = ["colours"] }
//! ```
//! 
//! [`log!()`]: macro.log.html
//! [`info!()`]: macro.info.html
//! [`warn!()`]: macro.warn.html
//! [`err!()`]: macro.err.html

extern crate chrono;
#[cfg(feature = "colours")]
extern crate console;

#[cfg(feature = "colours")]
#[doc(hidden)]
pub use console::style;

use chrono::prelude::*;
use std::fmt::Display;

// -----------------------------------------------------------------------------
// Globals variables

// Time format in logging messages
const TIME_FMT: &'static str = "%F %T";

#[doc(hidden)]
pub fn _log(prefix: impl Display, msg: String, err: bool) {
    use std::io::{stderr, stdout, Write};

    let timestamp = Local::now().format(TIME_FMT).to_string();
    // Style the timestamp if colours enabled. Must be handlet differently
    // for stderr and stdout.
    #[cfg(feature = "colours")]
    let timestamp = match err || cfg!(not(feature = "log2stdout")) {
        true  => style(timestamp).for_stderr().cyan().dim(),
        false => style(timestamp).for_stdout().cyan().dim(),
    };

    let txt = format!("{} {}{}\n", timestamp, prefix, msg);

    // Unless log2stdout enabled, always print to stderr.
    if err || cfg!(not(feature = "log2stdout")) {
        let _ = stderr().lock().write_all(txt.as_bytes());
    } else {
        let _ = stdout().lock().write_all(txt.as_bytes());
    }
}

/*******************************************************************************
 *                                                                             *
 *  macros
 *                                                                             *
 *******************************************************************************/

/// Print a log message, prefixed by a timestamp.
#[macro_export]
macro_rules! log {
    ($($arg:tt)+) => (
        $crate::_log("", format!($($arg)+), false);
    )
}

/// Print an info log message, prefixed by a timestamp and _Info_.
#[macro_export]
macro_rules! info {
    ($($arg:tt)+) => ({
        #[cfg(not(feature = "colours"))]
        $crate::_log("Info: ", format!($($arg)+), false);
        #[cfg(feature = "colours")]
        match cfg!(feature = "log2stdout") {
            true  => $crate::_log($crate::style("Info: ").for_stdout().bold().green(), format!($($arg)+), false),
            false => $crate::_log($crate::style("Info: ").for_stderr().bold().green(), format!($($arg)+), false),
        }
    })
}

/// Print a warning log message, prefixed by a timestamp and _Warning_.
#[macro_export]
macro_rules! warn {
    ($($arg:tt)+) => (
        #[cfg(not(feature = "colours"))]
        $crate::_log("Warning: ", format!($($arg)+), true);
        #[cfg(feature = "colours")]
        $crate::_log($crate::style("Warning: ").for_stderr().bold().yellow(), format!($($arg)+), true);
    )
}

/// Print an error log message, prefixed by a timestamp and _Error_.
#[macro_export]
macro_rules! err {
    ($($arg:tt)+) => (
        #[cfg(not(feature = "colours"))]
        $crate::_log("Error: ", format!($($arg)+), true);
        #[cfg(feature = "colours")]
        $crate::_log($crate::style("Error: ").for_stderr().bold().red(), format!($($arg)+), true);
    )
}