diff --git a/Cargo.lock b/Cargo.lock index a5977ef1..25be76a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1602,9 +1602,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.169" +version = "0.2.170" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" [[package]] name = "libsecp256k1" @@ -2487,6 +2487,25 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + [[package]] name = "signature" version = "1.6.4" @@ -3147,7 +3166,9 @@ version = "2.2.1" dependencies = [ "env_logger", "lazy_static", + "libc", "log", + "signal-hook", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 63b455e6..06242463 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -161,6 +161,7 @@ indicatif = "0.17.9" itertools = "0.12.1" js-sys = "0.3.77" lazy_static = "1.5.0" +libc = "0.2.170" libsecp256k1 = { version = "0.6.0", default-features = false, features = [ "std", "static-context", @@ -192,6 +193,7 @@ serde_with = { version = "3.12.0", default-features = false } serial_test = "2.0.0" sha2 = "0.10.8" sha3 = "0.10.8" +signal-hook = "0.3.17" siphasher = "0.3.11" solana-account = { path = "account", version = "2.2.1" } solana-account-info = { path = "account-info", version = "2.2.1" } diff --git a/logger/Cargo.toml b/logger/Cargo.toml index 8ca855da..8bbda65f 100644 --- a/logger/Cargo.toml +++ b/logger/Cargo.toml @@ -14,6 +14,10 @@ env_logger = { workspace = true } lazy_static = { workspace = true } log = { workspace = true } +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +libc = { workspace = true } +signal-hook = { workspace = true } + [lib] name = "solana_logger" diff --git a/logger/src/lib.rs b/logger/src/lib.rs index 8d6a2066..822e0216 100644 --- a/logger/src/lib.rs +++ b/logger/src/lib.rs @@ -2,7 +2,11 @@ use { lazy_static::lazy_static, - std::sync::{Arc, RwLock}, + std::{ + env, + sync::{Arc, RwLock}, + thread::JoinHandle, + }, }; lazy_static! { @@ -75,3 +79,67 @@ pub fn setup_file_with_default(logfile: &str, filter: &str) { .build(); replace_logger(logger); } + +#[cfg(all(unix, not(target_arch = "wasm32")))] +fn redirect_stderr(filename: &str) { + use std::{fs::OpenOptions, os::unix::io::AsRawFd}; + match OpenOptions::new().create(true).append(true).open(filename) { + Ok(file) => unsafe { + libc::dup2(file.as_raw_fd(), libc::STDERR_FILENO); + }, + Err(err) => eprintln!("Unable to open {filename}: {err}"), + } +} + +// Redirect stderr to a file with support for logrotate by sending a SIGUSR1 to the process. +// +// Upon success, future `log` macros and `eprintln!()` can be found in the specified log file. +#[cfg(not(target_arch = "wasm32"))] +pub fn redirect_stderr_to_file(logfile: Option) -> Option> { + // Default to RUST_BACKTRACE=1 for more informative validator logs + if env::var_os("RUST_BACKTRACE").is_none() { + env::set_var("RUST_BACKTRACE", "1") + } + + match logfile { + None => { + setup_with_default_filter(); + None + } + Some(logfile) => { + #[cfg(unix)] + { + use log::info; + let mut signals = + signal_hook::iterator::Signals::new([signal_hook::consts::SIGUSR1]) + .unwrap_or_else(|err| { + eprintln!("Unable to register SIGUSR1 handler: {err:?}"); + std::process::exit(1); + }); + + setup_with_default_filter(); + redirect_stderr(&logfile); + Some( + std::thread::Builder::new() + .name("solSigUsr1".into()) + .spawn(move || { + for signal in signals.forever() { + info!( + "received SIGUSR1 ({}), reopening log file: {:?}", + signal, logfile + ); + redirect_stderr(&logfile); + } + }) + .unwrap(), + ) + } + #[cfg(not(unix))] + { + println!("logrotate is not supported on this platform"); + setup_file_with_default(&logfile, solana_logger::DEFAULT_FILTER); + None + } + } + } +} diff --git a/scripts/check-nits.sh b/scripts/check-nits.sh index e4e7d603..0a5371ce 100755 --- a/scripts/check-nits.sh +++ b/scripts/check-nits.sh @@ -17,6 +17,7 @@ declare prints=( # Parts of the tree that are expected to be print free declare print_free_tree=( ':**.rs' + ':^logger/src/lib.rs' ':^msg/src/lib.rs' ':^program-option/src/lib.rs' ':^pubkey/src/lib.rs'