Skip to content

Commit 5b5e501

Browse files
authored
feat(lambda-rs-logging): Update log files to be kept open on flush
2 parents 0ffa1bf + 88f1812 commit 5b5e501

File tree

2 files changed

+73
-16
lines changed

2 files changed

+73
-16
lines changed

crates/lambda-rs-logging/src/handler.rs

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
//! Log handling implementations for the logger.
22
33
use std::{
4-
fs::OpenOptions,
4+
fs::{
5+
File,
6+
OpenOptions,
7+
},
58
io::{
69
self,
710
IsTerminal,
@@ -25,15 +28,21 @@ pub trait Handler: Send + Sync {
2528
/// A handler that logs to a file.
2629
#[derive(Debug)]
2730
pub struct FileHandler {
28-
file: String,
2931
log_buffer: Mutex<Vec<String>>,
32+
writer: Mutex<io::BufWriter<File>>,
3033
}
3134

3235
impl FileHandler {
33-
pub fn new(file: String) -> Self {
36+
pub fn new(path: String) -> Self {
37+
let log_file = OpenOptions::new()
38+
.append(true)
39+
.create(true)
40+
.open(&path)
41+
.expect("open log file");
42+
3443
Self {
35-
file,
3644
log_buffer: Mutex::new(Vec::new()),
45+
writer: Mutex::new(io::BufWriter::new(log_file)),
3746
}
3847
}
3948
}
@@ -59,27 +68,30 @@ impl Handler for FileHandler {
5968
LogLevel::FATAL => format!("\x1B[31;1m{}\x1B[0m", log_message),
6069
};
6170

62-
let mut buf = self.log_buffer.lock().unwrap();
63-
buf.push(colored_message);
71+
let mut buffer = match self.log_buffer.lock() {
72+
Ok(guard) => guard,
73+
Err(poisoned) => poisoned.into_inner(),
74+
};
75+
buffer.push(colored_message);
6476

6577
// Flush buffer every ten messages.
66-
if buf.len() < 10 {
78+
if buffer.len() < 10 {
6779
return;
6880
}
6981

70-
let log_message = buf.join("\n");
82+
let messages = std::mem::take(&mut *buffer);
83+
drop(buffer);
7184

72-
let mut file = OpenOptions::new()
73-
.append(true)
74-
.create(true)
75-
.open(self.file.clone())
76-
.unwrap();
85+
let log_message = messages.join("\n");
7786

78-
file
87+
let mut writer = match self.writer.lock() {
88+
Ok(guard) => guard,
89+
Err(poisoned) => poisoned.into_inner(),
90+
};
91+
writer
7992
.write_all(log_message.as_bytes())
8093
.expect("Unable to write data");
81-
82-
buf.clear();
94+
let _ = writer.flush();
8395
}
8496
}
8597

crates/lambda-rs-logging/src/lib.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,51 @@ mod tests {
502502
assert!(!content.is_empty());
503503
}
504504

505+
#[test]
506+
#[cfg(unix)]
507+
fn file_handler_does_not_reopen_between_flushes() {
508+
use std::{
509+
fs,
510+
time::UNIX_EPOCH,
511+
};
512+
513+
let tmp = std::env::temp_dir();
514+
let base = format!(
515+
"lambda_logging_persist_{}_{}",
516+
std::process::id(),
517+
SystemTime::now()
518+
.duration_since(UNIX_EPOCH)
519+
.unwrap()
520+
.as_nanos()
521+
);
522+
let original = tmp.join(format!("{}.log", base));
523+
let moved = tmp.join(format!("{}_moved.log", base));
524+
525+
let logger = Logger::new(LogLevel::TRACE, "file_reopen");
526+
logger.add_handler(Box::new(crate::handler::FileHandler::new(
527+
original.to_string_lossy().to_string(),
528+
)));
529+
530+
for i in 0..10 {
531+
logger.info(format!("batch1_line{}", i));
532+
}
533+
534+
fs::rename(&original, &moved).expect("rename log file while handler lives");
535+
536+
for i in 0..10 {
537+
logger.info(format!("batch2_line{}", i));
538+
}
539+
540+
assert!(
541+
!original.exists(),
542+
"handler should not reopen and recreate the original path"
543+
);
544+
545+
let content = fs::read_to_string(&moved).expect("moved file must exist");
546+
assert!(content.contains("batch1_line0"));
547+
assert!(content.contains("batch2_line0"));
548+
}
549+
505550
#[test]
506551
fn macro_early_guard_avoids_formatting() {
507552
// Ensure TRACE is disabled by setting level to INFO.

0 commit comments

Comments
 (0)