Skip to main content

rns_server/
logs.rs

1use std::fs::{self, OpenOptions};
2use std::io::Write;
3use std::path::PathBuf;
4use std::time::{SystemTime, UNIX_EPOCH};
5
6#[derive(Clone)]
7pub struct LogStore {
8    dir: PathBuf,
9}
10
11impl LogStore {
12    pub fn new(dir: PathBuf) -> Self {
13        Self { dir }
14    }
15
16    pub fn process_log_path(&self, process: &str) -> PathBuf {
17        self.dir.join(format!("{process}.log"))
18    }
19
20    pub fn append_line(&self, process: &str, stream: &str, line: &str) -> Result<(), String> {
21        fs::create_dir_all(&self.dir)
22            .map_err(|err| format!("failed to create log dir {}: {}", self.dir.display(), err))?;
23        let path = self.process_log_path(process);
24        let mut file = OpenOptions::new()
25            .create(true)
26            .append(true)
27            .open(&path)
28            .map_err(|err| format!("failed to open process log {}: {}", path.display(), err))?;
29        writeln!(file, "{} [{}] {}", unix_timestamp_secs(), stream, line)
30            .map_err(|err| format!("failed to append process log {}: {}", path.display(), err))
31    }
32}
33
34fn unix_timestamp_secs() -> u64 {
35    SystemTime::now()
36        .duration_since(UNIX_EPOCH)
37        .unwrap_or_default()
38        .as_secs()
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44
45    #[test]
46    fn log_store_appends_process_output() {
47        let dir = std::env::temp_dir().join(format!("rns-server-logs-{}", std::process::id()));
48        let store = LogStore::new(dir.clone());
49
50        store.append_line("rnsd", "stdout", "started").unwrap();
51        store.append_line("rnsd", "stderr", "warning").unwrap();
52
53        let body = std::fs::read_to_string(store.process_log_path("rnsd")).unwrap();
54        assert!(body.contains("[stdout] started"));
55        assert!(body.contains("[stderr] warning"));
56
57        let _ = std::fs::remove_dir_all(dir);
58    }
59
60    #[test]
61    fn process_log_path_is_per_process() {
62        let store = LogStore::new(PathBuf::from("/tmp/rns/logs"));
63        assert_eq!(
64            store.process_log_path("rns-statsd"),
65            std::path::Path::new("/tmp/rns/logs/rns-statsd.log")
66        );
67    }
68}