use std::path::PathBuf;
use tokio::sync::mpsc;
use super::super::cloud::tamper::TamperEvent;
#[derive(Clone)]
pub struct TamperLogger {
tx: mpsc::Sender<TamperEvent>,
}
pub struct TamperLoggerHandle {
_join_handle: tokio::task::JoinHandle<()>,
}
impl TamperLogger {
pub fn new(log_dir: PathBuf) -> (Self, TamperLoggerHandle) {
let (tx, rx) = mpsc::channel(256);
let handle = TamperLoggerHandle {
_join_handle: tokio::spawn(writer_task(log_dir, rx)),
};
(Self { tx }, handle)
}
pub fn log(&self, event: TamperEvent) {
if self.tx.try_send(event).is_err() {
tracing::warn!("tamper log channel full or closed, event dropped");
}
}
}
async fn writer_task(log_dir: PathBuf, mut rx: mpsc::Receiver<TamperEvent>) {
let path = log_dir.join("tamper.jsonl");
if let Some(parent) = path.parent() {
let _ = std::fs::create_dir_all(parent);
}
while let Some(event) = rx.recv().await {
match serde_json::to_string(&event) {
Ok(json_line) => {
use std::io::Write;
let mut file = match std::fs::OpenOptions::new()
.create(true)
.append(true)
.open(&path)
{
Ok(f) => f,
Err(e) => {
tracing::warn!(error = %e, "cannot open tamper.jsonl");
continue;
}
};
if let Err(e) = writeln!(file, "{json_line}") {
tracing::warn!(error = %e, "cannot write to tamper.jsonl");
}
}
Err(e) => {
tracing::warn!(error = %e, "cannot serialize tamper event");
}
}
}
}