1use 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};
11const MB_TO_BYTES: u64 = 1024 * 1024;
13
14#[derive(Debug, Args)]
16#[command(next_help_heading = "Logging")]
17pub struct LogArgs {
18 #[arg(long = "log.stdout.format", value_name = "FORMAT", global = true, default_value_t = LogFormat::Terminal)]
20 pub log_stdout_format: LogFormat,
21
22 #[arg(long = "log.stdout.filter", value_name = "FILTER", global = true, default_value = "")]
24 pub log_stdout_filter: String,
25
26 #[arg(long = "log.file.format", value_name = "FORMAT", global = true, default_value_t = LogFormat::Terminal)]
28 pub log_file_format: LogFormat,
29
30 #[arg(long = "log.file.filter", value_name = "FILTER", global = true, default_value = "debug")]
32 pub log_file_filter: String,
33
34 #[arg(long = "log.file.directory", value_name = "PATH", global = true, default_value_t)]
36 pub log_file_directory: PlatformPath<LogsDir>,
37
38 #[arg(long = "log.file.name", value_name = "NAME", global = true, default_value = "reth.log")]
40 pub log_file_name: String,
41
42 #[arg(long = "log.file.max-size", value_name = "SIZE", global = true, default_value_t = 200)]
44 pub log_file_max_size: u64,
45
46 #[arg(long = "log.file.max-files", value_name = "COUNT", global = true, default_value_t = 5)]
49 pub log_file_max_files: usize,
50
51 #[arg(long = "log.journald", global = true)]
53 pub journald: bool,
54
55 #[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 #[arg(long = "log.samply", global = true, hide = true)]
66 pub samply: bool,
67
68 #[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 #[arg(long = "log.tracy", global = true, hide = true)]
80 pub tracy: bool,
81
82 #[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 #[arg(
95 long,
96 value_name = "COLOR",
97 global = true,
98 default_value_t = ColorMode::Always
99 )]
100 pub color: ColorMode,
101
102 #[command(flatten)]
104 pub verbosity: Verbosity,
105}
106
107impl LogArgs {
108 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 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 pub fn init_tracing(&self) -> eyre::Result<Option<FileWorkerGuard>> {
135 self.init_tracing_with_layers(Layers::new())
136 }
137
138 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#[derive(Debug, Copy, Clone, ValueEnum, Eq, PartialEq)]
178pub enum ColorMode {
179 Always,
181 Auto,
183 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#[derive(Debug, Copy, Clone, Args)]
199#[command(next_help_heading = "Display")]
200pub struct Verbosity {
201 #[arg(short, long, action = ArgAction::Count, global = true, default_value_t = 3, verbatim_doc_comment, help_heading = "Display")]
209 verbosity: u8,
210
211 #[arg(long, alias = "silent", short = 'q', global = true, help_heading = "Display")]
213 quiet: bool,
214}
215
216impl Verbosity {
217 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}