1use crate::error::AuditError;
7use crate::types::{AuditEvent, AuditLevel};
8use async_trait::async_trait;
9use std::sync::Arc;
10
11#[derive(Debug, Clone)]
13pub struct AuditConfig {
14 pub min_level: AuditLevel,
16 pub include_debug: bool,
18 pub max_payload_size: usize,
20 pub redact_sensitive: bool,
22 pub redact_fields: Vec<String>,
24 pub enabled: bool,
26 pub buffer_size: usize,
28 pub flush_interval_secs: u64,
30}
31
32impl Default for AuditConfig {
33 fn default() -> Self {
34 Self {
35 min_level: AuditLevel::Info,
36 include_debug: false,
37 max_payload_size: 10 * 1024, redact_sensitive: true,
39 redact_fields: vec![
40 "password".to_string(),
41 "secret".to_string(),
42 "token".to_string(),
43 "api_key".to_string(),
44 "apiKey".to_string(),
45 "authorization".to_string(),
46 ],
47 enabled: true,
48 buffer_size: 1000,
49 flush_interval_secs: 5,
50 }
51 }
52}
53
54impl AuditConfig {
55 pub fn new() -> Self {
57 Self::default()
58 }
59
60 pub fn development() -> Self {
62 Self {
63 min_level: AuditLevel::Debug,
64 include_debug: true,
65 redact_sensitive: false,
66 ..Default::default()
67 }
68 }
69
70 pub fn production() -> Self {
72 Self {
73 min_level: AuditLevel::Info,
74 include_debug: false,
75 redact_sensitive: true,
76 max_payload_size: 5 * 1024, ..Default::default()
78 }
79 }
80
81 pub fn with_min_level(mut self, level: AuditLevel) -> Self {
83 self.min_level = level;
84 self
85 }
86
87 pub fn with_debug(mut self, include: bool) -> Self {
89 self.include_debug = include;
90 self
91 }
92
93 pub fn with_max_payload_size(mut self, size: usize) -> Self {
95 self.max_payload_size = size;
96 self
97 }
98
99 pub fn with_redaction(mut self, enabled: bool) -> Self {
101 self.redact_sensitive = enabled;
102 self
103 }
104
105 pub fn with_redact_fields(mut self, fields: Vec<String>) -> Self {
107 self.redact_fields.extend(fields);
108 self
109 }
110
111 pub fn enabled(mut self, enabled: bool) -> Self {
113 self.enabled = enabled;
114 self
115 }
116
117 pub fn with_buffer_size(mut self, size: usize) -> Self {
119 self.buffer_size = size;
120 self
121 }
122
123 pub fn should_log(&self, level: AuditLevel) -> bool {
125 self.enabled && level >= self.min_level
126 }
127}
128
129#[async_trait]
133pub trait AuditLogger: Send + Sync {
134 async fn log(&self, event: AuditEvent) -> Result<(), AuditError>;
136
137 async fn log_batch(&self, events: Vec<AuditEvent>) -> Result<(), AuditError> {
139 for event in events {
140 self.log(event).await?;
141 }
142 Ok(())
143 }
144
145 async fn flush(&self) -> Result<(), AuditError>;
147
148 fn name(&self) -> &str;
150
151 async fn health_check(&self) -> Result<(), AuditError> {
153 Ok(())
154 }
155
156 async fn stats(&self) -> AuditStats {
158 AuditStats::default()
159 }
160}
161
162#[derive(Debug, Clone, Default)]
164pub struct AuditStats {
165 pub total_events: u64,
167 pub events_by_level: std::collections::HashMap<AuditLevel, u64>,
169 pub failed_events: u64,
171 pub bytes_written: u64,
173 pub last_event_time: Option<chrono::DateTime<chrono::Utc>>,
175}
176
177pub struct CompositeLogger {
179 loggers: Vec<Arc<dyn AuditLogger>>,
180 config: AuditConfig,
181}
182
183impl CompositeLogger {
184 pub fn new(config: AuditConfig) -> Self {
186 Self {
187 loggers: Vec::new(),
188 config,
189 }
190 }
191
192 pub fn add_logger(mut self, logger: Arc<dyn AuditLogger>) -> Self {
194 self.loggers.push(logger);
195 self
196 }
197
198 pub fn with_logger(&mut self, logger: Arc<dyn AuditLogger>) {
200 self.loggers.push(logger);
201 }
202}
203
204#[async_trait]
205impl AuditLogger for CompositeLogger {
206 async fn log(&self, event: AuditEvent) -> Result<(), AuditError> {
207 if !self.config.should_log(event.level) {
208 return Ok(());
209 }
210
211 let mut errors = Vec::new();
212 for logger in &self.loggers {
213 if let Err(e) = logger.log(event.clone()).await {
214 errors.push(format!("{}: {}", logger.name(), e));
215 }
216 }
217
218 if errors.is_empty() {
219 Ok(())
220 } else {
221 Err(AuditError::Multiple(errors))
222 }
223 }
224
225 async fn flush(&self) -> Result<(), AuditError> {
226 let mut errors = Vec::new();
227 for logger in &self.loggers {
228 if let Err(e) = logger.flush().await {
229 errors.push(format!("{}: {}", logger.name(), e));
230 }
231 }
232
233 if errors.is_empty() {
234 Ok(())
235 } else {
236 Err(AuditError::Multiple(errors))
237 }
238 }
239
240 fn name(&self) -> &str {
241 "composite"
242 }
243
244 async fn health_check(&self) -> Result<(), AuditError> {
245 for logger in &self.loggers {
246 logger.health_check().await?;
247 }
248 Ok(())
249 }
250}
251
252pub struct NoOpLogger;
254
255#[async_trait]
256impl AuditLogger for NoOpLogger {
257 async fn log(&self, _event: AuditEvent) -> Result<(), AuditError> {
258 Ok(())
259 }
260
261 async fn flush(&self) -> Result<(), AuditError> {
262 Ok(())
263 }
264
265 fn name(&self) -> &str {
266 "noop"
267 }
268}
269
270#[derive(Default)]
272pub struct MemoryLogger {
273 events: tokio::sync::RwLock<Vec<AuditEvent>>,
274}
275
276impl MemoryLogger {
277 pub fn new() -> Self {
279 Self::default()
280 }
281
282 pub async fn events(&self) -> Vec<AuditEvent> {
284 self.events.read().await.clone()
285 }
286
287 pub async fn count(&self) -> usize {
289 self.events.read().await.len()
290 }
291
292 pub async fn clear(&self) {
294 self.events.write().await.clear();
295 }
296}
297
298#[async_trait]
299impl AuditLogger for MemoryLogger {
300 async fn log(&self, event: AuditEvent) -> Result<(), AuditError> {
301 self.events.write().await.push(event);
302 Ok(())
303 }
304
305 async fn flush(&self) -> Result<(), AuditError> {
306 Ok(())
307 }
308
309 fn name(&self) -> &str {
310 "memory"
311 }
312
313 async fn stats(&self) -> AuditStats {
314 let events = self.events.read().await;
315 let mut stats = AuditStats {
316 total_events: events.len() as u64,
317 ..Default::default()
318 };
319
320 for event in events.iter() {
321 *stats.events_by_level.entry(event.level).or_insert(0) += 1;
322 }
323
324 if let Some(last) = events.last() {
325 stats.last_event_time = Some(last.timestamp);
326 }
327
328 stats
329 }
330}
331
332#[cfg(test)]
333mod tests {
334 use super::*;
335 use crate::types::EventKind;
336
337 #[test]
338 fn test_audit_config_defaults() {
339 let config = AuditConfig::default();
340 assert_eq!(config.min_level, AuditLevel::Info);
341 assert!(config.enabled);
342 assert!(config.redact_sensitive);
343 }
344
345 #[test]
346 fn test_audit_config_should_log() {
347 let config = AuditConfig::new().with_min_level(AuditLevel::Warn);
348
349 assert!(!config.should_log(AuditLevel::Debug));
350 assert!(!config.should_log(AuditLevel::Info));
351 assert!(config.should_log(AuditLevel::Warn));
352 assert!(config.should_log(AuditLevel::Error));
353 assert!(config.should_log(AuditLevel::Critical));
354 }
355
356 #[test]
357 fn test_audit_config_disabled() {
358 let config = AuditConfig::new().enabled(false);
359 assert!(!config.should_log(AuditLevel::Critical));
360 }
361
362 #[tokio::test]
363 async fn test_memory_logger() {
364 let logger = MemoryLogger::new();
365
366 let event = AuditEvent::info(EventKind::Custom {
367 name: "test".to_string(),
368 payload: serde_json::json!({}),
369 });
370
371 logger.log(event).await.unwrap();
372 assert_eq!(logger.count().await, 1);
373
374 let events = logger.events().await;
375 assert_eq!(events.len(), 1);
376 }
377
378 #[tokio::test]
379 async fn test_noop_logger() {
380 let logger = NoOpLogger;
381 let event = AuditEvent::info(EventKind::Custom {
382 name: "test".to_string(),
383 payload: serde_json::json!({}),
384 });
385
386 assert!(logger.log(event).await.is_ok());
387 assert!(logger.flush().await.is_ok());
388 }
389
390 #[tokio::test]
391 async fn test_composite_logger() {
392 let memory1 = Arc::new(MemoryLogger::new());
393 let memory2 = Arc::new(MemoryLogger::new());
394
395 let composite = CompositeLogger::new(AuditConfig::default())
396 .add_logger(memory1.clone())
397 .add_logger(memory2.clone());
398
399 let event = AuditEvent::info(EventKind::Custom {
400 name: "test".to_string(),
401 payload: serde_json::json!({}),
402 });
403
404 composite.log(event).await.unwrap();
405
406 assert_eq!(memory1.count().await, 1);
407 assert_eq!(memory2.count().await, 1);
408 }
409}