solana_logger/
lib.rs

1//! The `logger` module configures `env_logger`
2
3use {
4    lazy_static::lazy_static,
5    std::{
6        env,
7        sync::{Arc, RwLock},
8        thread::JoinHandle,
9    },
10};
11
12lazy_static! {
13    static ref LOGGER: Arc<RwLock<env_logger::Logger>> =
14        Arc::new(RwLock::new(env_logger::Logger::from_default_env()));
15}
16
17pub const DEFAULT_FILTER: &str = "solana=info,agave=info";
18
19struct LoggerShim {}
20
21impl log::Log for LoggerShim {
22    fn enabled(&self, metadata: &log::Metadata) -> bool {
23        LOGGER.read().unwrap().enabled(metadata)
24    }
25
26    fn log(&self, record: &log::Record) {
27        LOGGER.read().unwrap().log(record);
28    }
29
30    fn flush(&self) {}
31}
32
33fn replace_logger(logger: env_logger::Logger) {
34    log::set_max_level(logger.filter());
35    *LOGGER.write().unwrap() = logger;
36    let _ = log::set_boxed_logger(Box::new(LoggerShim {}));
37}
38
39// Configures logging with a specific filter overriding RUST_LOG.  _RUST_LOG is used instead
40// so if set it takes precedence.
41// May be called at any time to re-configure the log filter
42pub fn setup_with(filter: &str) {
43    let logger =
44        env_logger::Builder::from_env(env_logger::Env::new().filter_or("_RUST_LOG", filter))
45            .format_timestamp_nanos()
46            .build();
47    replace_logger(logger);
48}
49
50// Configures logging with a default filter if RUST_LOG is not set
51pub fn setup_with_default(filter: &str) {
52    let logger = env_logger::Builder::from_env(env_logger::Env::new().default_filter_or(filter))
53        .format_timestamp_nanos()
54        .build();
55    replace_logger(logger);
56}
57
58// Configures logging with the `DEFAULT_FILTER` if RUST_LOG is not set
59pub fn setup_with_default_filter() {
60    setup_with_default(DEFAULT_FILTER);
61}
62
63// Configures logging with the default filter "error" if RUST_LOG is not set
64pub fn setup() {
65    setup_with_default("error");
66}
67
68// Configures file logging with a default filter if RUST_LOG is not set
69pub fn setup_file_with_default(logfile: &str, filter: &str) {
70    use std::fs::OpenOptions;
71    let file = OpenOptions::new()
72        .create(true)
73        .append(true)
74        .open(logfile)
75        .unwrap();
76    let logger = env_logger::Builder::from_env(env_logger::Env::new().default_filter_or(filter))
77        .format_timestamp_nanos()
78        .target(env_logger::Target::Pipe(Box::new(file)))
79        .build();
80    replace_logger(logger);
81}
82
83#[cfg(all(unix, not(target_arch = "wasm32")))]
84fn redirect_stderr(filename: &str) {
85    use std::{fs::OpenOptions, os::unix::io::AsRawFd};
86    match OpenOptions::new().create(true).append(true).open(filename) {
87        Ok(file) => unsafe {
88            libc::dup2(file.as_raw_fd(), libc::STDERR_FILENO);
89        },
90        Err(err) => eprintln!("Unable to open {filename}: {err}"),
91    }
92}
93
94// Redirect stderr to a file with support for logrotate by sending a SIGUSR1 to the process.
95//
96// Upon success, future `log` macros and `eprintln!()` can be found in the specified log file.
97#[cfg(not(target_arch = "wasm32"))]
98pub fn redirect_stderr_to_file(logfile: Option<String>) -> Option<JoinHandle<()>> {
99    // Default to RUST_BACKTRACE=1 for more informative validator logs
100    if env::var_os("RUST_BACKTRACE").is_none() {
101        env::set_var("RUST_BACKTRACE", "1")
102    }
103
104    match logfile {
105        None => {
106            setup_with_default_filter();
107            None
108        }
109        Some(logfile) => {
110            #[cfg(unix)]
111            {
112                use log::info;
113                let mut signals =
114                    signal_hook::iterator::Signals::new([signal_hook::consts::SIGUSR1])
115                        .unwrap_or_else(|err| {
116                            eprintln!("Unable to register SIGUSR1 handler: {err:?}");
117                            std::process::exit(1);
118                        });
119
120                setup_with_default_filter();
121                redirect_stderr(&logfile);
122                Some(
123                    std::thread::Builder::new()
124                        .name("solSigUsr1".into())
125                        .spawn(move || {
126                            for signal in signals.forever() {
127                                info!(
128                                    "received SIGUSR1 ({}), reopening log file: {:?}",
129                                    signal, logfile
130                                );
131                                redirect_stderr(&logfile);
132                            }
133                        })
134                        .unwrap(),
135                )
136            }
137            #[cfg(not(unix))]
138            {
139                println!("logrotate is not supported on this platform");
140                setup_file_with_default(&logfile, DEFAULT_FILTER);
141                None
142            }
143        }
144    }
145}