diff --git a/Cargo.lock b/Cargo.lock index 1e64561..8e81e5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -243,6 +243,15 @@ version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "lowcharts" version = "0.2.0" @@ -252,7 +261,9 @@ dependencies = [ "derive_builder", "float_eq", "isatty", + "log", "regex", + "simplelog", "tempfile", "yansi", ] @@ -417,6 +428,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "simplelog" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59d0fe306a0ced1c88a58042dc22fc2ddd000982c26d75f6aa09a394547c41e0" +dependencies = [ + "chrono", + "log", + "termcolor", +] + [[package]] name = "strsim" version = "0.10.0" diff --git a/Cargo.toml b/Cargo.toml index 54214f0..0be6afa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,8 @@ isatty = "0.1" derive_builder = "0.10.0" regex = "1.4.5" chrono = "0.4" +simplelog = "^0.10.0" +log = "*" [dev-dependencies] float_eq = "0.5.0" diff --git a/src/app.rs b/src/app.rs index 87f6873..2fc6efc 100644 --- a/src/app.rs +++ b/src/app.rs @@ -133,6 +133,7 @@ pub fn get_app() -> App<'static> { .long("color") .about("Use colors in the output") .possible_values(&["auto", "no", "yes"]) + .default_value("auto") .takes_value(true), ) .arg( diff --git a/src/main.rs b/src/main.rs index 74689a3..763a30c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,12 +3,15 @@ use std::env; use isatty::stdout_isatty; use regex::Regex; -use yansi::Color::{Red, Yellow}; use yansi::Paint; #[macro_use] extern crate derive_builder; +#[macro_use] +extern crate log; +use simplelog::{ColorChoice, ConfigBuilder, LevelFilter, TermLogger, TerminalMode}; + mod app; mod dateparser; mod histogram; @@ -18,9 +21,13 @@ mod reader; mod stats; mod timehist; -fn disable_color_if_needed(option: &str) { +fn configure_output(option: &str, verbose: bool) { + let mut color_choice = ColorChoice::Auto; match option { - "no" => Paint::disable(), + "no" => { + Paint::disable(); + color_choice = ColorChoice::Never; + } "auto" => match env::var("TERM") { Ok(value) if value == "dumb" => Paint::disable(), _ => { @@ -29,21 +36,35 @@ fn disable_color_if_needed(option: &str) { } } }, + "yes" => { + color_choice = ColorChoice::Always; + } _ => (), - } + }; + TermLogger::init( + if verbose { + LevelFilter::Info + } else { + LevelFilter::Debug + }, + ConfigBuilder::new() + .set_time_level(LevelFilter::Trace) + .set_thread_level(LevelFilter::Trace) + .set_target_level(LevelFilter::Trace) + .build(), + TerminalMode::Stderr, + color_choice, + ) + .unwrap(); } -fn get_reader(matches: &ArgMatches, verbose: bool) -> reader::DataReader { +fn get_reader(matches: &ArgMatches) -> reader::DataReader { let mut builder = reader::DataReaderBuilder::default(); - builder.verbose(verbose); if matches.is_present("min") || matches.is_present("max") { let min = matches.value_of_t("min").unwrap_or(f64::NEG_INFINITY); let max = matches.value_of_t("max").unwrap_or(f64::INFINITY); if min > max { - eprintln!( - "[{}] Minimum should be smaller than maximum", - Red.paint("ERROR") - ); + error!("Minimum should be smaller than maximum"); std::process::exit(1); } builder.range(min..max); @@ -54,7 +75,7 @@ fn get_reader(matches: &ArgMatches, verbose: bool) -> reader::DataReader { builder.regex(re); } _ => { - eprintln!("[{}]: Failed to parse regex {}", Red.paint("ERROR"), string); + error!("Failed to parse regex {}", string); std::process::exit(1); } }; @@ -62,11 +83,11 @@ fn get_reader(matches: &ArgMatches, verbose: bool) -> reader::DataReader { builder.build().unwrap() } -fn histogram(matches: &ArgMatches, verbose: bool) { - let reader = get_reader(&matches, verbose); +fn histogram(matches: &ArgMatches) { + let reader = get_reader(&matches); let vec = reader.read(matches.value_of("input").unwrap()); if vec.is_empty() { - eprintln!("[{}] No data to process", Yellow.paint("WARN")); + warn!("No data to process"); std::process::exit(0); } let stats = stats::Stats::new(&vec); @@ -77,14 +98,14 @@ fn histogram(matches: &ArgMatches, verbose: bool) { let mut histogram = histogram::Histogram::new(intervals, (stats.max - stats.min) / intervals as f64, stats); histogram.load(&vec); - println!("{:width$}", histogram, width = width); + print!("{:width$}", histogram, width = width); } -fn plot(matches: &ArgMatches, verbose: bool) { - let reader = get_reader(&matches, verbose); +fn plot(matches: &ArgMatches) { + let reader = get_reader(&matches); let vec = reader.read(matches.value_of("input").unwrap()); if vec.is_empty() { - eprintln!("[{}] No data to process", Yellow.paint("WARN")); + warn!("No data to process"); std::process::exit(0); } let mut plot = plot::Plot::new( @@ -117,7 +138,7 @@ fn timehist(matches: &ArgMatches) { builder.regex(re); } _ => { - eprintln!("[{}]: Failed to parse regex {}", Red.paint("ERROR"), string); + error!("Failed to parse regex {}", string); std::process::exit(1); } }; @@ -129,7 +150,7 @@ fn timehist(matches: &ArgMatches) { let reader = builder.build().unwrap(); let vec = reader.read(matches.value_of("input").unwrap()); if vec.len() <= 1 { - eprintln!("[{}] Not enough data to process", Yellow.paint("WARN")); + warn!("Not enough data to process"); std::process::exit(0); } let mut timehist = timehist::TimeHistogram::new(matches.value_of_t("intervals").unwrap(), &vec); @@ -140,16 +161,16 @@ fn timehist(matches: &ArgMatches) { fn main() { let matches = app::get_app().get_matches(); - let verbose = matches.is_present("verbose"); - if let Some(c) = matches.value_of("color") { - disable_color_if_needed(c); - } + configure_output( + matches.value_of("color").unwrap(), + matches.is_present("verbose"), + ); match matches.subcommand() { Some(("hist", subcommand_matches)) => { - histogram(subcommand_matches, verbose); + histogram(subcommand_matches); } Some(("plot", subcommand_matches)) => { - plot(subcommand_matches, verbose); + plot(subcommand_matches); } Some(("matches", subcommand_matches)) => { matchbar(subcommand_matches); diff --git a/src/reader.rs b/src/reader.rs index 66aadcf..5ad00a2 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -4,7 +4,6 @@ use std::ops::Range; use chrono::{DateTime, FixedOffset}; use regex::Regex; -use yansi::Color::{Magenta, Red}; use crate::dateparser::LogDateParser; use crate::matchbar::{MatchBar, MatchBarRow}; @@ -15,8 +14,6 @@ pub struct DataReader { range: Option>, #[builder(setter(strip_option), default)] regex: Option, - #[builder(default)] - verbose: bool, } impl DataReader { @@ -40,7 +37,7 @@ impl DataReader { } } } - Err(error) => eprintln!("[{}]: {}", Red.paint("ERROR"), error), + Err(error) => error!("{}", error), } } vec @@ -50,14 +47,7 @@ impl DataReader { match line.parse::() { Ok(n) => Some(n), Err(parse_error) => { - if self.verbose { - eprintln!( - "[{}] Cannot parse float ({}) at '{}'", - Red.paint("ERROR"), - parse_error, - line - ); - } + debug!("Cannot parse float ({}) at '{}'", parse_error, line); None } } @@ -75,13 +65,7 @@ impl DataReader { } } None => { - if self.verbose { - eprintln!( - "[{}] Regex does not match '{}'", - Magenta.paint("DEBUG"), - line - ); - } + debug!("Regex does not match '{}'", line); None } } @@ -99,7 +83,7 @@ impl DataReader { row.inc_if_matches(&as_string); } } - Err(error) => eprintln!("[{}]: {}", Red.paint("ERROR"), error), + Err(error) => error!("{}", error), } } MatchBar::new(rows) @@ -121,7 +105,7 @@ impl TimeReader { let first_line = match iterator.next() { Some(Ok(as_string)) => as_string, Some(Err(error)) => { - eprintln!("[{}]: {}", Red.paint("ERROR"), error); + error!("{}", error); return vec; } _ => return vec, @@ -130,22 +114,14 @@ impl TimeReader { Some(ts_format) => match LogDateParser::new_with_format(&first_line, &ts_format) { Ok(p) => p, Err(error) => { - eprintln!( - "[{}]: Could not figure out parsing strategy: {}", - Red.paint("ERROR"), - error - ); + error!("Could not figure out parsing strategy: {}", error); return vec; } }, None => match LogDateParser::new_with_guess(&first_line) { Ok(p) => p, Err(error) => { - eprintln!( - "[{}]: Could not figure out parsing strategy: {}", - Red.paint("ERROR"), - error - ); + error!("Could not figure out parsing strategy: {}", error); return vec; } }, @@ -166,7 +142,7 @@ impl TimeReader { } } } - Err(error) => eprintln!("[{}]: {}", Red.paint("ERROR"), error), + Err(error) => error!("{}", error), } } vec @@ -179,12 +155,7 @@ fn open_file(path: &str) -> Box { _ => match File::open(path) { Ok(fd) => Box::new(io::BufReader::new(fd)), Err(error) => { - eprintln!( - "[{}] Could not open {}: {}", - Red.paint("ERROR"), - path, - error - ); + error!("Could not open {}: {}", path, error); std::process::exit(0); } }, diff --git a/src/timehist.rs b/src/timehist.rs index c0e498a..9d247b9 100644 --- a/src/timehist.rs +++ b/src/timehist.rs @@ -106,13 +106,6 @@ impl fmt::Display for TimeHistogram { )?; let fmt = self.date_fmt_string(); for row in self.vec.iter() { - // println!("ROW"); - // println!("COUNT {}", row.count); - // println!("WIDTH {}", row.count / divisor); - // println!("WIDTH2 {:A