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, Layers, 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 prefix name of the log files.
39    #[arg(long = "log.file.name", value_name = "NAME", global = true, default_value = "reth.log")]
40    pub log_file_name: String,
41
42    /// The maximum size (in MB) of one log file.
43    #[arg(long = "log.file.max-size", value_name = "SIZE", global = true, default_value_t = 200)]
44    pub log_file_max_size: u64,
45
46    /// The maximum amount of log files that will be stored. If set to 0, background file logging
47    /// is disabled.
48    #[arg(long = "log.file.max-files", value_name = "COUNT", global = true, default_value_t = 5)]
49    pub log_file_max_files: usize,
50
51    /// Write logs to journald.
52    #[arg(long = "log.journald", global = true)]
53    pub journald: bool,
54
55    /// The filter to use for logs written to journald.
56    #[arg(
57        long = "log.journald.filter",
58        value_name = "FILTER",
59        global = true,
60        default_value = "error"
61    )]
62    pub journald_filter: String,
63
64    /// Emit traces to samply. Only useful when profiling.
65    #[arg(long = "log.samply", global = true, hide = true)]
66    pub samply: bool,
67
68    /// The filter to use for traces emitted to samply.
69    #[arg(
70        long = "log.samply.filter",
71        value_name = "FILTER",
72        global = true,
73        default_value = "debug",
74        hide = true
75    )]
76    pub samply_filter: String,
77
78    /// Emit traces to tracy. Only useful when profiling.
79    #[arg(long = "log.tracy", global = true, hide = true)]
80    pub tracy: bool,
81
82    /// The filter to use for traces emitted to tracy.
83    #[arg(
84        long = "log.tracy.filter",
85        value_name = "FILTER",
86        global = true,
87        default_value = "debug",
88        hide = true
89    )]
90    pub tracy_filter: String,
91
92    /// Sets whether or not the formatter emits ANSI terminal escape codes for colors and other
93    /// text formatting.
94    #[arg(
95        long,
96        value_name = "COLOR",
97        global = true,
98        default_value_t = ColorMode::Always
99    )]
100    pub color: ColorMode,
101
102    /// The verbosity settings for the tracer.
103    #[command(flatten)]
104    pub verbosity: Verbosity,
105}
106
107impl LogArgs {
108    /// Creates a [`LayerInfo`] instance.
109    fn layer_info(&self, format: LogFormat, filter: String, use_color: bool) -> LayerInfo {
110        LayerInfo::new(
111            format,
112            self.verbosity.directive().to_string(),
113            filter,
114            use_color.then(|| self.color.to_string()),
115        )
116    }
117
118    /// File info from the current log options.
119    fn file_info(&self) -> FileInfo {
120        FileInfo::new(
121            self.log_file_directory.clone().into(),
122            self.log_file_name.clone(),
123            self.log_file_max_size * MB_TO_BYTES,
124            self.log_file_max_files,
125        )
126    }
127
128    /// Initializes tracing with the configured options from cli args.
129    ///
130    /// Uses default layers for tracing. If you need to include custom layers,
131    /// use `init_tracing_with_layers` instead.
132    ///
133    /// Returns the file worker guard if a file worker was configured.
134    pub fn init_tracing(&self) -> eyre::Result<Option<FileWorkerGuard>> {
135        self.init_tracing_with_layers(Layers::new())
136    }
137
138    /// Initializes tracing with the configured options from cli args.
139    ///
140    /// Returns the file worker guard, and the file name, if a file worker was configured.
141    pub fn init_tracing_with_layers(
142        &self,
143        layers: Layers,
144    ) -> eyre::Result<Option<FileWorkerGuard>> {
145        let mut tracer = RethTracer::new();
146
147        let stdout = self.layer_info(self.log_stdout_format, self.log_stdout_filter.clone(), true);
148        tracer = tracer.with_stdout(stdout);
149
150        if self.journald {
151            tracer = tracer.with_journald(self.journald_filter.clone());
152        }
153
154        if self.log_file_max_files > 0 {
155            let info = self.file_info();
156            let file = self.layer_info(self.log_file_format, self.log_file_filter.clone(), false);
157            tracer = tracer.with_file(file, info);
158        }
159
160        if self.samply {
161            let config = self.layer_info(LogFormat::Terminal, self.samply_filter.clone(), false);
162            tracer = tracer.with_samply(config);
163        }
164
165        #[cfg(feature = "tracy")]
166        if self.tracy {
167            let config = self.layer_info(LogFormat::Terminal, self.tracy_filter.clone(), false);
168            tracer = tracer.with_tracy(config);
169        }
170
171        let guard = tracer.init_with_layers(layers)?;
172        Ok(guard)
173    }
174}
175
176/// The color mode for the cli.
177#[derive(Debug, Copy, Clone, ValueEnum, Eq, PartialEq)]
178pub enum ColorMode {
179    /// Colors on
180    Always,
181    /// Auto-detect
182    Auto,
183    /// Colors off
184    Never,
185}
186
187impl Display for ColorMode {
188    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189        match self {
190            Self::Always => write!(f, "always"),
191            Self::Auto => write!(f, "auto"),
192            Self::Never => write!(f, "never"),
193        }
194    }
195}
196
197/// The verbosity settings for the cli.
198#[derive(Debug, Copy, Clone, Args)]
199#[command(next_help_heading = "Display")]
200pub struct Verbosity {
201    /// Set the minimum log level.
202    ///
203    /// -v      Errors
204    /// -vv     Warnings
205    /// -vvv    Info
206    /// -vvvv   Debug
207    /// -vvvvv  Traces (warning: very verbose!)
208    #[arg(short, long, action = ArgAction::Count, global = true, default_value_t = 3, verbatim_doc_comment, help_heading = "Display")]
209    verbosity: u8,
210
211    /// Silence all log output.
212    #[arg(long, alias = "silent", short = 'q', global = true, help_heading = "Display")]
213    quiet: bool,
214}
215
216impl Verbosity {
217    /// Get the corresponding [Directive] for the given verbosity, or none if the verbosity
218    /// corresponds to silent.
219    pub fn directive(&self) -> Directive {
220        if self.quiet {
221            LevelFilter::OFF.into()
222        } else {
223            let level = match self.verbosity - 1 {
224                0 => Level::ERROR,
225                1 => Level::WARN,
226                2 => Level::INFO,
227                3 => Level::DEBUG,
228                _ => Level::TRACE,
229            };
230
231            level.into()
232        }
233    }
234}