1#![cfg(feature = "agave-unstable-api")]
2use std::{
4 path::{Path, PathBuf},
5 sync::{Arc, LazyLock, RwLock},
6};
7
8static LOGGER: LazyLock<Arc<RwLock<env_logger::Logger>>> =
9 LazyLock::new(|| Arc::new(RwLock::new(env_logger::Logger::from_default_env())));
10
11pub const DEFAULT_FILTER: &str = "solana=info,agave=info";
12
13struct LoggerShim {}
14
15impl log::Log for LoggerShim {
16 fn enabled(&self, metadata: &log::Metadata) -> bool {
17 LOGGER.read().unwrap().enabled(metadata)
18 }
19
20 fn log(&self, record: &log::Record) {
21 LOGGER.read().unwrap().log(record);
22 }
23
24 fn flush(&self) {}
25}
26
27fn replace_logger(logger: env_logger::Logger) {
28 log::set_max_level(logger.filter());
29 *LOGGER.write().unwrap() = logger;
30 let _ = log::set_boxed_logger(Box::new(LoggerShim {}));
31}
32
33pub fn setup_with(filter: &str) {
37 let logger =
38 env_logger::Builder::from_env(env_logger::Env::new().filter_or("_RUST_LOG", filter))
39 .format_timestamp_nanos()
40 .build();
41 replace_logger(logger);
42}
43
44pub fn setup_with_default(filter: &str) {
46 let logger = env_logger::Builder::from_env(env_logger::Env::new().default_filter_or(filter))
47 .format_timestamp_nanos()
48 .build();
49 replace_logger(logger);
50}
51
52pub fn setup_with_default_filter() {
54 setup_with_default(DEFAULT_FILTER);
55}
56
57pub fn setup() {
59 setup_with_default("error");
60}
61
62#[cfg(not(unix))]
64fn setup_file_with_default_filter(logfile: &Path) {
65 let file = std::fs::OpenOptions::new()
66 .create(true)
67 .append(true)
68 .open(logfile)
69 .unwrap();
70
71 let logger =
72 env_logger::Builder::from_env(env_logger::Env::new().default_filter_or(DEFAULT_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)]
80pub fn 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 initialize_logging(logfile: Option<PathBuf>) {
91 let Some(logfile) = logfile else {
92 setup_with_default_filter();
93 return;
94 };
95
96 #[cfg(unix)]
97 {
98 setup_with_default_filter();
99 redirect_stderr(&logfile);
100 }
101 #[cfg(not(unix))]
102 {
103 setup_file_with_default_filter(&logfile);
104 }
105}