1use crate::{
10 LogFormat, LogOutputDest, appender,
11 error::{Error, Result},
12};
13use std::collections::BTreeMap;
14use tracing_appender::non_blocking::WorkerGuard;
15use tracing_core::{Event, Level, Subscriber};
16use tracing_subscriber::{
17 Layer, Registry,
18 filter::Targets,
19 fmt::{
20 self as tracing_fmt, FmtContext, FormatEvent, FormatFields,
21 format::Writer,
22 time::{FormatTime, SystemTime},
23 },
24 layer::Filter,
25 registry::LookupSpan,
26 reload::{self, Handle},
27};
28
29const MAX_LOG_SIZE: usize = 20 * 1024 * 1024;
30const MAX_UNCOMPRESSED_LOG_FILES: usize = 10;
31const MAX_LOG_FILES: usize = 1000;
32const ALL_ANT_LOGS: &str = "all";
34const VERBOSE_ANT_LOGS: &str = "v";
36
37pub struct ReloadHandle(pub(crate) Handle<Box<dyn Filter<Registry> + Send + Sync>, Registry>);
39
40impl ReloadHandle {
41 pub fn modify_log_level(&self, logging_value: &str) -> Result<()> {
47 let targets: Vec<(String, Level)> = get_logging_targets(logging_value)?;
48 self.0.modify(|old_filter| {
49 let new_filter: Box<dyn Filter<Registry> + Send + Sync> =
50 Box::new(Targets::new().with_targets(targets));
51 *old_filter = new_filter;
52 })?;
53
54 Ok(())
55 }
56}
57
58#[derive(Default)]
59pub(crate) struct LogFormatter;
61
62impl<S, N> FormatEvent<S, N> for LogFormatter
63where
64 S: Subscriber + for<'a> LookupSpan<'a>,
65 N: for<'a> FormatFields<'a> + 'static,
66{
67 fn format_event(
68 &self,
69 ctx: &FmtContext<'_, S, N>,
70 mut writer: Writer,
71 event: &Event<'_>,
72 ) -> std::fmt::Result {
73 let level = *event.metadata().level();
75 let module = event.metadata().module_path().unwrap_or("<unknown module>");
76 let lno = event.metadata().line().unwrap_or(0);
77 let time = SystemTime;
78
79 write!(writer, "[")?;
80 time.format_time(&mut writer)?;
81 write!(writer, " {level} {module} {lno}")?;
82 ctx.visit_spans(|span| write!(writer, "/{}", span.name()))?;
83 write!(writer, "] ")?;
84
85 ctx.field_format().format_fields(writer.by_ref(), event)?;
87
88 writeln!(writer)
89 }
90}
91
92#[derive(Default)]
94pub(crate) struct TracingLayers {
95 pub(crate) layers: Vec<Box<dyn Layer<Registry> + Send + Sync>>,
96 pub(crate) log_appender_guard: Option<WorkerGuard>,
97}
98
99impl TracingLayers {
100 pub(crate) fn fmt_layer(
101 &mut self,
102 default_logging_targets: Vec<(String, Level)>,
103 output_dest: &LogOutputDest,
104 format: LogFormat,
105 max_uncompressed_log_files: Option<usize>,
106 max_compressed_log_files: Option<usize>,
107 print_updates_to_stdout: bool,
108 ) -> Result<ReloadHandle> {
109 let layer = match output_dest {
110 LogOutputDest::Stdout => {
111 if print_updates_to_stdout {
112 println!("Logging to stdout");
113 }
114 tracing_fmt::layer()
115 .with_ansi(false)
116 .with_target(false)
117 .event_format(LogFormatter)
118 .boxed()
119 }
120 LogOutputDest::Stderr => tracing_fmt::layer()
121 .with_ansi(false)
122 .with_target(false)
123 .event_format(LogFormatter)
124 .with_writer(std::io::stderr)
125 .boxed(),
126 LogOutputDest::Path(path) => {
127 std::fs::create_dir_all(path)?;
128 if print_updates_to_stdout {
129 println!("Logging to directory: {path:?}");
130 }
131
132 let max_uncompressed_log_files =
134 max_uncompressed_log_files.unwrap_or(MAX_UNCOMPRESSED_LOG_FILES);
135 let max_log_files = if let Some(max_compressed_log_files) = max_compressed_log_files
137 {
138 max_compressed_log_files + max_uncompressed_log_files
139 } else {
140 std::cmp::max(max_uncompressed_log_files, MAX_LOG_FILES)
141 };
142 let (file_rotation, worker_guard) = appender::file_rotater(
143 path,
144 MAX_LOG_SIZE,
145 max_uncompressed_log_files,
146 max_log_files,
147 );
148 self.log_appender_guard = Some(worker_guard);
149
150 match format {
151 LogFormat::Json => tracing_fmt::layer()
152 .json()
153 .flatten_event(true)
154 .with_writer(file_rotation)
155 .boxed(),
156 LogFormat::Default => tracing_fmt::layer()
157 .with_ansi(false)
158 .with_writer(file_rotation)
159 .event_format(LogFormatter)
160 .boxed(),
161 }
162 }
163 };
164 let targets = match std::env::var("ANT_LOG") {
165 Ok(sn_log_val) => {
166 if print_updates_to_stdout {
167 println!("Using ANT_LOG={sn_log_val}");
168 }
169 get_logging_targets(&sn_log_val)?
170 }
171 Err(_) => default_logging_targets,
172 };
173
174 let target_filters: Box<dyn Filter<Registry> + Send + Sync> =
175 Box::new(Targets::new().with_targets(targets));
176
177 let (filter, reload_handle) = reload::Layer::new(target_filters);
178
179 let layer = layer.with_filter(filter);
180 self.layers.push(Box::new(layer));
181
182 Ok(ReloadHandle(reload_handle))
183 }
184
185 #[cfg(feature = "otlp")]
186 pub(crate) fn otlp_layer(
187 &mut self,
188 default_logging_targets: Vec<(String, Level)>,
189 ) -> Result<()> {
190 use opentelemetry::{
191 KeyValue,
192 sdk::{Resource, trace},
193 };
194 use opentelemetry_otlp::WithExportConfig;
195 use opentelemetry_semantic_conventions::resource::{SERVICE_INSTANCE_ID, SERVICE_NAME};
196 use rand::{Rng, distributions::Alphanumeric, thread_rng};
197
198 let service_name = std::env::var("OTLP_SERVICE_NAME").unwrap_or_else(|_| {
199 let random_node_name: String = thread_rng()
200 .sample_iter(&Alphanumeric)
201 .take(10)
202 .map(char::from)
203 .collect();
204 random_node_name
205 });
206 println!("The opentelemetry traces are logged under the name: {service_name}");
207
208 let tracer = opentelemetry_otlp::new_pipeline()
209 .tracing()
210 .with_exporter(opentelemetry_otlp::new_exporter().tonic().with_env())
211 .with_trace_config(trace::config().with_resource(Resource::new(vec![
212 KeyValue::new(SERVICE_NAME, service_name),
213 KeyValue::new(SERVICE_INSTANCE_ID, std::process::id().to_string()),
214 ])))
215 .install_batch(opentelemetry::runtime::Tokio)?;
216
217 let targets = match std::env::var("ANT_LOG_OTLP") {
218 Ok(sn_log_val) => {
219 println!("Using ANT_LOG_OTLP={sn_log_val}");
220 get_logging_targets(&sn_log_val)?
221 }
222 Err(_) => default_logging_targets,
223 };
224
225 let target_filters: Box<dyn Filter<Registry> + Send + Sync> =
226 Box::new(Targets::new().with_targets(targets));
227 let otlp_layer = tracing_opentelemetry::layer()
228 .with_tracer(tracer)
229 .with_filter(target_filters)
230 .boxed();
231 self.layers.push(otlp_layer);
232 Ok(())
233 }
234}
235
236fn get_logging_targets(logging_env_value: &str) -> Result<Vec<(String, Level)>> {
241 let mut targets = BTreeMap::new();
242 let mut contains_keyword_all_sn_logs = false;
243 let mut contains_keyword_verbose_sn_logs = false;
244
245 for crate_log_level in logging_env_value.split(',') {
246 if crate_log_level == ALL_ANT_LOGS {
249 contains_keyword_all_sn_logs = true;
250 continue;
251 } else if crate_log_level == VERBOSE_ANT_LOGS {
252 contains_keyword_verbose_sn_logs = true;
253 continue;
254 }
255
256 let mut split = crate_log_level.split('=');
257 let crate_name = split.next().ok_or_else(|| {
258 Error::LoggingConfiguration("Could not obtain crate name in logging string".to_string())
259 })?;
260 let log_level = split.next().unwrap_or("trace");
261 targets.insert(crate_name.to_string(), get_log_level_from_str(log_level)?);
262 }
263
264 let mut to_be_overriden_targets =
265 if contains_keyword_all_sn_logs || contains_keyword_verbose_sn_logs {
266 let mut t = BTreeMap::from_iter(vec![
267 ("ant".to_string(), Level::TRACE),
269 ("evm_testnet".to_string(), Level::TRACE),
270 ("antnode".to_string(), Level::TRACE),
271 ("antnode_rpc_client".to_string(), Level::TRACE),
272 ("antctl".to_string(), Level::TRACE),
273 ("antctld".to_string(), Level::TRACE),
274 ("ant_bootstrap".to_string(), Level::TRACE),
276 ("ant_build_info".to_string(), Level::TRACE),
277 ("ant_evm".to_string(), Level::TRACE),
278 ("ant_logging".to_string(), Level::TRACE),
279 ("ant_node_manager".to_string(), Level::TRACE),
280 ("ant_node_rpc_client".to_string(), Level::TRACE),
281 ("ant_protocol".to_string(), Level::TRACE),
282 ("ant_service_management".to_string(), Level::TRACE),
283 ("autonomi".to_string(), Level::TRACE),
284 ("evmlib".to_string(), Level::TRACE),
285 ]);
286
287 if !t.contains_key("ant_node") {
288 if contains_keyword_all_sn_logs {
289 t.insert("ant_node".to_string(), Level::TRACE)
290 } else if contains_keyword_verbose_sn_logs {
291 t.insert("ant_node".to_string(), Level::DEBUG)
292 } else {
293 t.insert("ant_node".to_string(), Level::INFO)
294 };
295 }
296 t
297 } else {
298 Default::default()
299 };
300 to_be_overriden_targets.extend(targets);
301 Ok(to_be_overriden_targets.into_iter().collect())
302}
303
304fn get_log_level_from_str(log_level: &str) -> Result<Level> {
305 match log_level.to_lowercase().as_str() {
306 "info" => Ok(Level::INFO),
307 "debug" => Ok(Level::DEBUG),
308 "trace" => Ok(Level::TRACE),
309 "warn" => Ok(Level::WARN),
310 "error" => Ok(Level::WARN),
311 _ => Err(Error::LoggingConfiguration(format!(
312 "Log level {log_level} is not supported"
313 ))),
314 }
315}