snarkos_cli/helpers/
logger.rs1use crate::helpers::{DynamicFormatter, LogWriter};
17
18use anyhow::{Result, bail};
19
20use crossterm::tty::IsTty;
21use std::{
22 fs::File,
23 io,
24 path::Path,
25 str::FromStr,
26 sync::{Arc, atomic::AtomicBool},
27};
28use tokio::sync::mpsc;
29use tracing_subscriber::{
30 EnvFilter,
31 layer::{Layer, SubscriberExt},
32 util::SubscriberInitExt,
33};
34
35fn parse_log_verbosity(verbosity: u8) -> Result<EnvFilter> {
36 let default_log_str = match verbosity {
39 0 => "info",
40 1 => "debug",
41 2.. => "trace",
42 };
43 let filter = EnvFilter::from_str(default_log_str).unwrap();
44
45 let filter = if verbosity >= 2 {
47 filter.add_directive("snarkos_node_sync=trace".parse().unwrap())
48 } else if verbosity >= 1 {
49 filter.add_directive("snarkos_node_sync=debug".parse().unwrap())
50 } else {
51 filter
52 };
53
54 let filter = if verbosity >= 3 {
55 filter.add_directive("snarkos_node_bft=trace".parse().unwrap())
56 } else if verbosity >= 1 {
57 filter.add_directive("snarkos_node_bft=debug".parse().unwrap())
58 } else {
59 filter
60 };
61
62 let filter = if verbosity >= 4 {
63 filter.add_directive("snarkos_node_bft::gateway=trace".parse().unwrap())
64 } else if verbosity >= 1 {
65 filter.add_directive("snarkos_node_bft::gateway=debug".parse().unwrap())
66 } else {
67 filter
68 };
69
70 let filter = if verbosity >= 4 {
71 filter
73 .add_directive("mio=warn".parse().unwrap())
74 .add_directive("tokio_util=warn".parse().unwrap())
75 .add_directive("hyper=warn".parse().unwrap())
76 .add_directive("reqwest=warn".parse().unwrap())
77 .add_directive("want=warn".parse().unwrap())
78 .add_directive("h2=warn".parse().unwrap())
79 .add_directive("tower=warn".parse().unwrap())
80 .add_directive("axum=warn".parse().unwrap())
81 .add_directive("ureq=warn".parse().unwrap())
82 .add_directive("rustls=warn".parse().unwrap())
83 } else {
84 filter
86 .add_directive("mio=off".parse().unwrap())
87 .add_directive("tokio_util=off".parse().unwrap())
88 .add_directive("hyper=off".parse().unwrap())
89 .add_directive("reqwest=off".parse().unwrap())
90 .add_directive("want=off".parse().unwrap())
91 .add_directive("h2=off".parse().unwrap())
92 .add_directive("tower=off".parse().unwrap())
93 .add_directive("axum=off".parse().unwrap())
94 .add_directive("ureq=off".parse().unwrap())
95 .add_directive("rustls=off".parse().unwrap())
96 };
97
98 let filter = if verbosity >= 5 {
99 filter.add_directive("snarkos_node_router=trace".parse().unwrap())
100 } else if verbosity >= 1 {
101 filter.add_directive("snarkos_node_router=debug".parse().unwrap())
102 } else {
103 filter
104 };
105
106 let filter = if verbosity >= 6 {
107 filter.add_directive("snarkos_node_tcp=trace".parse().unwrap())
108 } else {
109 filter.add_directive("snarkos_node_tcp=off".parse().unwrap())
110 };
111
112 Ok(filter)
113}
114
115fn parse_log_filter(filter_str: &str) -> Result<EnvFilter> {
116 EnvFilter::from_str(filter_str).map_err(|err| err.into())
117}
118
119pub fn initialize_logger<P: AsRef<Path>>(
134 verbosity: u8,
135 log_filter: &Option<String>,
136 nodisplay: bool,
137 logfile: P,
138 shutdown: Arc<AtomicBool>,
139) -> Result<mpsc::Receiver<Vec<u8>>> {
140 let [stdout_filter, logfile_filter] = std::array::from_fn(|_| {
141 if let Some(filter) = log_filter { parse_log_filter(filter) } else { parse_log_verbosity(verbosity) }
142 });
143
144 let Some(logfile_dir) = logfile.as_ref().parent() else { bail!("Root directory passed as a logfile") };
146
147 if !logfile_dir.exists() {
148 if let Err(err) = std::fs::create_dir_all(logfile_dir) {
149 bail!("Failed to create a directory: '{}' ({err})", logfile_dir.display());
150 }
151 }
152 let logfile = match File::options().append(true).create(true).open(logfile) {
154 Ok(logfile) => logfile,
155 Err(err) => bail!("Failed to open the file for writing logs: {err}"),
156 };
157
158 let (log_sender, log_receiver) = mpsc::channel(1024);
160
161 let log_sender = match nodisplay {
164 true => None,
165 false => Some(log_sender),
166 };
167
168 let show_target = verbosity > 2 || log_filter.is_some();
171
172 let layered = tracing_subscriber::registry()
174 .with(
175 tracing_subscriber::fmt::Layer::default()
177 .with_ansi(log_sender.is_none() && io::stdout().is_tty())
178 .with_writer(move || LogWriter::new(&log_sender))
179 .with_target(show_target)
180 .event_format(DynamicFormatter::new(shutdown))
181 .with_filter(stdout_filter?),
182 )
183 .with(
184 tracing_subscriber::fmt::Layer::default()
186 .with_ansi(false)
187 .with_writer(logfile)
188 .with_target(show_target)
189 .with_filter(logfile_filter?),
190 );
191
192 #[cfg(feature = "tokio_console")]
194 let layered = layered.with(console_subscriber::spawn());
195
196 let _ = layered.try_init();
198
199 Ok(log_receiver)
200}
201
202pub fn initialize_terminal_logger(verbosity: u8) -> Result<()> {
204 let stdout_filter = parse_log_verbosity(verbosity)?;
205
206 let show_target = verbosity > 2;
209
210 let _ = tracing_subscriber::registry()
212 .with(
213 tracing_subscriber::fmt::Layer::default()
215 .with_ansi(io::stdout().is_tty())
216 .with_target(show_target)
217 .event_format(DynamicFormatter::new(Arc::new(AtomicBool::new(false))))
218 .with_filter(stdout_filter),
219 )
220 .try_init();
221
222 Ok(())
223}
224
225pub fn welcome_message() -> String {
227 use colored::Colorize;
228
229 let mut output = String::new();
230 output += &r#"
231
232 ╦╬╬╬╬╬╦
233 ╬╬╬╬╬╬╬╬╬ ▄▄▄▄ ▄▄▄
234 ╬╬╬╬╬╬╬╬╬╬╬ ▐▓▓▓▓▌ ▓▓▓
235 ╬╬╬╬╬╬╬╬╬╬╬╬╬ ▐▓▓▓▓▓▓▌ ▓▓▓ ▄▄▄▄▄▄ ▄▄▄▄▄▄
236 ╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬ ▐▓▓▓ ▓▓▓▌ ▓▓▓ ▄▓▓▀▀▀▀▓▓▄ ▐▓▓▓▓▓▓▓▓▌
237 ╬╬╬╬╬╬╬╜ ╙╬╬╬╬╬╬╬ ▐▓▓▓▌ ▐▓▓▓▌ ▓▓▓ ▐▓▓▓▄▄▄▄▓▓▓▌ ▐▓▓▓ ▓▓▓▌
238 ╬╬╬╬╬╬╣ ╠╬╬╬╬╬╬ ▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓ ▐▓▓▀▀▀▀▀▀▀▀▘ ▐▓▓▓ ▓▓▓▌
239 ╬╬╬╬╬╬╣ ╠╬╬╬╬╬╬ ▓▓▓▓▌ ▐▓▓▓▓ ▓▓▓ ▀▓▓▄▄▄▄▓▓▀ ▐▓▓▓▓▓▓▓▓▌
240 ╬╬╬╬╬╬╣ ╠╬╬╬╬╬╬ ▝▀▀▀▀ ▀▀▀▀▘ ▀▀▀ ▀▀▀▀▀▀ ▀▀▀▀▀▀
241╚╬╬╬╬╬╩ ╩╬╬╬╬╩
242
243
244"#
245 .white()
246 .bold();
247 output += &"👋 Welcome to Aleo! We thank you for running a node and supporting privacy.\n".bold();
248 output
249}
250
251#[cfg(test)]
252mod tests {
253 use super::*;
254
255 #[test]
256 fn log_filter() {
257 let result = parse_log_filter("=");
258 assert!(result.is_err(), "must disallow invalid log filter");
259
260 let result = parse_log_filter("snarkos=trace");
261 assert!(result.is_ok(), "must allow valid log filter");
262 }
263}