Skip to main content

agentmux_common/
log_retention.rs

1// Copyright 2026, AgentMux Corp.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Log-file retention, shared by srv and the CEF host (each rotates its own
5//! logs into its own directory).
6
7use std::path::Path;
8use std::time::{Duration, SystemTime};
9
10/// Delete log files (*.log.*) older than `days` to prevent unbounded growth.
11/// Only touches files with `.log.` in the name — pointer files and other data are safe.
12pub fn cleanup_old_logs(log_dir: &Path, days: u64) {
13    let cutoff = SystemTime::now() - Duration::from_secs(days * 86400);
14    let Ok(entries) = std::fs::read_dir(log_dir) else { return };
15    for entry in entries.flatten() {
16        let path = entry.path();
17        if !path.to_string_lossy().contains(".log.") {
18            continue;
19        }
20        if let Ok(meta) = entry.metadata() {
21            if let Ok(modified) = meta.modified() {
22                if modified < cutoff {
23                    let _ = std::fs::remove_file(&path);
24                }
25            }
26        }
27    }
28}
29
30#[cfg(test)]
31mod tests {
32    use super::*;
33    use std::fs::{File, FileTimes};
34
35    fn aged(dir: &Path, name: &str, age_days: u64) -> std::path::PathBuf {
36        let p = dir.join(name);
37        let f = File::create(&p).unwrap();
38        let t = SystemTime::now() - Duration::from_secs(age_days * 86400);
39        f.set_times(FileTimes::new().set_modified(t)).unwrap();
40        p
41    }
42
43    #[test]
44    fn deletes_only_old_rotated_logs() {
45        let tmp = tempfile::tempdir().unwrap();
46        let dir = tmp.path();
47        let old_log = aged(dir, "srv.log.2026-09-01", 30);
48        let new_log = aged(dir, "srv.log.2026-09-29", 1);
49        let old_other = aged(dir, "current.txt", 30);
50        cleanup_old_logs(dir, 7);
51        assert!(!old_log.exists(), "an old rotated log is deleted");
52        assert!(new_log.exists(), "a recent rotated log stays");
53        assert!(old_other.exists(), "a file without `.log.` in its name is never touched");
54    }
55
56    #[test]
57    fn a_missing_directory_is_not_an_error() {
58        cleanup_old_logs(Path::new("/nonexistent/agentmux-log-retention-test"), 7);
59    }
60}