cortexai_audit/lib.rs
1//! Audit logging for AI agents.
2//!
3//! This crate provides comprehensive audit logging for all tool calls,
4//! LLM requests, and agent lifecycle events. It supports multiple backends
5//! and is designed for compliance and debugging in production environments.
6//!
7//! # Features
8//!
9//! - **Structured Events**: Rich event types for tool calls, LLM requests, errors, and more
10//! - **Multiple Backends**: File, JSON, and async logging with rotation support
11//! - **Configurable Levels**: Filter events by severity (Debug, Info, Warn, Error, Critical)
12//! - **Log Rotation**: Size-based, daily, or hourly rotation with cleanup
13//! - **Non-blocking**: Async wrapper for high-throughput scenarios
14//! - **Compliance Ready**: Designed for GDPR, SOC2, and enterprise requirements
15//!
16//! # Quick Start
17//!
18//! ```rust,no_run
19//! use cortexai_audit::{
20//! AuditEvent, AuditContext, JsonFileLogger, RotationConfig, RotationPolicy, AuditLogger
21//! };
22//!
23//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
24//! // Create a JSON file logger with daily rotation
25//! let rotation = RotationConfig::new(RotationPolicy::Daily)
26//! .with_max_files(30);
27//!
28//! let logger = JsonFileLogger::new(
29//! "/var/log/agents/audit.jsonl",
30//! Default::default(),
31//! rotation,
32//! ).await?;
33//!
34//! // Log a tool call
35//! let event = AuditEvent::tool_call("read_file", serde_json::json!({"path": "/tmp/data.txt"}), true)
36//! .with_context(
37//! AuditContext::new()
38//! .with_trace_id("req-123")
39//! .with_agent_id("research-agent")
40//! );
41//!
42//! logger.log(event).await?;
43//! logger.flush().await?;
44//! # Ok(())
45//! # }
46//! ```
47//!
48//! # Event Types
49//!
50//! The crate supports various event types through `EventKind`:
51//!
52//! - `ToolCall`: Tool invocations with parameters and results
53//! - `LlmRequest`: Outgoing LLM API requests
54//! - `LlmResponse`: Incoming LLM responses
55//! - `AgentLifecycle`: Agent start, stop, pause, resume events
56//! - `ApprovalDecision`: Human-in-the-loop approval decisions
57//! - `Error`: Error events with stack traces
58//! - `Security`: Security-related events (auth, rate limits)
59//! - `Custom`: Extensible custom events
60//!
61//! # Backends
62//!
63//! ## FileLogger
64//! Simple human-readable text format, good for development.
65//!
66//! ## JsonFileLogger
67//! Structured JSON Lines format with rotation, ideal for production.
68//!
69//! ## AsyncLogger
70//! Non-blocking wrapper around any logger for high-throughput scenarios.
71//!
72//! ## MemoryLogger
73//! In-memory storage for testing.
74//!
75//! ## CompositeLogger
76//! Writes to multiple backends simultaneously.
77//!
78//! # Configuration
79//!
80//! ```rust
81//! use cortexai_audit::{AuditConfig, AuditLevel};
82//!
83//! // Development config - verbose logging
84//! let dev_config = AuditConfig::development();
85//!
86//! // Production config - info level with redaction
87//! let prod_config = AuditConfig::production();
88//!
89//! // Custom config
90//! let custom_config = AuditConfig::new()
91//! .with_min_level(AuditLevel::Warn)
92//! .with_redaction(true)
93//! .with_max_payload_size(5 * 1024);
94//! ```
95
96pub mod backends;
97pub mod error;
98pub mod traits;
99pub mod types;
100
101// Re-export main types
102pub use error::AuditError;
103pub use traits::{AuditConfig, AuditLogger, AuditStats, CompositeLogger, MemoryLogger, NoOpLogger};
104pub use types::{
105 AuditContext, AuditEvent, AuditEventBuilder, AuditLevel, EventKind, LifecycleAction,
106 SecurityEventType,
107};
108
109// Re-export backends
110pub use backends::{
111 AsyncLogger, AsyncLoggerBuilder, FileLogger, JsonFileLogger, RotationConfig, RotationPolicy,
112};
113
114/// Convenience function to create a production-ready logger.
115///
116/// Creates a JSON file logger with:
117/// - Daily rotation
118/// - 30 days retention
119/// - Production-level configuration
120pub async fn create_production_logger(
121 path: impl Into<std::path::PathBuf>,
122) -> Result<JsonFileLogger, AuditError> {
123 let rotation = RotationConfig::new(RotationPolicy::Daily).with_max_files(30);
124 JsonFileLogger::new(path, AuditConfig::production(), rotation).await
125}
126
127/// Convenience function to create a development logger.
128///
129/// Creates a file logger with debug-level logging.
130pub async fn create_dev_logger(
131 path: impl Into<std::path::PathBuf>,
132) -> Result<FileLogger, AuditError> {
133 FileLogger::new(path, AuditConfig::development()).await
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 #[tokio::test]
141 async fn test_memory_logger_integration() {
142 let logger = MemoryLogger::new();
143
144 // Log various events
145 logger
146 .log(AuditEvent::tool_call("tool1", serde_json::json!({}), true))
147 .await
148 .unwrap();
149 logger
150 .log(AuditEvent::llm_request("anthropic", "claude-3", false))
151 .await
152 .unwrap();
153 logger
154 .log(AuditEvent::error_event(
155 "RuntimeError",
156 "Something went wrong",
157 ))
158 .await
159 .unwrap();
160
161 assert_eq!(logger.count().await, 3);
162
163 let events = logger.events().await;
164 assert!(matches!(events[0].kind, EventKind::ToolCall { .. }));
165 assert!(matches!(events[1].kind, EventKind::LlmRequest { .. }));
166 assert!(matches!(events[2].kind, EventKind::Error { .. }));
167 }
168
169 #[test]
170 fn test_event_builder() {
171 let event = AuditEventBuilder::new()
172 .level(AuditLevel::Info)
173 .kind(EventKind::Custom {
174 name: "test".to_string(),
175 payload: serde_json::json!({"key": "value"}),
176 })
177 .trace_id("trace-1")
178 .session_id("session-1")
179 .agent_id("agent-1")
180 .build();
181
182 assert_eq!(event.level, AuditLevel::Info);
183 assert_eq!(event.context.trace_id, Some("trace-1".to_string()));
184 assert_eq!(event.context.session_id, Some("session-1".to_string()));
185 assert_eq!(event.context.agent_id, Some("agent-1".to_string()));
186 }
187
188 #[test]
189 fn test_config_presets() {
190 let dev = AuditConfig::development();
191 assert_eq!(dev.min_level, AuditLevel::Debug);
192 assert!(dev.include_debug);
193 assert!(!dev.redact_sensitive);
194
195 let prod = AuditConfig::production();
196 assert_eq!(prod.min_level, AuditLevel::Info);
197 assert!(!prod.include_debug);
198 assert!(prod.redact_sensitive);
199 }
200}