From 50f1ec0374b7e8d803c2918261c9ff8baa768205 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Fri, 24 Apr 2020 14:26:53 -0700 Subject: [PATCH] Add support for log rotation, sending SIGUSR1 will cause the log file to be re-opened (#9713) --- validator/Cargo.toml | 3 +- validator/src/main.rs | 108 ++++++++++++++++++++++++++++-------------- 2 files changed, 75 insertions(+), 36 deletions(-) diff --git a/validator/Cargo.toml b/validator/Cargo.toml index 5e1cd9334..990acc5bc 100644 --- a/validator/Cargo.toml +++ b/validator/Cargo.toml @@ -31,7 +31,8 @@ solana-vote-program = { path = "../programs/vote", version = "1.2.0" } solana-vote-signer = { path = "../vote-signer", version = "1.2.0" } [target."cfg(unix)".dependencies] -gag = "0.1.10" +libc = "0.2.66" +signal-hook = "0.1.13" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/validator/src/main.rs b/validator/src/main.rs index 89847fa9c..f3d0ef328 100644 --- a/validator/src/main.rs +++ b/validator/src/main.rs @@ -45,7 +45,7 @@ use std::{ atomic::{AtomicBool, Ordering}, Arc, }, - thread::sleep, + thread::{sleep, JoinHandle}, time::{Duration, Instant}, }; @@ -400,6 +400,64 @@ fn is_snapshot_config_invalid( || snapshot_interval_slots % accounts_hash_interval_slots != 0) } +#[cfg(unix)] +fn redirect_stderr(filename: &str) { + use std::{fs::OpenOptions, os::unix::io::AsRawFd}; + match OpenOptions::new() + .write(true) + .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), + } +} + +fn start_logger(logfile: Option) -> Option> { + let logger_thread = match logfile { + None => None, + Some(logfile) => { + #[cfg(unix)] + { + let signals = signal_hook::iterator::Signals::new(&[signal_hook::SIGUSR1]) + .unwrap_or_else(|err| { + eprintln!("Unable to register SIGUSR1 handler: {:?}", err); + exit(1); + }); + + redirect_stderr(&logfile); + Some(std::thread::spawn(move || { + for signal in signals.forever() { + info!( + "received SIGUSR1 ({}), reopening log file: {:?}", + signal, logfile + ); + redirect_stderr(&logfile); + } + })) + } + #[cfg(not(unix))] + { + println!("logging to a file is not supported on this platform"); + () + } + } + }; + + solana_logger::setup_with_default( + &[ + "solana=info", /* info logging for all solana modules */ + "rpc=trace", /* json_rpc request/response logging */ + ] + .join(","), + ); + + logger_thread +} + #[allow(clippy::cognitive_complexity)] pub fn main() { let default_dynamic_port_range = @@ -661,7 +719,9 @@ pub fn main() { .long("log") .value_name("FILE") .takes_value(true) - .help("Redirect logging to the specified file, '-' for standard error"), + .help("Redirect logging to the specified file, '-' for standard error. \ + Sending the SIGUSR1 signal to the validator process will cause it \ + to re-open the log file"), ) .arg( Arg::with_name("wait_for_supermajority") @@ -927,42 +987,20 @@ pub fn main() { warn!("--vote-signer-address ignored"); } - let _log_redirect = { - #[cfg(unix)] - { - let default_logfile = format!( - "solana-validator-{}-{}.log", - identity_keypair.pubkey(), - chrono::Utc::now().format("%Y%m%d-%H%M%S") - ); - let logfile = matches.value_of("logfile").unwrap_or(&default_logfile); + let logfile = { + let logfile = matches + .value_of("logfile") + .map(|s| s.into()) + .unwrap_or_else(|| format!("solana-validator-{}.log", identity_keypair.pubkey())); - if logfile == "-" { - None - } else { - println!("log file: {}", logfile); - Some(gag::Redirect::stderr(File::create(logfile).unwrap_or_else( - |err| { - eprintln!("Unable to create {}: {:?}", logfile, err); - exit(1); - }, - ))) - } - } - #[cfg(not(unix))] - { - println!("logging to a file is not supported on this platform"); - () + if logfile == "-" { + None + } else { + println!("log file: {}", logfile); + Some(logfile) } }; - - solana_logger::setup_with_default( - &[ - "solana=info", /* info logging for all solana modules */ - "rpc=trace", /* json_rpc request/response logging */ - ] - .join(","), - ); + let _logger_thread = start_logger(logfile); // Default to RUST_BACKTRACE=1 for more informative validator logs if env::var_os("RUST_BACKTRACE").is_none() {