1use 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};
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.max-size", value_name = "SIZE", global = true, default_value_t = 200)]
40 pub log_file_max_size: u64,
41
42 #[arg(long = "log.file.max-files", value_name = "COUNT", global = true, default_value_t = 5)]
45 pub log_file_max_files: usize,
46
47 #[arg(long = "log.journald", global = true)]
49 pub journald: bool,
50
51 #[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 #[arg(
63 long,
64 value_name = "COLOR",
65 global = true,
66 default_value_t = ColorMode::Always
67 )]
68 pub color: ColorMode,
69 #[command(flatten)]
71 pub verbosity: Verbosity,
72}
73
74impl LogArgs {
75 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 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 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#[derive(Debug, Copy, Clone, ValueEnum, Eq, PartialEq)]
120pub enum ColorMode {
121 Always,
123 Auto,
125 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#[derive(Debug, Copy, Clone, Args)]
141#[command(next_help_heading = "Display")]
142pub struct Verbosity {
143 #[arg(short, long, action = ArgAction::Count, global = true, default_value_t = 3, verbatim_doc_comment, help_heading = "Display")]
151 verbosity: u8,
152
153 #[arg(long, alias = "silent", short = 'q', global = true, help_heading = "Display")]
155 quiet: bool,
156}
157
158impl Verbosity {
159 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}