diff --git a/Cargo.lock b/Cargo.lock index f717e6e70..c717d5852 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4839,6 +4839,7 @@ version = "0.1.0" dependencies = [ "chrono", "clap", + "global_config", "logroller", "metrics", "tracing", diff --git a/anchor/logging/Cargo.toml b/anchor/logging/Cargo.toml index 3df732336..a0bb20717 100644 --- a/anchor/logging/Cargo.toml +++ b/anchor/logging/Cargo.toml @@ -10,6 +10,7 @@ chrono = { version = "0.4", default-features = false, features = [ "std", ] } clap = { workspace = true } +global_config = { workspace = true } logroller = "0.1.8" metrics = { workspace = true } tracing = { workspace = true } diff --git a/anchor/logging/src/lib.rs b/anchor/logging/src/lib.rs index a2dcdb86e..4e389acd7 100644 --- a/anchor/logging/src/lib.rs +++ b/anchor/logging/src/lib.rs @@ -1,6 +1,218 @@ +//! Collection of logging logic for initialising Anchor. + +use std::{ + path::{Path, PathBuf}, + str::FromStr, +}; + +use clap::Parser; +use count_layer::CountLayer; +use global_config::GlobalConfig; +use logroller::{Compression, LogRollerBuilder, Rotation, RotationSize}; +use tracing::Level; +use tracing_appender::non_blocking::{NonBlocking, WorkerGuard}; +use tracing_subscriber::{EnvFilter, Layer, fmt, layer::SubscriberExt, util::SubscriberInitExt}; + +use crate::{ + tracing_libp2p_discv5_layer::create_libp2p_discv5_tracing_layer, utils::build_workspace_filter, +}; + mod count_layer; -mod logging; mod tracing_libp2p_discv5_layer; -pub mod utils; -pub use count_layer::CountLayer; -pub use logging::*; +mod utils; + +#[derive(Parser, Debug, Clone)] +pub struct FileLoggingFlags { + #[arg( + long, + global = true, + default_value_t = Level::DEBUG, + value_parser = Level::from_str, + help = "Specifies the verbosity level used when emitting logs to the log file")] + pub logfile_debug_level: Level, + + #[arg( + long, + global = true, + value_name = "SIZE", + help = "Maximum size of each log file in MB. Set to 0 to disable file logging.", + default_value_t = 50 + )] + pub logfile_max_size: u64, + + #[arg( + long, + global = true, + value_name = "NUMBER", + help = "Maximum number of log files to keep. Set to 0 to disable file logging.", + default_value_t = 100 + )] + pub logfile_max_number: u64, + + #[arg( + long, + global = true, + value_name = "DIR", + help = "Directory path where the log file will be stored" + )] + pub logfile_dir: Option, + + #[arg( + long, + global = true, + help = "If present, compress old log files. This can help reduce the space needed \ + to store old logs." + )] + pub logfile_compression: bool, + + #[arg(long, global = true, help = "Enables colors in logfile.")] + pub logfile_color: bool, + + #[arg( + long, + global = true, + default_value_t = Level::DEBUG, + value_parser = Level::from_str, + help = "Specifies the verbosity level used for the discv5 dependency log file")] + pub discv5_log_level: Level, + + #[arg( + long, + global = true, + default_value_t = Level::DEBUG, + value_parser = Level::from_str, + help = "Specifies the verbosity level used for the libp2p dependency log file. \ + Certain score penalty information is logged regardless of this setting.")] + pub libp2p_log_level: Level, +} + +impl FileLoggingFlags { + pub fn disabled_file_logging(&self) -> bool { + self.logfile_max_number == 0 || self.logfile_max_size == 0 + } +} + +pub struct LoggingLayer { + pub non_blocking_writer: NonBlocking, + pub guard: WorkerGuard, +} +impl LoggingLayer { + pub fn new(non_blocking_writer: NonBlocking, guard: WorkerGuard) -> Self { + Self { + non_blocking_writer, + guard, + } + } +} + +pub fn init_file_logging( + logs_dir: &Path, + config: &FileLoggingFlags, +) -> Result, String> { + if config.disabled_file_logging() { + return Ok(None); + } + + let filename = PathBuf::from("anchor.log"); + let mut appender = LogRollerBuilder::new(logs_dir, &filename) + .rotation(Rotation::SizeBased(RotationSize::MB( + config.logfile_max_size, + ))) + .max_keep_files(config.logfile_max_number); + + if config.logfile_compression { + appender = appender.compression(Compression::Gzip); + } + + let file_appender = appender + .build() + .map_err(|e| format!("Failed to create rolling file appender: {e}"))?; + + let (writer, guard) = tracing_appender::non_blocking(file_appender); + Ok(Some(LoggingLayer::new(writer, guard))) +} + +pub fn enable_logging( + file_logging_flags: Option<&FileLoggingFlags>, + global_config: &GlobalConfig, +) -> Result, String> { + let mut logging_layers = Vec::new(); + let mut guards = Vec::new(); + + let workspace_filter = match build_workspace_filter() { + Ok(filter) => filter, + Err(e) => { + return Err(format!("Unable to build workspace filter: {e}")); + } + }; + + logging_layers.push( + fmt::layer() + .with_filter( + EnvFilter::builder() + .with_default_directive(global_config.debug_level.into()) + .from_env_lossy(), + ) + .with_filter(workspace_filter.clone()) + .boxed(), + ); + + if let Some(file_logging_flags) = file_logging_flags { + let logs_dir = file_logging_flags + .logfile_dir + .clone() + .unwrap_or_else(|| global_config.data_dir.default_logs_dir()); + + let filter_level: Level = file_logging_flags.logfile_debug_level; + + let libp2p_discv5_layer = + create_libp2p_discv5_tracing_layer(&logs_dir, file_logging_flags)?; + let file_logging_layer = init_file_logging(&logs_dir, file_logging_flags)?; + + if let Some((libp2p_discv5_layer, layer_guards)) = libp2p_discv5_layer { + guards.extend(layer_guards); + // Create filter that reduces external library noise to separately configured levels + // while preserving the configured file log level for Anchor crates + + let default = format!( + "discv5={},libp2p_gossipsub={}", + file_logging_flags.discv5_log_level, file_logging_flags.libp2p_log_level + ); + + logging_layers.push( + libp2p_discv5_layer + .with_filter( + EnvFilter::try_from_default_env() + .unwrap_or_else(|_| EnvFilter::new(default)), + ) + .boxed(), + ); + } + + if let Some(file_logging_layer) = file_logging_layer { + guards.push(file_logging_layer.guard); + logging_layers.push( + fmt::layer() + .with_writer(file_logging_layer.non_blocking_writer) + .with_ansi(file_logging_flags.logfile_color) + .with_filter( + EnvFilter::builder() + .with_default_directive(filter_level.into()) + .from_env_lossy(), + ) + .with_filter(workspace_filter.clone()) + .boxed(), + ); + } + } + + // Add the CountLayer + logging_layers.push(CountLayer.boxed()); + + tracing_subscriber::registry() + .with(logging_layers) + .try_init() + .map_err(|e| format!("Failed to initialize logging: {e}"))?; + + Ok(guards) +} diff --git a/anchor/logging/src/logging.rs b/anchor/logging/src/logging.rs deleted file mode 100644 index f64a15804..000000000 --- a/anchor/logging/src/logging.rs +++ /dev/null @@ -1,127 +0,0 @@ -//! Collection of logging logic for initialising Anchor. -use std::{ - path::{Path, PathBuf}, - str::FromStr, -}; - -use clap::Parser; -use logroller::{Compression, LogRollerBuilder, Rotation, RotationSize}; -use tracing::Level; -use tracing_appender::non_blocking::{NonBlocking, WorkerGuard}; - -pub use crate::tracing_libp2p_discv5_layer::{ - Libp2pDiscv5TracingLayer, create_libp2p_discv5_tracing_layer, -}; - -#[derive(Parser, Debug, Clone)] -pub struct FileLoggingFlags { - #[arg( - long, - global = true, - default_value_t = Level::DEBUG, - value_parser = Level::from_str, - help = "Specifies the verbosity level used when emitting logs to the log file")] - pub logfile_debug_level: Level, - - #[arg( - long, - global = true, - value_name = "SIZE", - help = "Maximum size of each log file in MB. Set to 0 to disable file logging.", - default_value_t = 50 - )] - pub logfile_max_size: u64, - - #[arg( - long, - global = true, - value_name = "NUMBER", - help = "Maximum number of log files to keep. Set to 0 to disable file logging.", - default_value_t = 100 - )] - pub logfile_max_number: u64, - - #[arg( - long, - global = true, - value_name = "DIR", - help = "Directory path where the log file will be stored" - )] - pub logfile_dir: Option, - - #[arg( - long, - global = true, - help = "If present, compress old log files. This can help reduce the space needed \ - to store old logs." - )] - pub logfile_compression: bool, - - #[arg(long, global = true, help = "Enables colors in logfile.")] - pub logfile_color: bool, - - #[arg( - long, - global = true, - default_value_t = Level::DEBUG, - value_parser = Level::from_str, - help = "Specifies the verbosity level used for the discv5 dependency log file")] - pub discv5_log_level: Level, - - #[arg( - long, - global = true, - default_value_t = Level::DEBUG, - value_parser = Level::from_str, - help = "Specifies the verbosity level used for the libp2p dependency log file. \ - Certain score penalty information is logged regardless of this setting.")] - pub libp2p_log_level: Level, -} - -impl FileLoggingFlags { - pub fn disabled_file_logging(&self) -> bool { - self.logfile_max_number == 0 || self.logfile_max_size == 0 - } -} - -pub struct LoggingLayer { - pub non_blocking_writer: NonBlocking, - pub guard: WorkerGuard, -} -impl LoggingLayer { - pub fn new(non_blocking_writer: NonBlocking, guard: WorkerGuard) -> Self { - Self { - non_blocking_writer, - guard, - } - } -} - -pub fn init_file_logging(logs_dir: &Path, config: FileLoggingFlags) -> Option { - let filename = PathBuf::from("anchor.log"); - - if config.disabled_file_logging() { - return None; - } - - let mut appender = LogRollerBuilder::new(logs_dir, &filename) - .rotation(Rotation::SizeBased(RotationSize::MB( - config.logfile_max_size, - ))) - .max_keep_files(config.logfile_max_number); - - if config.logfile_compression { - appender = appender.compression(Compression::Gzip); - } - - match appender.build() { - Ok(file_appender) => { - let (writer, guard) = tracing_appender::non_blocking(file_appender); - Some(LoggingLayer::new(writer, guard)) - } - Err(e) => { - eprintln!("Failed to create rolling file appender: {e}"); - None - } - } -} diff --git a/anchor/logging/src/tracing_libp2p_discv5_layer.rs b/anchor/logging/src/tracing_libp2p_discv5_layer.rs index 8247c1023..20eddab20 100644 --- a/anchor/logging/src/tracing_libp2p_discv5_layer.rs +++ b/anchor/logging/src/tracing_libp2p_discv5_layer.rs @@ -1,16 +1,19 @@ -use std::{io::Write, path::PathBuf}; +use std::{ + io::Write, + path::{Path, PathBuf}, +}; use chrono::Local; -use logroller::{LogRollerBuilder, Rotation, RotationSize}; +use logroller::{Compression, LogRollerBuilder, Rotation, RotationSize}; use tracing::Subscriber; use tracing_appender::non_blocking::{NonBlocking, WorkerGuard}; use tracing_subscriber::{Layer, layer::Context}; +use crate::FileLoggingFlags; + pub struct Libp2pDiscv5TracingLayer { pub libp2p_non_blocking_writer: NonBlocking, - _libp2p_guard: WorkerGuard, pub discv5_non_blocking_writer: NonBlocking, - _discv5_guard: WorkerGuard, } impl Layer for Libp2pDiscv5TracingLayer @@ -63,57 +66,46 @@ impl tracing_core::field::Visit for LogMessageExtractor { } pub fn create_libp2p_discv5_tracing_layer( - base_tracing_log_path: Option, - max_log_size: u64, -) -> Option { - if let Some(mut tracing_log_path) = base_tracing_log_path { - // Ensure that `tracing_log_path` only contains directories. - for p in tracing_log_path.clone().iter() { - tracing_log_path = tracing_log_path.join(p); - if let Ok(metadata) = tracing_log_path.metadata() - && !metadata.is_dir() - { - tracing_log_path.pop(); - break; - } - } + logs_dir: &Path, + logging_config: &FileLoggingFlags, +) -> Result, String> { + if logging_config.disabled_file_logging() { + return Ok(None); + } - let libp2p_writer = - LogRollerBuilder::new(tracing_log_path.clone(), PathBuf::from("libp2p.log")) - .rotation(Rotation::SizeBased(RotationSize::MB(max_log_size))) - .max_keep_files(1); - - let discv5_writer = - LogRollerBuilder::new(tracing_log_path.clone(), PathBuf::from("discv5.log")) - .rotation(Rotation::SizeBased(RotationSize::MB(max_log_size))) - .max_keep_files(1); - - let libp2p_writer = match libp2p_writer.build() { - Ok(writer) => writer, - Err(e) => { - eprintln!("Failed to initialize libp2p rolling file appender: {e}"); - std::process::exit(1); - } - }; + let mut libp2p_writer = LogRollerBuilder::new(logs_dir, &PathBuf::from("libp2p.log")) + .rotation(Rotation::SizeBased(RotationSize::MB( + logging_config.logfile_max_size, + ))) + .max_keep_files(logging_config.logfile_max_number); + + let mut discv5_writer = LogRollerBuilder::new(logs_dir, &PathBuf::from("discv5.log")) + .rotation(Rotation::SizeBased(RotationSize::MB( + logging_config.logfile_max_size, + ))) + .max_keep_files(logging_config.logfile_max_number); + + if logging_config.logfile_compression { + libp2p_writer = libp2p_writer.compression(Compression::Gzip); + discv5_writer = discv5_writer.compression(Compression::Gzip); + } - let discv5_writer = match discv5_writer.build() { - Ok(writer) => writer, - Err(e) => { - eprintln!("Failed to initialize discv5 rolling file appender: {e}"); - std::process::exit(1); - } - }; + let libp2p_writer = libp2p_writer + .build() + .map_err(|e| format!("Failed to initialize libp2p rolling file appender: {e}"))?; - let (libp2p_non_blocking_writer, _libp2p_guard) = NonBlocking::new(libp2p_writer); - let (discv5_non_blocking_writer, _discv5_guard) = NonBlocking::new(discv5_writer); + let discv5_writer = discv5_writer + .build() + .map_err(|e| format!("Failed to initialize discv5 rolling file appender: {e}"))?; - Some(Libp2pDiscv5TracingLayer { + let (libp2p_non_blocking_writer, libp2p_guard) = NonBlocking::new(libp2p_writer); + let (discv5_non_blocking_writer, discv5_guard) = NonBlocking::new(discv5_writer); + + Ok(Some(( + Libp2pDiscv5TracingLayer { libp2p_non_blocking_writer, - _libp2p_guard, discv5_non_blocking_writer, - _discv5_guard, - }) - } else { - None - } + }, + [libp2p_guard, discv5_guard], + ))) } diff --git a/anchor/src/main.rs b/anchor/src/main.rs index a75d6a152..ea2d90c99 100644 --- a/anchor/src/main.rs +++ b/anchor/src/main.rs @@ -4,14 +4,8 @@ use environment::Environment; use global_config::{GlobalConfig, GlobalFlags}; use keygen::Keygen; use keysplit::Keysplit; -use logging::{ - CountLayer, FileLoggingFlags, create_libp2p_discv5_tracing_layer, init_file_logging, - utils::build_workspace_filter, -}; use task_executor::ShutdownReason; -use tracing::{Level, error, info}; -use tracing_appender::non_blocking::WorkerGuard; -use tracing_subscriber::{EnvFilter, Layer, fmt, layer::SubscriberExt, util::SubscriberInitExt}; +use tracing::{error, info}; use types::EthSpecId; mod environment; @@ -57,7 +51,7 @@ fn main() { None }; - let _guards = match enable_logging(file_logging_flags, &global_config) { + let _guards = match logging::enable_logging(file_logging_flags, &global_config) { Ok(guards) => guards, Err(err) => { eprintln!("Failed to initialize logging: {err}"); @@ -164,89 +158,3 @@ fn start_anchor(anchor_config: &Node, global_config: GlobalConfig, mut environme } }; } - -pub fn enable_logging( - file_logging_flags: Option<&FileLoggingFlags>, - global_config: &GlobalConfig, -) -> Result, String> { - let mut logging_layers = Vec::new(); - let mut guards = Vec::new(); - - let workspace_filter = match build_workspace_filter() { - Ok(filter) => filter, - Err(e) => { - return Err(format!("Unable to build workspace filter: {e}")); - } - }; - - logging_layers.push( - fmt::layer() - .with_filter( - EnvFilter::builder() - .with_default_directive(global_config.debug_level.into()) - .from_env_lossy(), - ) - .with_filter(workspace_filter.clone()) - .boxed(), - ); - - if let Some(file_logging_flags) = file_logging_flags { - let logs_dir = file_logging_flags - .logfile_dir - .clone() - .unwrap_or_else(|| global_config.data_dir.default_logs_dir()); - - let filter_level: Level = file_logging_flags.logfile_debug_level; - - let libp2p_discv5_layer = create_libp2p_discv5_tracing_layer( - Some(logs_dir.clone()), - file_logging_flags.logfile_max_size, - ); - let file_logging_layer = init_file_logging(&logs_dir, file_logging_flags.clone()); - - if let Some(libp2p_discv5_layer) = libp2p_discv5_layer { - // Create filter that reduces external library noise to separately configured levels - // while preserving the configured file log level for Anchor crates - - let default = format!( - "discv5={},libp2p_gossipsub={}", - file_logging_flags.discv5_log_level, file_logging_flags.libp2p_log_level - ); - - logging_layers.push( - libp2p_discv5_layer - .with_filter( - EnvFilter::try_from_default_env() - .unwrap_or_else(|_| EnvFilter::new(default)), - ) - .boxed(), - ); - } - - if let Some(file_logging_layer) = file_logging_layer { - guards.push(file_logging_layer.guard); - logging_layers.push( - fmt::layer() - .with_writer(file_logging_layer.non_blocking_writer) - .with_ansi(file_logging_flags.logfile_color) - .with_filter( - EnvFilter::builder() - .with_default_directive(filter_level.into()) - .from_env_lossy(), - ) - .with_filter(workspace_filter.clone()) - .boxed(), - ); - } - } - - // Add the CountLayer - logging_layers.push(CountLayer.boxed()); - - tracing_subscriber::registry() - .with(logging_layers) - .try_init() - .map_err(|e| format!("Failed to initialize logging: {e}"))?; - - Ok(guards) -}