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 _ = 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 .try_init();
180
181 Ok(log_receiver)
182}
183
184pub fn initialize_terminal_logger(verbosity: u8) -> Result<()> {
186 let stdout_filter = parse_log_verbosity(verbosity)?;
187
188 let show_target = verbosity > 2;
191
192 let _ = tracing_subscriber::registry()
194 .with(
195 tracing_subscriber::fmt::Layer::default()
197 .with_ansi(io::stdout().is_tty())
198 .with_target(show_target)
199 .event_format(DynamicFormatter::new(Arc::new(AtomicBool::new(false))))
200 .with_filter(stdout_filter),
201 )
202 .try_init();
203
204 Ok(())
205}
206
207pub fn welcome_message() -> String {
209 use colored::Colorize;
210
211 let mut output = String::new();
212 output += &r#"
213
214 ╦╬╬╬╬╬╦
215 ╬╬╬╬╬╬╬╬╬ ▄▄▄▄ ▄▄▄
216 ╬╬╬╬╬╬╬╬╬╬╬ ▐▓▓▓▓▌ ▓▓▓
217 ╬╬╬╬╬╬╬╬╬╬╬╬╬ ▐▓▓▓▓▓▓▌ ▓▓▓ ▄▄▄▄▄▄ ▄▄▄▄▄▄
218 ╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬ ▐▓▓▓ ▓▓▓▌ ▓▓▓ ▄▓▓▀▀▀▀▓▓▄ ▐▓▓▓▓▓▓▓▓▌
219 ╬╬╬╬╬╬╬╜ ╙╬╬╬╬╬╬╬ ▐▓▓▓▌ ▐▓▓▓▌ ▓▓▓ ▐▓▓▓▄▄▄▄▓▓▓▌ ▐▓▓▓ ▓▓▓▌
220 ╬╬╬╬╬╬╣ ╠╬╬╬╬╬╬ ▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓ ▐▓▓▀▀▀▀▀▀▀▀▘ ▐▓▓▓ ▓▓▓▌
221 ╬╬╬╬╬╬╣ ╠╬╬╬╬╬╬ ▓▓▓▓▌ ▐▓▓▓▓ ▓▓▓ ▀▓▓▄▄▄▄▓▓▀ ▐▓▓▓▓▓▓▓▓▌
222 ╬╬╬╬╬╬╣ ╠╬╬╬╬╬╬ ▝▀▀▀▀ ▀▀▀▀▘ ▀▀▀ ▀▀▀▀▀▀ ▀▀▀▀▀▀
223╚╬╬╬╬╬╩ ╩╬╬╬╬╩
224
225
226"#
227 .white()
228 .bold();
229 output += &"👋 Welcome to Aleo! We thank you for running a node and supporting privacy.\n".bold();
230 output
231}
232
233#[cfg(test)]
234mod tests {
235 use super::*;
236
237 #[test]
238 fn log_filter() {
239 let result = parse_log_filter("=");
240 assert!(result.is_err(), "must disallow invalid log filter");
241
242 let result = parse_log_filter("snarkos=trace");
243 assert!(result.is_ok(), "must allow valid log filter");
244 }
245}