From f6ab14470b1228bb99ebffa90a967ef8915d14a3 Mon Sep 17 00:00:00 2001 From: rasul Date: Wed, 6 Nov 2019 11:25:34 -0600 Subject: [PATCH] use fern for logging to stdout and to file --- Cargo.lock | 12 ++++++ Cargo.toml | 1 + src/config.rs | 33 ++++------------ src/logger.rs | 106 +++++++++++++------------------------------------- src/main.rs | 4 +- 5 files changed, 50 insertions(+), 106 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e491224..ac4be7d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -173,6 +173,16 @@ dependencies = [ "synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "fern" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", + "colored 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "fuchsia-cprng" version = "0.1.1" @@ -354,6 +364,7 @@ dependencies = [ "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", "colored 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "fern 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", @@ -463,6 +474,7 @@ dependencies = [ "checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" "checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" "checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" +"checksum fern 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e69ab0d5aca163e388c3a49d284fed6c3d0810700e77c5ae2756a50ec1a4daaa" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" "checksum getopts 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" diff --git a/Cargo.toml b/Cargo.toml index 124a0b1..45e9c8d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" chrono = "0.4" colored = "1.8" dirs = "2.0" +fern = { version = "0.5", features = ["colored"] } getopts = "0.2" log = "0.4" serde = "1.0" diff --git a/src/config.rs b/src/config.rs index 29794fa..01c8166 100644 --- a/src/config.rs +++ b/src/config.rs @@ -9,8 +9,7 @@ use log::LevelFilter; pub struct Config { pub config_directory: PathBuf, pub log_level: LevelFilter, - pub stdout: PathBuf, - pub stderr: PathBuf, + pub log_file: PathBuf, } impl Config { @@ -26,8 +25,7 @@ impl Config { "log level (error, warn, info, debug, trace)", "LEVEL", ); - opts.optopt("o", "out", "stdout path", "STDOUT"); - opts.optopt("e", "err", "stderr path", "STDERR"); + opts.optopt("f", "logfile", "log file", "FILE"); opts.optflag("h", "help", "print this help menu"); let matches = match opts.parse(&args[1..]) { @@ -53,27 +51,11 @@ impl Config { PathBuf::from(".") }; - let stdout = if let Some(dir) = matches.opt_str("o") { - PathBuf::from(dir) - } else if let Some(mut dir) = config_dir() { - dir.push("sup"); - dir.push("stdout.log"); - dir - } else { - let mut p = PathBuf::from("."); - p.push("stdout.log"); - p - }; - - let stderr = if let Some(dir) = matches.opt_str("e") { - PathBuf::from(dir) - } else if let Some(mut dir) = config_dir() { - dir.push("sup"); - dir.push("stderr.log"); - dir + let log_file = if let Some(f) = matches.opt_str("f") { + PathBuf::from(f) } else { - let mut p = PathBuf::from("."); - p.push("stderr.log"); + let mut p = PathBuf::new(); + p.push("sup.log"); p }; @@ -97,8 +79,7 @@ impl Config { Config { config_directory, log_level, - stdout, - stderr, + log_file, } } } diff --git a/src/logger.rs b/src/logger.rs index 10a8b71..cbbc211 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -1,84 +1,34 @@ -use std::boxed::Box; -use std::error::Error; -use std::fmt; +use std::path::PathBuf; +use chrono::Local; use colored::Colorize; -use log::*; +use fern::{Dispatch, log_file}; +use fern::colors::{Color, ColoredLevelConfig}; +use log::LevelFilter; use crate::result::Result; -static LOGGER: Logger = Logger; - -#[derive(Debug)] -pub enum LoggerError { - SetLogger(SetLoggerError), -} - -impl From for LoggerError { - fn from(e: SetLoggerError) -> Self { - LoggerError::SetLogger(e) - } -} - -impl Error for LoggerError { - fn description(&self) -> &str { - match *self { - LoggerError::SetLogger(_) => "Logger already initialized", - } - } - fn cause(&self) -> Option<&dyn Error> { - match *self { - LoggerError::SetLogger(_) => Some(self), - } - } -} - -impl fmt::Display for LoggerError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use std::error::Error as FmtError; - write!(f, "{}", self.description()) - } -} - -struct Logger; - -impl Log for Logger { - fn enabled(&self, metadata: &Metadata) -> bool { - metadata.level() <= Level::Trace - } - - fn log(&self, record: &Record) { - if self.enabled(record.metadata()) { - let now = chrono::Local::now(); - let ts = now.format("%Y-%m-%dT%H:%M:%S%.3f%z").to_string(); - - let msg = match record.level() { - Level::Error => format!("{} {} {}", ts.white().bold(), "ERR".red(), record.args()), - Level::Warn => { - format!("{} {} {}", ts.white().bold(), "WRN".purple(), record.args()) - } - Level::Info => format!("{} {} {}", ts.white().bold(), "INF".cyan(), record.args()), - Level::Debug => { - format!("{} {} {}", ts.white().bold(), "DBG".yellow(), record.args()) - } - Level::Trace => { - format!("{} {} {}", ts.white().bold(), "TRC".green(), record.args()) - } - }; - println!("{}", msg); - } - } - - fn flush(&self) {} -} - -pub fn init() -> Result<()> { - match set_logger(&LOGGER).map(|()| set_max_level(LevelFilter::Info)) { - Ok(_) => Ok(()), - Err(e) => Err(Box::new(LoggerError::from(e))), - } -} - -pub fn set_level(level: LevelFilter) { - set_max_level(level); +pub fn setup_logger(logfile: PathBuf, level: LevelFilter) -> Result<()> { + Dispatch::new() + .format(|out, message, record| { + let colors = ColoredLevelConfig::new() + .error(Color::Red) + .warn(Color::Magenta) + .info(Color::Cyan) + .debug(Color::Yellow) + .trace(Color::Green); + + out.finish(format_args!( + "{} {} {}", + Local::now().format("%Y-%m-%dT%H:%M:%S%.3f%z").to_string().white().bold(), + colors.color(record.level()), + message + )) + }) + .level(level) + .chain(std::io::stdout()) + .chain(log_file(logfile)?) + .apply()?; + + Ok(()) } diff --git a/src/main.rs b/src/main.rs index 7324da2..8e6c282 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ extern crate chrono; extern crate colored; extern crate dirs; +extern crate fern; extern crate getopts; #[macro_use] extern crate log; @@ -26,11 +27,10 @@ use result::Result; fn main() { let config = Config::from_args(); - if let Err(e) = logger::init() { + if let Err(e) = logger::setup_logger(config.log_file, config.log_level) { println!("FATAL: Error initializing logger: {:?}", e); std::process::exit(1); } - logger::set_level(config.log_level); let mut apps_file = config.config_directory.clone(); apps_file.push("apps.toml");