util/file_log: close the log file explicitly
@@ -51,13 +51,13 @@ pub struct RotatingFileLogger {
rotation_timespan: Duration,
next_rotation_time: DateTime<Utc>,
file_path: PathBuf,
- file: File,
+ file: Option<File>,
}
impl RotatingFileLogger {
pub fn new(file_path: impl AsRef<Path>, rotation_timespan: Duration) -> io::Result<Self> {
let file_path = file_path.as_ref().to_path_buf();
- let file = open_log_file(&file_path)?;
+ let file = Some(open_log_file(&file_path)?);
let file_attr = fs::metadata(&file_path)?;
let file_modified_time = file_attr.modified().unwrap().into();
let next_rotation_time = compute_rotation_time(&file_modified_time, rotation_timespan);
@@ -70,7 +70,7 @@ impl RotatingFileLogger {
}
fn open(&mut self) -> io::Result<()> {
- self.file = open_log_file(&self.file_path)?;
+ self.file = Some(open_log_file(&self.file_path)?);
Ok(())
}
@@ -91,21 +91,25 @@ impl RotatingFileLogger {
self.next_rotation_time = compute_rotation_time(&now, self.rotation_timespan);
}
+ /// Flushes and closes log file, without rotation.
fn close(&mut self) -> io::Result<()> {
- self.file.flush()
+ assert!(self.file.is_some());
+ self.file.take().unwrap().flush()
}
}
impl Write for RotatingFileLogger {
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
- self.file.write(bytes)
+ assert!(self.file.is_some());
+ self.file.as_mut().unwrap().write(bytes)
}
fn flush(&mut self) -> io::Result<()> {
if self.should_rotate() {
self.rotate()?;
};
- self.file.flush()
+ assert!(self.file.is_some());
+ self.file.as_mut().unwrap().flush()
}
}
@@ -164,4 +168,28 @@ mod tests {
assert!(file_exists(&rotated_file));
assert!(!logger.should_rotate());
}
+
+ #[test]
+ fn test_close_file_logger() {
+ let tmp_dir = TempDir::new("").unwrap();
+ let log_file = tmp_dir
+ .path()
+ .join("test_close_file_logger.log")
+ .to_str()
+ .unwrap()
+ .to_string();
+ let one_day = Duration::days(1);
+
+ let mut logger = RotatingFileLogger::new(&log_file, one_day).unwrap();
+ // Handles written amount returned.
+ let _ = logger.write(b"write before close").unwrap();
+ logger.flush().unwrap();
+ logger.close().unwrap();
+ assert!(::panic_hook::recover_safe(|| logger.write(b"write after close")).is_err());
+ assert!(::panic_hook::recover_safe(|| logger.flush()).is_err());
+ assert!(::panic_hook::recover_safe(|| logger.close()).is_err());
+ // Reopens file, otherwise `close()` will fail in assertion when `drop()`.
+ logger.open().unwrap();
+ drop(logger);
+ }
}
