diff --git a/Cargo.lock b/Cargo.lock index 1dda817059975..637144ff15319 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1529,6 +1529,22 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "flexi_logger" +version = "0.15.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc9007a256db1f2750da1e050d04a4536bdd380929c8f0aa1a4b280f45a925a" +dependencies = [ + "atty", + "chrono", + "glob 0.3.0", + "lazy_static", + "log", + "regex", + "thiserror", + "yansi", +] + [[package]] name = "fnv" version = "1.0.6" @@ -6200,8 +6216,8 @@ dependencies = [ "atty", "chrono", "derive_more", - "env_logger 0.7.1", "fdlimit", + "flexi_logger", "futures 0.3.5", "lazy_static", "log", @@ -9410,7 +9426,7 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bfd5b7557925ce778ff9b9ef90e3ade34c524b5ff10e239c69a42d546d2af56" dependencies = [ - "rand 0.7.3", + "rand 0.3.23", ] [[package]] @@ -10008,6 +10024,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "yansi" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71" + [[package]] name = "zeroize" version = "1.1.0" diff --git a/bin/node/bench/src/main.rs b/bin/node/bench/src/main.rs index 96ef1d920c1f5..da6a1d066b5a8 100644 --- a/bin/node/bench/src/main.rs +++ b/bin/node/bench/src/main.rs @@ -79,7 +79,7 @@ fn main() { let opt = Opt::from_args(); if !opt.json { - sc_cli::init_logger(""); + sc_cli::init_logger("", None).expect("init_logger should not fail."); } let mut import_benchmarks = Vec::new(); diff --git a/client/cli/Cargo.toml b/client/cli/Cargo.toml index a63b371b70ac6..1c06d2eee3be7 100644 --- a/client/cli/Cargo.toml +++ b/client/cli/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] derive_more = "0.99.2" -env_logger = "0.7.0" +flexi_logger = "0.15.9" log = "0.4.8" atty = "0.2.13" regex = "1.3.1" diff --git a/client/cli/src/config.rs b/client/cli/src/config.rs index efda45a0eca9a..ed715d49e9e3f 100644 --- a/client/cli/src/config.rs +++ b/client/cli/src/config.rs @@ -21,9 +21,10 @@ use crate::arg_enums::Database; use crate::error::Result; use crate::{ - init_logger, DatabaseParams, ImportParams, KeystoreParams, NetworkParams, NodeKeyParams, + DatabaseParams, ImportParams, KeystoreParams, NetworkParams, NodeKeyParams, OffchainWorkerParams, PruningParams, SharedParams, SubstrateCli, }; +use crate::logger::{LogRotationOpt, init_logger}; use names::{Generator, Name}; use sc_client_api::execution_extensions::ExecutionStrategies; use sc_service::config::{ @@ -488,6 +489,13 @@ pub trait CliConfiguration: Sized { Ok(self.shared_params().log_filters().join(",")) } + /// Get the log directory for logging. + /// + /// By default this is retrieved from `SharedParams`. + fn log_rotation_opt(&self) -> Result { + Ok(self.shared_params().log_rotation_opt().clone()) + } + /// Initialize substrate. This must be done only once. /// /// This method: @@ -497,11 +505,12 @@ pub trait CliConfiguration: Sized { /// 3. Initialize the logger fn init(&self) -> Result<()> { let logger_pattern = self.log_filters()?; + let log_rotation_opt = self.log_rotation_opt()?; sp_panic_handler::set(&C::support_url(), &C::impl_version()); fdlimit::raise_fd_limit(); - init_logger(&logger_pattern); + let _ = init_logger(&logger_pattern, Some(log_rotation_opt)).map_err(|e| eprintln!("{}", e)); Ok(()) } diff --git a/client/cli/src/error.rs b/client/cli/src/error.rs index f091354be154b..f29b59ed1243e 100644 --- a/client/cli/src/error.rs +++ b/client/cli/src/error.rs @@ -17,6 +17,7 @@ // along with this program. If not, see . //! Initialization errors. +use flexi_logger::FlexiLoggerError; /// Result type alias for the CLI. pub type Result = std::result::Result; @@ -32,6 +33,8 @@ pub enum Error { Service(sc_service::Error), /// Client error Client(sp_blockchain::Error), + /// Flexi Logger error + FlexiLogger(FlexiLoggerError), /// Input error #[from(ignore)] Input(String), @@ -65,6 +68,7 @@ impl std::error::Error for Error { Error::Cli(ref err) => Some(err), Error::Service(ref err) => Some(err), Error::Client(ref err) => Some(err), + Error::FlexiLogger(ref err) => Some(err), Error::Input(_) => None, Error::InvalidListenMultiaddress => None, Error::Other(_) => None, diff --git a/client/cli/src/lib.rs b/client/cli/src/lib.rs index c7f48d2721468..a06e48626f415 100644 --- a/client/cli/src/lib.rs +++ b/client/cli/src/lib.rs @@ -27,15 +27,13 @@ mod config; mod error; mod params; mod runner; +mod logger; pub use arg_enums::*; pub use commands::*; pub use config::*; pub use error::*; -use lazy_static::lazy_static; -use log::info; pub use params::*; -use regex::Regex; pub use runner::*; use sc_service::{Configuration, TaskExecutor}; pub use sc_service::{ChainSpec, Role}; @@ -46,6 +44,7 @@ use structopt::{ clap::{self, AppSettings}, StructOpt, }; +pub use crate::logger::{init_logger, LogRotationOpt}; /// Substrate client CLI /// @@ -227,79 +226,3 @@ pub trait SubstrateCli: Sized { /// Native runtime version. fn native_runtime_version(chain_spec: &Box) -> &'static RuntimeVersion; } - -/// Initialize the logger -pub fn init_logger(pattern: &str) { - use ansi_term::Colour; - - let mut builder = env_logger::Builder::new(); - // Disable info logging by default for some modules: - builder.filter(Some("ws"), log::LevelFilter::Off); - builder.filter(Some("yamux"), log::LevelFilter::Off); - builder.filter(Some("hyper"), log::LevelFilter::Warn); - builder.filter(Some("cranelift_wasm"), log::LevelFilter::Warn); - // Always log the special target `sc_tracing`, overrides global level - builder.filter(Some("sc_tracing"), log::LevelFilter::Info); - // Enable info for others. - builder.filter(None, log::LevelFilter::Info); - - if let Ok(lvl) = std::env::var("RUST_LOG") { - builder.parse_filters(&lvl); - } - - builder.parse_filters(pattern); - let isatty = atty::is(atty::Stream::Stderr); - let enable_color = isatty; - - builder.format(move |buf, record| { - let now = time::now(); - let timestamp = - time::strftime("%Y-%m-%d %H:%M:%S", &now).expect("Error formatting log timestamp"); - - let mut output = if log::max_level() <= log::LevelFilter::Info { - format!( - "{} {}", - Colour::Black.bold().paint(timestamp), - record.args(), - ) - } else { - let name = ::std::thread::current() - .name() - .map_or_else(Default::default, |x| { - format!("{}", Colour::Blue.bold().paint(x)) - }); - let millis = (now.tm_nsec as f32 / 1000000.0).floor() as usize; - let timestamp = format!("{}.{:03}", timestamp, millis); - format!( - "{} {} {} {} {}", - Colour::Black.bold().paint(timestamp), - name, - record.level(), - record.target(), - record.args() - ) - }; - - if !isatty && record.level() <= log::Level::Info && atty::is(atty::Stream::Stdout) { - // duplicate INFO/WARN output to console - println!("{}", output); - } - - if !enable_color { - output = kill_color(output.as_ref()); - } - - writeln!(buf, "{}", output) - }); - - if builder.try_init().is_err() { - info!("💬 Not registering Substrate logger, as there is already a global logger registered!"); - } -} - -fn kill_color(s: &str) -> String { - lazy_static! { - static ref RE: Regex = Regex::new("\x1b\\[[^m]+m").expect("Error initializing color regex"); - } - RE.replace_all(s, "").to_string() -} diff --git a/client/cli/src/logger.rs b/client/cli/src/logger.rs index 10e44098f0f16..b68a37787ee59 100644 --- a/client/cli/src/logger.rs +++ b/client/cli/src/logger.rs @@ -25,7 +25,7 @@ use lazy_static::lazy_static; use regex::Regex; use std::path::PathBuf; use structopt::StructOpt; -use crate::error::{Error, Result}; +use crate::error::{Result}; type IoResult = std::result::Result<(), std::io::Error>; @@ -38,20 +38,20 @@ pub struct LogRotationOpt { /// Specify the path of the directory which will contain the log files. /// Defaults to never rotating logs. #[structopt(long, parse(from_os_str))] - log_directory: Option, + pub log_directory: Option, /// Rotate the log file when the local clock has started a new day/hour/minute/second /// since the current file has been created. #[structopt(long, - conflicts_with("log-size"), + requires("log-directory"), possible_values(&["day", "hour", "minute", "second"]), parse(from_str = age_from_str)) ] - log_age: Option, + pub log_age: Option, /// Rotate the log file when it exceeds this size (in bytes). - #[structopt(long, conflicts_with("log-age"))] - log_size: Option, + #[structopt(long, requires("log-directory"))] + pub log_size: Option, } /// Utility for parsing an Age from a &str. @@ -175,9 +175,9 @@ pub fn init_logger( let criterion = match (age, size) { (Some(a), None) => Criterion::Age(a), (None, Some(s)) => Criterion::Size(s), + (Some(age), Some(size)) => Criterion::AgeOrSize(age, size), // Default to rotating with a size of `DEFAULT_ROTATION_SIZE`. (None, None) => Criterion::Size(DEFAULT_ROTATION_SIZE), - _ => return Err(Error::Input("Only one of Age or Size should be defined".into())) }; let isatty_stderr = atty::is(atty::Stream::Stderr); @@ -248,25 +248,15 @@ mod tests { #[test] fn logger_default() { - let pattern = ""; + let pattern = "sub-authority-discovery=trace"; let log_rotation_opt = LogRotationOpt { log_directory: None, log_age: None, log_size: None, }; + let res = init_logger(pattern, Some(log_rotation_opt)); - assert!(init_logger(pattern, Some(log_rotation_opt)).is_ok()); - } - - #[test] - fn logger_conflicting_opt() { - let pattern = ""; - let log_rotation_opt = LogRotationOpt { - log_directory: None, - log_age: Some(Age::Day), - log_size: Some(1337), - }; - - assert!(init_logger(pattern, Some(log_rotation_opt)).is_err()); + dbg!(&res); + assert!(res.is_ok()); } } diff --git a/client/cli/src/params/shared_params.rs b/client/cli/src/params/shared_params.rs index ad9ab04070563..0536a20b6010b 100644 --- a/client/cli/src/params/shared_params.rs +++ b/client/cli/src/params/shared_params.rs @@ -19,6 +19,7 @@ use sc_service::config::BasePath; use std::path::PathBuf; use structopt::StructOpt; +use crate::logger::LogRotationOpt; /// Shared parameters used by all `CoreParams`. #[derive(Debug, StructOpt)] @@ -41,6 +42,10 @@ pub struct SharedParams { /// By default, all targets log `info`. The global log level can be set with -l. #[structopt(short = "l", long, value_name = "LOG_PATTERN")] pub log: Vec, + + #[allow(missing_docs)] + #[structopt(flatten)] + pub log_rotation_opt: LogRotationOpt, } impl SharedParams { @@ -72,4 +77,9 @@ impl SharedParams { pub fn log_filters(&self) -> &[String] { &self.log } + + /// Get the file rotation options for the logging + pub fn log_rotation_opt(&self) -> &LogRotationOpt { + &self.log_rotation_opt + } }