From aae4baa99b8ea38388a39c110ebb01300b5d3479 Mon Sep 17 00:00:00 2001 From: Roman Dmitrienko Date: Tue, 6 Aug 2024 18:06:01 +0300 Subject: [PATCH] Implement daily log file rotation. --- src/logger.rs | 76 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 21 deletions(-) diff --git a/src/logger.rs b/src/logger.rs index 5f8627e07..bd4a6ed8c 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -9,17 +9,16 @@ use chrono::Utc; use std::fs; #[cfg(not(target_os = "windows"))] use std::os::unix::fs::symlink; -use std::path::Path; +use std::path::{Path, PathBuf}; pub(crate) struct FilesystemLogger { - file_path: String, + log_dir: String, level: Level, } impl FilesystemLogger { pub(crate) fn new(log_dir: String, level: Level) -> Result { - let log_file_name = - format!("ldk_node_{}.log", chrono::offset::Local::now().format("%Y_%m_%d")); + let log_file_name = FilesystemLogger::make_log_file_name(); let log_file_path = format!("{}/{}", log_dir, log_file_name); if let Some(parent_dir) = Path::new(&log_file_path).parent() { @@ -32,21 +31,59 @@ impl FilesystemLogger { .open(log_file_path.clone()) .map_err(|e| eprintln!("ERROR: Failed to open log file: {}", e))?; - #[cfg(not(target_os = "windows"))] - { - // Create a symlink to the current log file, with prior cleanup - let log_file_symlink = parent_dir.join("ldk_node_latest.log"); - if log_file_symlink.as_path().is_symlink() { - fs::remove_file(&log_file_symlink).map_err(|e| { - eprintln!("ERROR: Failed to remove log file symlink: {}", e) - })?; - } - symlink(&log_file_name, &log_file_symlink) - .map_err(|e| eprintln!("ERROR: Failed to create log file symlink: {}", e))?; + FilesystemLogger::make_log_file_symlink(&parent_dir, &log_file_name)?; + } + + Ok(Self { log_dir, level }) + } + + fn make_log_file_name() -> String { + format!("ldk_node_{}.log", chrono::offset::Local::now().format("%Y_%m_%d")) + } + + fn make_log_file_symlink, F: AsRef>( + log_dir: D, log_file_name: F, + ) -> Result<(), ()> { + #[cfg(not(target_os = "windows"))] + { + // Create a symlink to the current log file, with prior cleanup + let log_file_symlink = log_dir.as_ref().join("ldk_node_latest.log"); + if log_file_symlink.as_path().is_symlink() { + fs::remove_file(&log_file_symlink) + .map_err(|e| eprintln!("ERROR: Failed to remove log file symlink: {}", e))?; } + symlink(&log_file_name, &log_file_symlink) + .map_err(|e| eprintln!("ERROR: Failed to create log file symlink: {}", e))?; + } + + Ok(()) + } + + fn log_file_path(&self) -> PathBuf { + PathBuf::from(format!("{}/{}", self.log_dir, FilesystemLogger::make_log_file_name())) + } + + fn open_log_file(&self) -> Result { + let log_path = self.log_file_path(); + let is_new_file = log_path.try_exists().and_then(|e| Ok(!e)).unwrap_or(false); + + let ret = fs::OpenOptions::new() + .create(true) + .append(true) + .open(&log_path) + .map_err(|e| eprintln!("ERROR: Failed to open log file: {}", e))?; + + if is_new_file { + // Do not check for errors; in concurrent scenarios, this is not + // unlikely to fail. The concurrent thread should be able to finish + // the operation. + let _ = FilesystemLogger::make_log_file_symlink( + &self.log_dir, + log_path.file_name().unwrap(), + ); } - Ok(Self { file_path: log_file_path, level }) + Ok(ret) } } impl Logger for FilesystemLogger { @@ -63,12 +100,9 @@ impl Logger for FilesystemLogger { record.line, raw_log ); - fs::OpenOptions::new() - .create(true) - .append(true) - .open(self.file_path.clone()) + self.open_log_file() .expect("Failed to open log file") .write_all(log.as_bytes()) - .expect("Failed to write to log file") + .expect("Failed to write to log file"); } }