reth_node_core/args/
log.rs

1//! clap [Args](clap::Args) for logging configuration.
2
3use crate::dirs::{LogsDir, PlatformPath};
4use clap::{ArgAction, Args, ValueEnum};
5use reth_tracing::{
6    tracing_subscriber::filter::Directive, FileInfo, FileWorkerGuard, LayerInfo, LogFormat,
7    RethTracer, Tracer,
8};
9use std::{fmt, fmt::Display};
10use tracing::{level_filters::LevelFilter, Level};
11/// Constant to convert megabytes to bytes
12const MB_TO_BYTES: u64 = 1024 * 1024;
13
14/// The log configuration.
15#[derive(Debug, Args)]
16#[command(next_help_heading = "Logging")]
17pub struct LogArgs {
18    /// The format to use for logs written to stdout.
19    #[arg(long = "log.stdout.format", value_name = "FORMAT", global = true, default_value_t = LogFormat::Terminal)]
20    pub log_stdout_format: LogFormat,
21
22    /// The filter to use for logs written to stdout.
23    #[arg(long = "log.stdout.filter", value_name = "FILTER", global = true, default_value = "")]
24    pub log_stdout_filter: String,
25
26    /// The format to use for logs written to the log file.
27    #[arg(long = "log.file.format", value_name = "FORMAT", global = true, default_value_t = LogFormat::Terminal)]
28    pub log_file_format: LogFormat,
29
30    /// The filter to use for logs written to the log file.
31    #[arg(long = "log.file.filter", value_name = "FILTER", global = true, default_value = "debug")]
32    pub log_file_filter: String,
33
34    /// The path to put log files in.
35    #[arg(long = "log.file.directory", value_name = "PATH", global = true, default_value_t)]
36    pub log_file_directory: PlatformPath<LogsDir>,
37
38    /// The maximum size (in MB) of one log file.
39    #[arg(long = "log.file.max-size", value_name = "SIZE", global = true, default_value_t = 200)]
40    pub log_file_max_size: u64,
41
42    /// The maximum amount of log files that will be stored. If set to 0, background file logging
43    /// is disabled.
44    #[arg(long = "log.file.max-files", value_name = "COUNT", global = true, default_value_t = 5)]
45    pub log_file_max_files: usize,
46
47    /// Write logs to journald.
48    #[arg(long = "log.journald", global = true)]
49    pub journald: bool,
50
51    /// The filter to use for logs written to journald.
52    #[arg(
53        long = "log.journald.filter",
54        value_name = "FILTER",
55        global = true,
56        default_value = "error"
57    )]
58    pub journald_filter: String,
59
60    /// Sets whether or not the formatter emits ANSI terminal escape codes for colors and other
61    /// text formatting.
62    #[arg(
63        long,
64        value_name = "COLOR",
65        global = true,
66        default_value_t = ColorMode::Always
67    )]
68    pub color: ColorMode,
69    /// The verbosity settings for the tracer.
70    #[command(flatten)]
71    pub verbosity: Verbosity,
72}
73
74impl LogArgs {
75    /// Creates a [`LayerInfo`] instance.
76    fn layer(&self, format: LogFormat, filter: String, use_color: bool) -> LayerInfo {
77        LayerInfo::new(
78            format,
79            self.verbosity.directive().to_string(),
80            filter,
81            use_color.then(|| self.color.to_string()),
82        )
83    }
84
85    /// File info from the current log options.
86    fn file_info(&self) -> FileInfo {
87        FileInfo::new(
88            self.log_file_directory.clone().into(),
89            self.log_file_max_size * MB_TO_BYTES,
90            self.log_file_max_files,
91        )
92    }
93
94    /// Initializes tracing with the configured options from cli args.
95    ///
96    /// Returns the file worker guard, and the file name, if a file worker was configured.
97    pub fn init_tracing(&self) -> eyre::Result<Option<FileWorkerGuard>> {
98        let mut tracer = RethTracer::new();
99
100        let stdout = self.layer(self.log_stdout_format, self.log_stdout_filter.clone(), true);
101        tracer = tracer.with_stdout(stdout);
102
103        if self.journald {
104            tracer = tracer.with_journald(self.journald_filter.clone());
105        }
106
107        if self.log_file_max_files > 0 {
108            let info = self.file_info();
109            let file = self.layer(self.log_file_format, self.log_file_filter.clone(), false);
110            tracer = tracer.with_file(file, info);
111        }
112
113        let guard = tracer.init()?;
114        Ok(guard)
115    }
116}
117
118/// The color mode for the cli.
119#[derive(Debug, Copy, Clone, ValueEnum, Eq, PartialEq)]
120pub enum ColorMode {
121    /// Colors on
122    Always,
123    /// Colors on
124    Auto,
125    /// Colors off
126    Never,
127}
128
129impl Display for ColorMode {
130    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131        match self {
132            Self::Always => write!(f, "always"),
133            Self::Auto => write!(f, "auto"),
134            Self::Never => write!(f, "never"),
135        }
136    }
137}
138
139/// The verbosity settings for the cli.
140#[derive(Debug, Copy, Clone, Args)]
141#[command(next_help_heading = "Display")]
142pub struct Verbosity {
143    /// Set the minimum log level.
144    ///
145    /// -v      Errors
146    /// -vv     Warnings
147    /// -vvv    Info
148    /// -vvvv   Debug
149    /// -vvvvv  Traces (warning: very verbose!)
150    #[arg(short, long, action = ArgAction::Count, global = true, default_value_t = 3, verbatim_doc_comment, help_heading = "Display")]
151    verbosity: u8,
152
153    /// Silence all log output.
154    #[arg(long, alias = "silent", short = 'q', global = true, help_heading = "Display")]
155    quiet: bool,
156}
157
158impl Verbosity {
159    /// Get the corresponding [Directive] for the given verbosity, or none if the verbosity
160    /// corresponds to silent.
161    pub fn directive(&self) -> Directive {
162        if self.quiet {
163            LevelFilter::OFF.into()
164        } else {
165            let level = match self.verbosity - 1 {
166                0 => Level::ERROR,
167                1 => Level::WARN,
168                2 => Level::INFO,
169                3 => Level::DEBUG,
170                _ => Level::TRACE,
171            };
172
173            level.into()
174        }
175    }
176}