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