返回 CodeWhale
audit.rs
根目录 / crates / tui / src / audit.rs
1 //! Lightweight audit logging for sensitive operations.
2
3 use std::fs;
4 use std::path::PathBuf;
5
6 use chrono::Utc;
7 use serde_json::{Value, json};
8
9 use crate::utils::{flush_and_sync, open_append};
10
11 /// Append an audit event to `$CODEWHALE_HOME/audit.log` (or the default
12 /// `~/.codewhale/audit.log` when no explicit CodeWhale home is configured).
13 ///
14 /// This helper is best-effort by design: callers should not fail critical flows
15 /// if audit persistence fails.
16 pub fn log_sensitive_event(event: &str, details: Value) {
17 if let Err(err) = append_event(event, details) {
18 crate::logging::warn(format!("audit log write failed: {err}"));
19 }
20 }
21
22 fn append_event(event: &str, details: Value) -> anyhow::Result<()> {
23 let path = default_audit_path()?;
24 let parent = path.parent().map(|p| p.to_path_buf());
25 if let Some(ref parent) = parent {
26 fs::create_dir_all(parent)?;
27 }
28 // Open for append with a BufWriter for buffered I/O, then flush + fsync
29 // after each event so the record is durably on disk.
30 let mut writer = open_append(&path)?;
31 let record = json!({
32 "ts": Utc::now().to_rfc3339(),
33 "event": event,
34 "details": details,
35 });
36 let line = serde_json::to_string(&record)?;
37 use std::io::Write;
38 writeln!(writer, "{line}")?;
39 flush_and_sync(&mut writer)?;
40 Ok(())
41 }
42
43 fn default_audit_path() -> anyhow::Result<PathBuf> {
44 Ok(codewhale_config::codewhale_home()?.join("audit.log"))
45 }
46
46 lines RUST