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 {
49 filter.add_directive("snarkos_node_sync=debug".parse().unwrap())
50 };
51
52 let filter = if verbosity >= 3 {
53 filter
54 .add_directive("snarkos_node_bft=trace".parse().unwrap())
55 .add_directive("snarkos_node_bft::gateway=debug".parse().unwrap())
56 } else {
57 filter.add_directive("snarkos_node_bft=debug".parse().unwrap())
58 };
59
60 let filter = if verbosity >= 4 {
61 let filter = filter.add_directive("snarkos_node_bft::gateway=trace".parse().unwrap());
62
63 filter
65 .add_directive("mio=warn".parse().unwrap())
66 .add_directive("tokio_util=warn".parse().unwrap())
67 .add_directive("hyper=warn".parse().unwrap())
68 .add_directive("reqwest=warn".parse().unwrap())
69 .add_directive("want=warn".parse().unwrap())
70 .add_directive("h2=warn".parse().unwrap())
71 .add_directive("tower=warn".parse().unwrap())
72 .add_directive("axum=warn".parse().unwrap())
73 .add_directive("ureq=warn".parse().unwrap())
74 } else {
75 let filter = filter.add_directive("snarkos_node_bft::gateway=debug".parse().unwrap());
76
77 filter
79 .add_directive("mio=off".parse().unwrap())
80 .add_directive("tokio_util=off".parse().unwrap())
81 .add_directive("hyper=off".parse().unwrap())
82 .add_directive("reqwest=off".parse().unwrap())
83 .add_directive("want=off".parse().unwrap())
84 .add_directive("h2=off".parse().unwrap())
85 .add_directive("tower=off".parse().unwrap())
86 .add_directive("axum=off".parse().unwrap())
87 .add_directive("ureq=off".parse().unwrap())
88 };
89
90 let filter = if verbosity >= 5 {
91 filter.add_directive("snarkos_node_router=trace".parse().unwrap())
92 } else {
93 filter.add_directive("snarkos_node_router=debug".parse().unwrap())
94 };
95
96 let filter = if verbosity >= 6 {
97 filter.add_directive("snarkos_node_tcp=trace".parse().unwrap())
98 } else {
99 filter.add_directive("snarkos_node_tcp=off".parse().unwrap())
100 };
101
102 Ok(filter)
103}
104
105fn parse_log_filter(filter_str: &str) -> Result<EnvFilter> {
106 EnvFilter::from_str(filter_str).map_err(|err| err.into())
107}
108
109pub fn initialize_logger<P: AsRef<Path>>(
122 verbosity: u8,
123 log_filter: &Option<String>,
124 nodisplay: bool,
125 logfile: P,
126 shutdown: Arc<AtomicBool>,
127) -> Result<mpsc::Receiver<Vec<u8>>> {
128 let [stdout_filter, logfile_filter] = std::array::from_fn(|_| {
129 if let Some(filter) = log_filter { parse_log_filter(filter) } else { parse_log_verbosity(verbosity) }
130 });
131
132 let Some(logfile_dir) = logfile.as_ref().parent() else { bail!("Root directory passed as a logfile") };
134
135 if !logfile_dir.exists() {
136 if let Err(err) = std::fs::create_dir_all(logfile_dir) {
137 bail!("Failed to create a directory: '{}' ({err})", logfile_dir.display());
138 }
139 }
140 let logfile = match File::options().append(true).create(true).open(logfile) {
142 Ok(logfile) => logfile,
143 Err(err) => bail!("Failed to open the file for writing logs: {err}"),
144 };
145
146 let (log_sender, log_receiver) = mpsc::channel(1024);
148
149 let log_sender = match nodisplay {
152 true => None,
153 false => Some(log_sender),
154 };
155
156 let show_target = verbosity > 2 || log_filter.is_some();
159
160 let layered = tracing_subscriber::registry()
162 .with(
163 tracing_subscriber::fmt::Layer::default()
165 .with_ansi(log_sender.is_none() && io::stdout().is_tty())
166 .with_writer(move || LogWriter::new(&log_sender))
167 .with_target(show_target)
168 .event_format(DynamicFormatter::new(shutdown))
169 .with_filter(stdout_filter?),
170 )
171 .with(
172 tracing_subscriber::fmt::Layer::default()
174 .with_ansi(false)
175 .with_writer(logfile)
176 .with_target(show_target)
177 .with_filter(logfile_filter?),
178 );
179
180 #[cfg(feature = "tokio_console")]
182 let layered = layered.with(console_subscriber::spawn());
183
184 let _ = layered.try_init();
186
187 Ok(log_receiver)
188}
189
190pub fn initialize_terminal_logger(verbosity: u8) -> Result<()> {
192 let stdout_filter = parse_log_verbosity(verbosity)?;
193
194 let show_target = verbosity > 2;
197
198 let _ = tracing_subscriber::registry()
200 .with(
201 tracing_subscriber::fmt::Layer::default()
203 .with_ansi(io::stdout().is_tty())
204 .with_target(show_target)
205 .event_format(DynamicFormatter::new(Arc::new(AtomicBool::new(false))))
206 .with_filter(stdout_filter),
207 )
208 .try_init();
209
210 Ok(())
211}
212
213pub fn welcome_message() -> String {
215 use colored::Colorize;
216
217 let mut output = String::new();
218 output += &r#"
219
220 ╦╬╬╬╬╬╦
221 ╬╬╬╬╬╬╬╬╬ ▄▄▄▄ ▄▄▄
222 ╬╬╬╬╬╬╬╬╬╬╬ ▐▓▓▓▓▌ ▓▓▓
223 ╬╬╬╬╬╬╬╬╬╬╬╬╬ ▐▓▓▓▓▓▓▌ ▓▓▓ ▄▄▄▄▄▄ ▄▄▄▄▄▄
224 ╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬ ▐▓▓▓ ▓▓▓▌ ▓▓▓ ▄▓▓▀▀▀▀▓▓▄ ▐▓▓▓▓▓▓▓▓▌
225 ╬╬╬╬╬╬╬╜ ╙╬╬╬╬╬╬╬ ▐▓▓▓▌ ▐▓▓▓▌ ▓▓▓ ▐▓▓▓▄▄▄▄▓▓▓▌ ▐▓▓▓ ▓▓▓▌
226 ╬╬╬╬╬╬╣ ╠╬╬╬╬╬╬ ▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓ ▐▓▓▀▀▀▀▀▀▀▀▘ ▐▓▓▓ ▓▓▓▌
227 ╬╬╬╬╬╬╣ ╠╬╬╬╬╬╬ ▓▓▓▓▌ ▐▓▓▓▓ ▓▓▓ ▀▓▓▄▄▄▄▓▓▀ ▐▓▓▓▓▓▓▓▓▌
228 ╬╬╬╬╬╬╣ ╠╬╬╬╬╬╬ ▝▀▀▀▀ ▀▀▀▀▘ ▀▀▀ ▀▀▀▀▀▀ ▀▀▀▀▀▀
229╚╬╬╬╬╬╩ ╩╬╬╬╬╩
230
231
232"#
233 .white()
234 .bold();
235 output += &"👋 Welcome to Aleo! We thank you for running a node and supporting privacy.\n".bold();
236 output
237}
238
239#[cfg(test)]
240mod tests {
241 use super::*;
242
243 #[test]
244 fn log_filter() {
245 let result = parse_log_filter("=");
246 assert!(result.is_err(), "must disallow invalid log filter");
247
248 let result = parse_log_filter("snarkos=trace");
249 assert!(result.is_ok(), "must allow valid log filter");
250 }
251}