Skip to main content

abu_agent/hook/
auditfilelog.rs

1use 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)   // 如果文件不存在则创建
24            .append(true)   // 追加模式,防止覆盖历史日志
25            .open(path)
26            .await?;
27
28        // 使用 BufWriter 减少频繁的系统调用 (默认 8KB 缓存)
29        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        // 1. 组装带有时间戳的审计记录
43        let record = AuditRecord {
44            timestamp: Utc::now().to_rfc3339(),
45            event,
46        };
47
48        // 2. 将事件序列化为单行 JSON 字符串 (JSONL 格式)
49        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        // 追加换行符
57        json_line.push('\n');
58
59        // 3. 异步获取锁并写入文件
60        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}