1#![cfg(feature = "agave-unstable-api")]
3use std::{
4 env,
5 path::{Path, PathBuf},
6 sync::{Arc, LazyLock, RwLock},
7 thread::JoinHandle,
8};
9
10static LOGGER: LazyLock<Arc<RwLock<env_logger::Logger>>> =
11 LazyLock::new(|| Arc::new(RwLock::new(env_logger::Logger::from_default_env())));
12
13pub const DEFAULT_FILTER: &str = "solana=info,agave=info";
14
15struct LoggerShim {}
16
17impl log::Log for LoggerShim {
18 fn enabled(&self, metadata: &log::Metadata) -> bool {
19 LOGGER.read().unwrap().enabled(metadata)
20 }
21
22 fn log(&self, record: &log::Record) {
23 LOGGER.read().unwrap().log(record);
24 }
25
26 fn flush(&self) {}
27}
28
29fn replace_logger(logger: env_logger::Logger) {
30 log::set_max_level(logger.filter());
31 *LOGGER.write().unwrap() = logger;
32 let _ = log::set_boxed_logger(Box::new(LoggerShim {}));
33}
34
35pub fn setup_with(filter: &str) {
39 let logger =
40 env_logger::Builder::from_env(env_logger::Env::new().filter_or("_RUST_LOG", filter))
41 .format_timestamp_nanos()
42 .build();
43 replace_logger(logger);
44}
45
46pub fn setup_with_default(filter: &str) {
48 let logger = env_logger::Builder::from_env(env_logger::Env::new().default_filter_or(filter))
49 .format_timestamp_nanos()
50 .build();
51 replace_logger(logger);
52}
53
54pub fn setup_with_default_filter() {
56 setup_with_default(DEFAULT_FILTER);
57}
58
59pub fn setup() {
61 setup_with_default("error");
62}
63
64pub fn setup_file_with_default(logfile: &Path, filter: &str) {
66 use std::fs::OpenOptions;
67 let file = OpenOptions::new()
68 .create(true)
69 .append(true)
70 .open(logfile)
71 .unwrap();
72 let logger = env_logger::Builder::from_env(env_logger::Env::new().default_filter_or(filter))
73 .format_timestamp_nanos()
74 .target(env_logger::Target::Pipe(Box::new(file)))
75 .build();
76 replace_logger(logger);
77}
78
79#[cfg(unix)]
80fn redirect_stderr(filename: &Path) {
81 use std::{fs::OpenOptions, os::unix::io::AsRawFd};
82 match OpenOptions::new().create(true).append(true).open(filename) {
83 Ok(file) => unsafe {
84 libc::dup2(file.as_raw_fd(), libc::STDERR_FILENO);
85 },
86 Err(err) => eprintln!("Unable to open {}: {err}", filename.display()),
87 }
88}
89
90pub fn redirect_stderr_to_file(logfile: Option<PathBuf>) -> Option<JoinHandle<()>> {
94 if env::var_os("RUST_BACKTRACE").is_none() {
96 env::set_var("RUST_BACKTRACE", "1")
97 }
98
99 match logfile {
100 None => {
101 setup_with_default_filter();
102 None
103 }
104 Some(logfile) => {
105 #[cfg(unix)]
106 {
107 use log::info;
108 let mut signals =
109 signal_hook::iterator::Signals::new([signal_hook::consts::SIGUSR1])
110 .unwrap_or_else(|err| {
111 eprintln!("Unable to register SIGUSR1 handler: {err:?}");
112 std::process::exit(1);
113 });
114
115 setup_with_default_filter();
116 redirect_stderr(&logfile);
117 Some(
118 std::thread::Builder::new()
119 .name("solSigUsr1".into())
120 .spawn(move || {
121 for signal in signals.forever() {
122 info!(
123 "received SIGUSR1 ({signal}), reopening log file: {logfile:?}",
124 );
125 redirect_stderr(&logfile);
126 }
127 })
128 .unwrap(),
129 )
130 }
131 #[cfg(not(unix))]
132 {
133 println!("logrotate is not supported on this platform");
134 setup_file_with_default(&logfile, DEFAULT_FILTER);
135 None
136 }
137 }
138 }
139}