-
Notifications
You must be signed in to change notification settings - Fork 1.1k
[Bugfix] Reopen CubeShim and VMM logs after rename-based rotation #1292
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,7 +12,7 @@ use std::io; | |
| use std::io::Write; | ||
| use std::mem; | ||
| use std::os::unix::io::AsRawFd; | ||
| use std::path::PathBuf; | ||
| use std::path::{Path, PathBuf}; | ||
|
|
||
| use std::time::SystemTime; | ||
| use time::format_description::well_known::Rfc3339; | ||
|
|
@@ -57,6 +57,7 @@ macro_rules! errf { | |
| } | ||
|
|
||
| const LOG_ITEM_COUNT: usize = 1024; | ||
| const LOG_REOPEN_INTERVAL: Duration = Duration::from_secs(1800); | ||
| const LOG_DIR: &str = "/data/log/CubeShim/"; | ||
| const LOF_FILE: &str = "cube-shim-req.log"; | ||
| const STAT_FILE: &str = "cube-shim-stat.log"; | ||
|
|
@@ -117,6 +118,55 @@ struct StatItem { | |
| function_type: String, | ||
| } | ||
|
|
||
| struct ReopenableFile { | ||
| path: PathBuf, | ||
| file: tokio::fs::File, | ||
| } | ||
|
|
||
| impl ReopenableFile { | ||
| async fn open(path: &Path) -> CResult<Self> { | ||
| let file = OpenOptions::new() | ||
|
lisongqian marked this conversation as resolved.
|
||
| .create(true) | ||
| .write(true) | ||
| .append(true) | ||
| .open(path) | ||
| .await | ||
| .map_err(|e| format!("open log file failed:{} file:{:?}", e, path))?; | ||
| Ok(Self { | ||
| path: path.to_path_buf(), | ||
| file, | ||
| }) | ||
| } | ||
|
|
||
| async fn reopen(&mut self) -> CResult<()> { | ||
| self.file | ||
| .flush() | ||
| .await | ||
| .map_err(|e| format!("flush log file before reopen failed:{}", e))?; | ||
| let file = OpenOptions::new() | ||
| .create(true) | ||
| .write(true) | ||
| .append(true) | ||
| .open(&self.path) | ||
| .await | ||
| .map_err(|e| format!("reopen log file failed:{} file:{:?}", e, self.path))?; | ||
| self.file = file; | ||
| Ok(()) | ||
| } | ||
|
lisongqian marked this conversation as resolved.
|
||
|
|
||
| async fn write(&mut self, content: &[u8]) -> CResult<()> { | ||
|
lisongqian marked this conversation as resolved.
|
||
| self.file | ||
| .write_all(content) | ||
| .await | ||
| .map_err(|e| format!("write log file failed:{} file:{:?}", e, self.path))?; | ||
| self.file | ||
| .flush() | ||
| .await | ||
| .map_err(|e| format!("flush log file failed:{} file:{:?}", e, self.path))?; | ||
| Ok(()) | ||
| } | ||
| } | ||
|
|
||
| impl Default for Log { | ||
| fn default() -> Self { | ||
| let (sender, _) = mpsc::channel::<(LogType, String)>(1); | ||
|
|
@@ -252,7 +302,7 @@ impl Log { | |
| }); | ||
|
|
||
| loop { | ||
| sleep(Duration::from_secs(1800)).await; | ||
| sleep(LOG_REOPEN_INTERVAL).await; | ||
| if let Err(e) = send.send((LogType::Rotate, "".to_string())).await { | ||
| eprintln!("send rotate failed:{}", e); | ||
| } | ||
|
|
@@ -264,51 +314,21 @@ impl Log { | |
| log_file_path: &PathBuf, | ||
| stat_file_path: &PathBuf, | ||
| ) -> CResult<()> { | ||
| let log_file = OpenOptions::new() | ||
| .create(true) | ||
| .write(true) | ||
| .append(true) | ||
| .open(log_file_path.clone()) | ||
| .await | ||
| .map_err(|e| format!("open log file failed:{} file:{:?}", e, log_file_path))?; | ||
| let mut log_writer = tokio::io::BufReader::new(log_file); | ||
|
|
||
| let stat_file = OpenOptions::new() | ||
| .create(true) | ||
| .write(true) | ||
| .append(true) | ||
| .open(stat_file_path.clone()) | ||
| .await | ||
| .map_err(|e| format!("open stat file failed:{} file:{:?}", e, stat_file_path))?; | ||
| let mut stat_writer = tokio::io::BufReader::new(stat_file); | ||
| let mut log_writer = ReopenableFile::open(log_file_path).await?; | ||
| let mut stat_writer = ReopenableFile::open(stat_file_path).await?; | ||
|
|
||
| //let lf = ['\n' as u8]; | ||
| while let Some(msg) = recv.recv().await { | ||
| match msg.0 { | ||
| LogType::Log => { | ||
| log_writer | ||
| .write_all(msg.1.as_bytes()) | ||
| .await | ||
| .map_err(|e| format!("write log file failed:{}", e))?; | ||
| //log_writer.write_all(&lf).await.map_err(|e| format!("write log file failed:{}", e)); | ||
| log_writer | ||
| .flush() | ||
| .await | ||
| .map_err(|e| format!("flush log failed:{}", e))?; | ||
| log_writer.write(msg.1.as_bytes()).await?; | ||
| } | ||
| LogType::Stat => { | ||
| stat_writer | ||
| .write_all(msg.1.as_bytes()) | ||
| .await | ||
| .map_err(|e| format!("write stat file failed:{}", e))?; | ||
| //stat_writer.write_all(&lf).await.map_err(|e| format!("write stat file failed:{}", e)); | ||
| stat_writer | ||
| .flush() | ||
| .await | ||
| .map_err(|e| format!("flush stat failed:{}", e))?; | ||
| stat_writer.write(msg.1.as_bytes()).await?; | ||
|
lisongqian marked this conversation as resolved.
|
||
| } | ||
| LogType::Rotate => { | ||
| break; | ||
| log_writer.reopen().await?; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The PR description says "keep the current descriptor usable when reopen fails; retry on later writes with a one-second cooldown" — that behavior is implemented on the VMM side, but on the CubeShim side a failed Two consequences worth noting:
This is recoverable, but it differs from the documented "keep old descriptor, retry with 1s cooldown" behavior. Consider matching the VMM's tolerant handling here (e.g. keep the old descriptor on reopen failure instead of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When Since |
||
| stat_writer.reopen().await?; | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -434,3 +454,80 @@ fn log_to_file(module: String, insid: String, log: String, func_type: String) -> | |
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
| use std::fs; | ||
| use std::sync::atomic::{AtomicU64, Ordering}; | ||
| use std::time::{SystemTime, UNIX_EPOCH}; | ||
|
|
||
| static NEXT_TEST_ID: AtomicU64 = AtomicU64::new(0); | ||
|
|
||
| fn test_log_path() -> PathBuf { | ||
| let suffix = SystemTime::now() | ||
| .duration_since(UNIX_EPOCH) | ||
| .expect("system clock before unix epoch") | ||
| .as_nanos(); | ||
| let test_id = NEXT_TEST_ID.fetch_add(1, Ordering::Relaxed); | ||
| std::env::temp_dir().join(format!( | ||
| "cube-shim-log-reopen-{}-{}-{}", | ||
| std::process::id(), | ||
| suffix, | ||
| test_id | ||
| )) | ||
| } | ||
|
|
||
| struct TestLogFiles { | ||
| active: PathBuf, | ||
| rotated: PathBuf, | ||
| } | ||
|
|
||
| impl TestLogFiles { | ||
| fn new() -> Self { | ||
| let active = test_log_path(); | ||
| let rotated = active.with_extension("log.1"); | ||
| Self { active, rotated } | ||
| } | ||
| } | ||
|
|
||
| impl Drop for TestLogFiles { | ||
| fn drop(&mut self) { | ||
| let _ = fs::remove_file(&self.active); | ||
| let _ = fs::remove_file(&self.rotated); | ||
| } | ||
| } | ||
|
|
||
| #[tokio::test] | ||
| async fn reopens_after_periodic_rotation() { | ||
| let files = TestLogFiles::new(); | ||
| let mut writer = ReopenableFile::open(&files.active).await.unwrap(); | ||
|
|
||
| writer.write(b"before\n").await.unwrap(); | ||
| fs::rename(&files.active, &files.rotated).unwrap(); | ||
| fs::File::create(&files.active).unwrap(); | ||
|
lisongqian marked this conversation as resolved.
lisongqian marked this conversation as resolved.
lisongqian marked this conversation as resolved.
|
||
| writer.reopen().await.unwrap(); | ||
| writer.write(b"after\n").await.unwrap(); | ||
| drop(writer); | ||
|
|
||
| assert_eq!(fs::read_to_string(&files.rotated).unwrap(), "before\n"); | ||
| assert_eq!(fs::read_to_string(&files.active).unwrap(), "after\n"); | ||
| } | ||
|
|
||
| #[tokio::test] | ||
| async fn failed_reopen_keeps_current_descriptor_usable() { | ||
| let files = TestLogFiles::new(); | ||
| let mut writer = ReopenableFile::open(&files.active).await.unwrap(); | ||
|
|
||
| writer.write(b"before\n").await.unwrap(); | ||
| writer.path = std::env::temp_dir(); | ||
| assert!(writer.reopen().await.is_err()); | ||
| writer.write(b"after\n").await.unwrap(); | ||
| drop(writer); | ||
|
|
||
| assert_eq!( | ||
| fs::read_to_string(&files.active).unwrap(), | ||
| "before\nafter\n" | ||
| ); | ||
| } | ||
| } | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.