abu_agent/hook/
auditfilelog.rs1use chrono::Utc;
2use serde::Serialize;
3use std::path::Path;
4use tokio::fs::{File, OpenOptions};
5use tokio::io::{AsyncWriteExt, BufWriter};
6use tokio::sync::Mutex;
7use super::{Hook, HookEvent};
8
9#[derive(Serialize)]
10struct AuditRecord<'a> {
11 timestamp: String,
12 #[serde(flatten)]
13 event: &'a HookEvent<'a>,
14}
15
16pub struct AuditFileLoggerHook {
17 writer: Mutex<BufWriter<File>>,
18}
19
20impl AuditFileLoggerHook {
21 pub async fn new(path: impl AsRef<Path>) -> std::io::Result<Self> {
22 let file = OpenOptions::new()
23 .create(true) .append(true) .open(path)
26 .await?;
27
28 let writer = BufWriter::new(file);
30
31 Ok(Self {
32 writer: Mutex::new(writer),
33 })
34 }
35}
36
37#[async_trait::async_trait]
38impl Hook for AuditFileLoggerHook {
39 type Error = std::io::Error;
40
41 async fn on_event(&self, event: &HookEvent<'_>) -> Result<(), Self::Error> {
42 let record = AuditRecord {
44 timestamp: Utc::now().to_rfc3339(),
45 event,
46 };
47
48 let mut json_line = serde_json::to_string(&record).map_err(|e| {
50 std::io::Error::new(
51 std::io::ErrorKind::InvalidData,
52 format!("Failed to serialize event: {}", e),
53 )
54 })?;
55
56 json_line.push('\n');
58
59 let mut writer = self.writer.lock().await;
61 writer.write_all(json_line.as_bytes()).await?;
62 writer.flush().await?;
63
64 Ok(())
65 }
66}