use std::io::{Read, Seek, SeekFrom, Write};
use std::path::Path;
pub(crate) const MAX_AGENT_LOG_BYTES: u64 = 32 * 1024 * 1024;
pub(crate) fn cap_agent_log_if_oversized(path: &Path) -> std::io::Result<bool> {
cap_agent_log_to(path, MAX_AGENT_LOG_BYTES)
}
fn cap_agent_log_to(path: &Path, max_bytes: u64) -> std::io::Result<bool> {
let len = match std::fs::metadata(path) {
Ok(meta) => meta.len(),
Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(false),
Err(err) => return Err(err),
};
if len <= max_bytes {
return Ok(false);
}
let omitted = len - max_bytes;
let mut file = std::fs::OpenOptions::new()
.read(true)
.write(true)
.open(path)?;
file.seek(SeekFrom::Start(omitted))?;
let mut tail = Vec::with_capacity(max_bytes as usize);
#[allow(
clippy::verbose_file_reads,
reason = "reads from the sought offset to end-of-file for the log's tail, not the whole \
file `fs::read` would read"
)]
file.read_to_end(&mut tail)?;
file.set_len(0)?;
file.seek(SeekFrom::Start(0))?;
let marker =
format!("... [{omitted} bytes truncated; agent.log capped at {max_bytes} bytes] ...\n");
file.write_all(marker.as_bytes())?;
file.write_all(&tail)?;
Ok(true)
}
pub(crate) fn cap_agent_log_or_warn(path: &Path) {
if let Err(err) = cap_agent_log_if_oversized(path) {
log::warn!("cleanup: failed to cap {}: {err}", path.display());
}
}
#[cfg(test)]
#[path = "log_cap_tests.rs"]
mod log_cap_tests;