Skip to content

Commit

Permalink
Json structured logs (#5784)
Browse files Browse the repository at this point in the history
Co-authored-by: Alexey Shekhirin <a.shekhirin@gmail.com>
  • Loading branch information
loocapro and shekhirin authored Jan 9, 2024
1 parent e122773 commit 8078c51
Show file tree
Hide file tree
Showing 7 changed files with 591 additions and 210 deletions.
28 changes: 28 additions & 0 deletions Cargo.lock

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

157 changes: 101 additions & 56 deletions bin/reth/src/args/log_args.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,41 @@
//! clap [Args](clap::Args) for logging configuration.

use crate::dirs::{LogsDir, PlatformPath};
use clap::{Args, ValueEnum};
use clap::{ArgAction, Args, ValueEnum};
use reth_tracing::{
tracing_subscriber::{registry::LookupSpan, EnvFilter},
BoxedLayer, FileWorkerGuard,
tracing_subscriber::filter::Directive, FileInfo, FileWorkerGuard, LayerInfo, LogFormat,
RethTracer, Tracer,
};
use std::{fmt, fmt::Display};
use tracing::Subscriber;

/// Default [directives](reth_tracing::tracing_subscriber::filter::Directive) for [EnvFilter] which
/// disables high-frequency debug logs from `hyper` and `trust-dns`
const DEFAULT_ENV_FILTER_DIRECTIVES: [&str; 3] =
["hyper::proto::h1=off", "trust_dns_proto=off", "atrust_dns_resolver=off"];

use tracing::{level_filters::LevelFilter, Level};
/// Constant to convert megabytes to bytes
const MB_TO_BYTES: u64 = 1024 * 1024;

/// The log configuration.
#[derive(Debug, Args)]
#[command(next_help_heading = "Logging")]
pub struct LogArgs {
/// The format to use for logs written to stdout.
#[arg(long = "log.stdout.format", value_name = "FORMAT", global = true, default_value_t = LogFormat::Terminal)]
pub log_stdout_format: LogFormat,

/// The filter to use for logs written to stdout.
#[arg(
long = "log.stdout.filter",
value_name = "FILTER",
global = true,
default_value = "info"
)]
pub log_stdout_filter: String,

/// The format to use for logs written to the log file.
#[arg(long = "log.file.format", value_name = "FORMAT", global = true, default_value_t = LogFormat::Terminal)]
pub log_file_format: LogFormat,

/// The filter to use for logs written to the log file.
#[arg(long = "log.file.filter", value_name = "FILTER", global = true, default_value = "debug")]
pub log_file_filter: String,

/// The path to put log files in.
#[arg(long = "log.file.directory", value_name = "PATH", global = true, default_value_t)]
pub log_file_directory: PlatformPath<LogsDir>,
Expand All @@ -34,10 +49,6 @@ pub struct LogArgs {
#[arg(long = "log.file.max-files", value_name = "COUNT", global = true, default_value_t = 5)]
pub log_file_max_files: usize,

/// The filter to use for logs written to the log file.
#[arg(long = "log.file.filter", value_name = "FILTER", global = true, default_value = "debug")]
pub log_file_filter: String,

/// Write logs to journald.
#[arg(long = "log.journald", global = true)]
pub journald: bool,
Expand All @@ -60,55 +71,50 @@ pub struct LogArgs {
default_value_t = ColorMode::Always
)]
pub color: ColorMode,
/// The verbosity settings for the tracer.
#[clap(flatten)]
pub verbosity: Verbosity,
}

impl LogArgs {
/// Builds tracing layers from the current log options.
pub fn layers<S>(&self) -> eyre::Result<(Vec<BoxedLayer<S>>, Option<FileWorkerGuard>)>
where
S: Subscriber,
for<'a> S: LookupSpan<'a>,
{
let mut layers = Vec::new();

// Function to create a new EnvFilter with environment (from `RUST_LOG` env var), default
// (from `DEFAULT_DIRECTIVES`) and additional directives.
let create_env_filter = |additional_directives: &str| -> eyre::Result<EnvFilter> {
let env_filter = EnvFilter::builder().from_env_lossy();

DEFAULT_ENV_FILTER_DIRECTIVES
.into_iter()
.chain(additional_directives.split(','))
.try_fold(env_filter, |env_filter, directive| {
Ok(env_filter.add_directive(directive.parse()?))
})
};

// Create and add the journald layer if enabled
/// Creates a [LayerInfo] instance.
fn layer(&self, format: LogFormat, filter: String, use_color: bool) -> LayerInfo {
LayerInfo::new(
format,
filter,
self.verbosity.directive(),
if use_color { Some(self.color.to_string()) } else { None },
)
}

/// File info from the current log options.
fn file_info(&self) -> FileInfo {
FileInfo::new(
self.log_file_directory.clone().into(),
self.log_file_max_size * MB_TO_BYTES,
self.log_file_max_files,
)
}

/// Initializes tracing with the configured options from cli args.
pub fn init_tracing(&self) -> eyre::Result<Option<FileWorkerGuard>> {
let mut tracer = RethTracer::new();

let stdout = self.layer(self.log_stdout_format, self.log_stdout_filter.clone(), true);
tracer = tracer.with_stdout(stdout);

if self.journald {
let journald_filter = create_env_filter(&self.journald_filter)?;
layers.push(
reth_tracing::journald(journald_filter).expect("Could not connect to journald"),
);
tracer = tracer.with_journald(self.journald_filter.clone());
}

// Create and add the file logging layer if enabled
let file_guard = if self.log_file_max_files > 0 {
let file_filter = create_env_filter(&self.log_file_filter)?;
let (layer, guard) = reth_tracing::file(
file_filter,
&self.log_file_directory,
"reth.log",
self.log_file_max_size * MB_TO_BYTES,
self.log_file_max_files,
);
layers.push(layer);
Some(guard)
} else {
None
};
if self.log_file_max_files > 0 {
let info = self.file_info();
let file = self.layer(self.log_file_format, self.log_file_filter.clone(), false);
tracer = tracer.with_file(file, info);
}

Ok((layers, file_guard))
let guard = tracer.init()?;
Ok(guard)
}
}

Expand All @@ -132,3 +138,42 @@ impl Display for ColorMode {
}
}
}

/// The verbosity settings for the cli.
#[derive(Debug, Copy, Clone, Args)]
#[command(next_help_heading = "Display")]
pub struct Verbosity {
/// Set the minimum log level.
///
/// -v Errors
/// -vv Warnings
/// -vvv Info
/// -vvvv Debug
/// -vvvvv Traces (warning: very verbose!)
#[clap(short, long, action = ArgAction::Count, global = true, default_value_t = 3, verbatim_doc_comment, help_heading = "Display")]
verbosity: u8,

/// Silence all log output.
#[clap(long, alias = "silent", short = 'q', global = true, help_heading = "Display")]
quiet: bool,
}

impl Verbosity {
/// Get the corresponding [Directive] for the given verbosity, or none if the verbosity
/// corresponds to silent.
pub fn directive(&self) -> Directive {
if self.quiet {
LevelFilter::OFF.into()
} else {
let level = match self.verbosity - 1 {
0 => Level::ERROR,
1 => Level::WARN,
2 => Level::INFO,
3 => Level::DEBUG,
_ => Level::TRACE,
};

level.into()
}
}
}
58 changes: 3 additions & 55 deletions bin/reth/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,9 @@ use crate::{
runner::CliRunner,
version::{LONG_VERSION, SHORT_VERSION},
};
use clap::{value_parser, ArgAction, Args, Parser, Subcommand};
use clap::{value_parser, Parser, Subcommand};
use reth_primitives::ChainSpec;
use reth_tracing::{
tracing::{metadata::LevelFilter, Level},
tracing_subscriber::filter::Directive,
FileWorkerGuard,
};
use reth_tracing::FileWorkerGuard;
use std::sync::Arc;

pub mod components;
Expand Down Expand Up @@ -67,9 +63,6 @@ pub struct Cli<Ext: RethCliExt = ()> {

#[clap(flatten)]
logs: LogArgs,

#[clap(flatten)]
verbosity: Verbosity,
}

impl<Ext: RethCliExt> Cli<Ext> {
Expand Down Expand Up @@ -101,13 +94,7 @@ impl<Ext: RethCliExt> Cli<Ext> {
/// If file logging is enabled, this function returns a guard that must be kept alive to ensure
/// that all logs are flushed to disk.
pub fn init_tracing(&self) -> eyre::Result<Option<FileWorkerGuard>> {
let mut layers =
vec![reth_tracing::stdout(self.verbosity.directive(), &self.logs.color.to_string())];

let (additional_layers, guard) = self.logs.layers()?;
layers.extend(additional_layers);

reth_tracing::init(layers);
let guard = self.logs.init_tracing()?;
Ok(guard)
}

Expand Down Expand Up @@ -173,45 +160,6 @@ impl<Ext: RethCliExt> Commands<Ext> {
}
}

/// The verbosity settings for the cli.
#[derive(Debug, Copy, Clone, Args)]
#[command(next_help_heading = "Display")]
pub struct Verbosity {
/// Set the minimum log level.
///
/// -v Errors
/// -vv Warnings
/// -vvv Info
/// -vvvv Debug
/// -vvvvv Traces (warning: very verbose!)
#[clap(short, long, action = ArgAction::Count, global = true, default_value_t = 3, verbatim_doc_comment, help_heading = "Display")]
verbosity: u8,

/// Silence all log output.
#[clap(long, alias = "silent", short = 'q', global = true, help_heading = "Display")]
quiet: bool,
}

impl Verbosity {
/// Get the corresponding [Directive] for the given verbosity, or none if the verbosity
/// corresponds to silent.
pub fn directive(&self) -> Directive {
if self.quiet {
LevelFilter::OFF.into()
} else {
let level = match self.verbosity - 1 {
0 => Level::ERROR,
1 => Level::WARN,
2 => Level::INFO,
3 => Level::DEBUG,
_ => Level::TRACE,
};

format!("{level}").parse().unwrap()
}
}
}

#[cfg(test)]
mod tests {
use clap::CommandFactory;
Expand Down
5 changes: 4 additions & 1 deletion crates/tracing/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ workspace = true

[dependencies]
tracing.workspace = true
tracing-subscriber = { version = "0.3", default-features = false, features = ["env-filter", "fmt"] }
tracing-subscriber = { version = "0.3", default-features = false, features = ["env-filter", "fmt", "json"] }
tracing-appender.workspace = true
tracing-journald = "0.3"
tracing-logfmt = "0.3.3"
rolling-file = "0.2.0"
eyre.workspace = true
clap = { workspace = true, features = ["derive"] }
Loading

0 comments on commit 8078c51

Please sign in to comment.