Add log-format option to CLI (#744)

* Add log-format CLI option

* Cargo fmt

* Add log format logic for file logging. Add doc

* Review comment

* Fix compilation errors

* Remove Mutex from logger
This commit is contained in:
pscott 2020-01-06 02:26:30 +01:00 committed by Paul Hauner
parent 55680ab1d3
commit 5427664cf4
4 changed files with 55 additions and 15 deletions

View File

@ -249,7 +249,7 @@ fn run<T: EthSpec>(env_builder: EnvironmentBuilder<T>, matches: &ArgMatches) {
let env = env_builder let env = env_builder
.multi_threaded_tokio_runtime() .multi_threaded_tokio_runtime()
.expect("should start tokio runtime") .expect("should start tokio runtime")
.async_logger("trace") .async_logger("trace", None)
.expect("should start null logger") .expect("should start null logger")
.build() .build()
.expect("should build env"); .expect("should build env");

View File

@ -15,7 +15,6 @@ use std::cell::RefCell;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::fs::{rename as FsRename, OpenOptions}; use std::fs::{rename as FsRename, OpenOptions};
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Mutex;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use tokio::runtime::{Builder as RuntimeBuilder, Runtime, TaskExecutor}; use tokio::runtime::{Builder as RuntimeBuilder, Runtime, TaskExecutor};
use types::{EthSpec, InteropEthSpec, MainnetEthSpec, MinimalEthSpec}; use types::{EthSpec, InteropEthSpec, MainnetEthSpec, MinimalEthSpec};
@ -99,12 +98,27 @@ impl<E: EthSpec> EnvironmentBuilder<E> {
/// The logger is "async" because it has a dedicated thread that accepts logs and then /// The logger is "async" because it has a dedicated thread that accepts logs and then
/// asynchronously flushes them to stdout/files/etc. This means the thread that raised the log /// asynchronously flushes them to stdout/files/etc. This means the thread that raised the log
/// does not have to wait for the logs to be flushed. /// does not have to wait for the logs to be flushed.
pub fn async_logger(mut self, debug_level: &str) -> Result<Self, String> { pub fn async_logger(
// Build the initial logger. mut self,
debug_level: &str,
log_format: Option<&str>,
) -> Result<Self, String> {
// Setting up the initial logger format and building it.
let drain = if let Some(format) = log_format {
match format.to_uppercase().as_str() {
"JSON" => {
let drain = slog_json::Json::default(std::io::stdout()).fuse();
slog_async::Async::new(drain).build()
}
_ => return Err("Logging format provided is not supported".to_string()),
}
} else {
let decorator = slog_term::TermDecorator::new().build(); let decorator = slog_term::TermDecorator::new().build();
let decorator = logging::AlignedTermDecorator::new(decorator, logging::MAX_MESSAGE_WIDTH); let decorator =
logging::AlignedTermDecorator::new(decorator, logging::MAX_MESSAGE_WIDTH);
let drain = slog_term::FullFormat::new(decorator).build().fuse(); let drain = slog_term::FullFormat::new(decorator).build().fuse();
let drain = slog_async::Async::new(drain).build(); slog_async::Async::new(drain).build()
};
let drain = match debug_level { let drain = match debug_level {
"info" => drain.filter_level(Level::Info), "info" => drain.filter_level(Level::Info),
@ -230,7 +244,12 @@ impl<E: EthSpec> Environment<E> {
} }
/// Sets the logger (and all child loggers) to log to a file. /// Sets the logger (and all child loggers) to log to a file.
pub fn log_to_json_file(&mut self, path: PathBuf, debug_level: &str) -> Result<(), String> { pub fn log_to_json_file(
&mut self,
path: PathBuf,
debug_level: &str,
log_format: Option<&str>,
) -> Result<(), String> {
// Creating a backup if the logfile already exists. // Creating a backup if the logfile already exists.
if path.exists() { if path.exists() {
let start = SystemTime::now(); let start = SystemTime::now();
@ -256,8 +275,14 @@ impl<E: EthSpec> Environment<E> {
.open(&path) .open(&path)
.map_err(|e| format!("Unable to open logfile: {:?}", e))?; .map_err(|e| format!("Unable to open logfile: {:?}", e))?;
let drain = Mutex::new(slog_json::Json::default(file)).fuse(); let log_format = log_format.unwrap_or("JSON");
let drain = slog_async::Async::new(drain).build(); let drain = match log_format.to_uppercase().as_str() {
"JSON" => {
let drain = slog_json::Json::default(file).fuse();
slog_async::Async::new(drain).build()
}
_ => return Err("Logging format provided is not supported".to_string()),
};
let drain = match debug_level { let drain = match debug_level {
"info" => drain.filter_level(Level::Info), "info" => drain.filter_level(Level::Info),

View File

@ -42,7 +42,17 @@ fn main() {
Arg::with_name("logfile") Arg::with_name("logfile")
.long("logfile") .long("logfile")
.value_name("FILE") .value_name("FILE")
.help("File path where output will be written.") .help(
"File path where output will be written. Default file logging format is JSON.",
)
.takes_value(true),
)
.arg(
Arg::with_name("log-format")
.long("log-format")
.value_name("FORMAT")
.help("Specifies the format used for logging.")
.possible_values(&["JSON"])
.takes_value(true), .takes_value(true),
) )
.arg( .arg(
@ -99,8 +109,10 @@ fn run<E: EthSpec>(
.value_of("debug-level") .value_of("debug-level")
.ok_or_else(|| "Expected --debug-level flag".to_string())?; .ok_or_else(|| "Expected --debug-level flag".to_string())?;
let log_format = matches.value_of("log-format");
let mut environment = environment_builder let mut environment = environment_builder
.async_logger(debug_level)? .async_logger(debug_level, log_format)?
.multi_threaded_tokio_runtime()? .multi_threaded_tokio_runtime()?
.build()?; .build()?;
@ -110,7 +122,7 @@ fn run<E: EthSpec>(
let path = log_path let path = log_path
.parse::<PathBuf>() .parse::<PathBuf>()
.map_err(|e| format!("Failed to parse log path: {:?}", e))?; .map_err(|e| format!("Failed to parse log path: {:?}", e))?;
environment.log_to_json_file(path, debug_level)?; environment.log_to_json_file(path, debug_level, log_format)?;
} }
if std::mem::size_of::<usize>() != 8 { if std::mem::size_of::<usize>() != 8 {

View File

@ -41,6 +41,7 @@ fn main() {
let nodes = 4; let nodes = 4;
let validators_per_node = 20; let validators_per_node = 20;
let log_level = "debug"; let log_level = "debug";
let log_format = None;
let speed_up_factor = 4; let speed_up_factor = 4;
let end_after_checks = true; let end_after_checks = true;
@ -49,6 +50,7 @@ fn main() {
validators_per_node, validators_per_node,
speed_up_factor, speed_up_factor,
log_level, log_level,
log_format,
end_after_checks, end_after_checks,
) { ) {
Ok(()) => println!("Simulation exited successfully"), Ok(()) => println!("Simulation exited successfully"),
@ -64,10 +66,11 @@ fn async_sim(
validators_per_node: usize, validators_per_node: usize,
speed_up_factor: u64, speed_up_factor: u64,
log_level: &str, log_level: &str,
log_format: Option<&str>,
end_after_checks: bool, end_after_checks: bool,
) -> Result<(), String> { ) -> Result<(), String> {
let mut env = EnvironmentBuilder::minimal() let mut env = EnvironmentBuilder::minimal()
.async_logger(log_level)? .async_logger(log_level, log_format)?
.multi_threaded_tokio_runtime()? .multi_threaded_tokio_runtime()?
.build()?; .build()?;