Skip to content

Commit

Permalink
Added new logging mechanism
Browse files Browse the repository at this point in the history
* Changed logging format
* Added flag to specify file which will be used for logs
  • Loading branch information
bluebears-dev committed Dec 25, 2020
1 parent 526e18f commit d53e293
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 49 deletions.
94 changes: 59 additions & 35 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ path = "src/bin/main.rs"
[dependencies]
rand = "0.7.3"
log = "0.4"
pretty_env_logger = "0.4.0"
fern = { version = "0.6", features = ["colored"] }
chrono = "0.4"
clap = "3.0.0-beta.2"
regex = "1.4.2"
colored = "2"
Expand Down
31 changes: 22 additions & 9 deletions src/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use ptero::{
encoder::{encode_command, EncodeSubCommand},
writer::get_writer,
},
log::{get_file_logger, get_stdout_logger, verbosity_to_level_filter},
};

const BANNER: &str = r#"
Expand Down Expand Up @@ -48,8 +49,15 @@ struct Opts {
/// By default only error logs are printed.
#[clap(short, parse(from_occurrences))]
verbose: u8,
/// If present, will print the output of the CLI in JSON format that can be further parsed by other tooling.
#[clap(long)]
json: bool,
/// Path to log file.
///
/// By default CLI won't save any logs. If this param is used, CLI will append new logs at the end of the file
/// pointed by the path. It is not affected by the `verbose` flag, and saves all the entries (starting from `TRACE`).
#[clap(long)]
log_file: Option<String>,
}

#[derive(Clap)]
Expand All @@ -62,16 +70,21 @@ enum SubCommand {
GetCapacity(GetCapacityCommand),
}

fn enable_logging(verbose: u8) {
let logging_level = match verbose {
0 => "info",
1 => "debug",
_ => "trace",
fn enable_logging(
verbose: u8,
log_path: Option<String>,
) -> std::result::Result<(), Box<dyn Error>> {
let level_filter = verbosity_to_level_filter(verbose);
let mut log_builder = fern::Dispatch::new().chain(get_stdout_logger(level_filter));

log_builder = if let Some(path) = log_path {
log_builder.chain(get_file_logger(&path))
} else {
log_builder
};

pretty_env_logger::formatted_builder()
.parse_filters(logging_level)
.init();
log_builder.apply()?;
Ok(())
}

fn run_subcommand(subcommand: SubCommand) -> Result<String, Box<dyn Error>> {
Expand All @@ -95,7 +108,7 @@ fn run_subcommand(subcommand: SubCommand) -> Result<String, Box<dyn Error>> {
fn main() -> Result<(), Box<dyn Error>> {
let opts: Opts = Opts::parse();

enable_logging(opts.verbose);
enable_logging(opts.verbose, opts.log_file)?;
let writer = get_writer(opts.json);
writer.message(BANNER);

Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ pub mod context;
/// Module containing all the available methods for text steganography.
pub mod method;

/// Logger utilities.
pub mod log;

pub mod cli {
pub mod capacity;
pub mod decoder;
Expand Down
103 changes: 103 additions & 0 deletions src/log.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use fern::{
colors::{Color, ColoredLevelConfig},
Dispatch,
};
use log::{Level, LevelFilter};

/// Converts verbosity number to [LevelFilter](log::LevelFilter) enum.
/// Used for configuring the logging level.
/// # Arguments
///
/// * `verbosity` - verbosity level described by number `u8`
///
/// # Examples
/// ## Converts verbosity number
/// ```
/// use ptero::log::verbosity_to_level_filter;
/// use log::{LevelFilter};
///
/// assert_eq!(verbosity_to_level_filter(0), LevelFilter::Info);
/// assert_eq!(verbosity_to_level_filter(1), LevelFilter::Debug);
/// assert_eq!(verbosity_to_level_filter(2), LevelFilter::Trace);
/// ```
/// ## Unrecognized verbosity defaults to trace
/// ```
/// use ptero::log::verbosity_to_level_filter;
/// use log::{LevelFilter};
///
/// assert_eq!(verbosity_to_level_filter(3), LevelFilter::Trace);
/// assert_eq!(verbosity_to_level_filter(100), LevelFilter::Trace);
/// assert_eq!(verbosity_to_level_filter(255), LevelFilter::Trace);
/// ```
pub fn verbosity_to_level_filter(verbosity: u8) -> LevelFilter {
match verbosity {
0 => LevelFilter::Info,
1 => LevelFilter::Debug,
_ => LevelFilter::Trace,
}
}

/// Returns pre-configured [ColoredLevelConfig](fern::colors::ColoredLevelConfig) used to color
/// logging level.
fn get_logging_colors() -> ColoredLevelConfig {
ColoredLevelConfig::new()
.error(Color::Red)
.warn(Color::BrightYellow)
.debug(Color::Magenta)
.trace(Color::BrightBlack)
}

/// Returns text which will be shown before the message. Used only in stdout formatter.
fn get_level_text(level: &Level) -> &str {
match level {
Level::Error => "ERROR",
Level::Warn => " WARN",
Level::Info => " ",
Level::Debug => "DEBUG",
Level::Trace => "TRACE",
}
}

/// Returns pre-configured stdout logger.
/// It only shows info relevant to user like message and logging level.
/// Uses coloring unlike file logger.
///
/// # Arguments
/// * `log_level` - level filter which is used to restrict amount of logs to user
pub fn get_stdout_logger(log_level: LevelFilter) -> Dispatch {
let colors = get_logging_colors();

fern::Dispatch::new()
.format(move |out, message, record| {
out.finish(format_args!(
"{color_line}{level_txt}\x1B[0m {message}",
level_txt = get_level_text(&record.level()),
color_line =
format_args!("\x1B[{}m", colors.get_color(&record.level()).to_fg_str()),
message = message,
));
})
.level(log_level)
.chain(std::io::stderr())
}

/// Returns pre-configured file logger.
/// This logger does not used coloring and adds additional info like date time or module path.
/// It doesn't restrict logging - saves everything beginning from `TRACE` level.
///
/// # Arguments
/// * `log_path` - path to the log file which will be used to store logs
pub fn get_file_logger(log_path: &str) -> Dispatch {
fern::Dispatch::new()
.format(move |out, message, record| {
out.finish(format_args!(
"{}[{}][{}] - {}",
chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"),
&record.target(),
&record.level(),
message,
));
})
.level(LevelFilter::Trace)
.chain(fern::log_file(&log_path).unwrap())
}
Loading

0 comments on commit d53e293

Please sign in to comment.