1use {
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
39pub 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
50pub 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
58pub fn setup_with_default_filter() {
60 setup_with_default(DEFAULT_FILTER);
61}
62
63pub fn setup() {
65 setup_with_default("error");
66}
67
68pub 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#[cfg(not(target_arch = "wasm32"))]
98pub fn redirect_stderr_to_file(logfile: Option<String>) -> Option<JoinHandle<()>> {
99 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}