Browse Source

Use a proper (and colored) logger for terminal

pull/2/head
JuanLeon Lahoz 5 years ago
parent
commit
3083bb4adc
  1. 22
      Cargo.lock
  2. 2
      Cargo.toml
  3. 1
      src/app.rs
  4. 73
      src/main.rs
  5. 47
      src/reader.rs
  6. 7
      src/timehist.rs

22
Cargo.lock generated

@ -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"

2
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"

1
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(

73
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);

47
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<Range<f64>>,
#[builder(setter(strip_option), default)]
regex: Option<Regex>,
#[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::<f64>() {
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<dyn io::BufRead> {
_ => 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);
}
},

7
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<width$}", "", width = row.count / divisor);
// println!("LABEL1 {}", row.start);
// println!("LABEFMT {}", self.date_fmt_string());
// println!("LABEL2 {}", row.start.format(self.date_fmt_string()));
writeln!(
f,
"[{label}] [{count}] {bar}",

Loading…
Cancel
Save