Audit Logging System
Overview
HeliosDB Nano includes an audit logging system that tracks all DDL and DML operations for compliance, security, and debugging purposes. The audit system is designed to be:
- Tamper-proof: Append-only log with cryptographic checksums
- High-performance: Asynchronous logging that doesn’t block operations
- Configurable: Fine-grained control over what gets logged
- Queryable: SQL interface to query audit logs
- Compliant: Supports SOC2, HIPAA, GDPR requirements
Features
Operation Tracking
The audit system tracks:
- DDL Operations: CREATE TABLE, DROP TABLE, ALTER TABLE, CREATE INDEX, DROP INDEX
- DML Operations: INSERT, UPDATE, DELETE
- SELECT Queries: Optional (disabled by default due to verbosity)
- Transaction Operations: BEGIN, COMMIT, ROLLBACK
- Authentication: LOGIN, LOGOUT, GRANT, REVOKE
- System Operations: BACKUP, RESTORE, VACUUM
Metadata Captured
For each operation, the audit log captures:
- Timestamp: When the operation occurred
- Session ID: Unique identifier for the session
- User: Who performed the operation
- Operation Type: Type of SQL operation
- Target: Table or object affected
- Query: Full SQL query text
- Affected Rows: Number of rows changed
- Success/Failure: Whether the operation succeeded
- Error Message: If the operation failed
- Checksum: Cryptographic hash for tamper detection
Optional metadata (configurable):
- Client IP address
- Application name
- Database name
- Query execution time
Configuration
Basic Configuration
use heliosdb_nano::audit::AuditConfig;
// Default configurationlet config = AuditConfig::default();// Logs: DDL, DML (not SELECT), Auth// Retention: 90 days// Checksums: Enabled
// Minimal configuration (DDL only)let config = AuditConfig::minimal();// Logs: DDL only// Retention: 30 days// Checksums: Disabled (for performance)
// Verbose configuration (everything)let config = AuditConfig::verbose();// Logs: DDL, DML, SELECT, Transactions, Auth// Retention: 365 days// Checksums: Enabled
// Compliance configuration (SOC2, HIPAA, GDPR)let config = AuditConfig::compliance();// Logs: DDL, DML, Transactions, Auth// Retention: 2555 days (7 years)// Checksums: Enabled (tamper detection)Custom Configuration
use heliosdb_nano::audit::{AuditConfig, MetadataCapture};
let config = AuditConfig { enabled: true, log_ddl: true, log_dml: true, log_select: false, log_transactions: true, log_auth: true, retention_days: 180, async_buffer_size: 100, enable_checksums: true, max_query_length: 10000, capture_metadata: MetadataCapture { capture_client_ip: true, capture_application_name: true, capture_database_name: true, capture_execution_time: true, capture_custom_fields: false, },};TOML Configuration
[audit]enabled = truelog_ddl = truelog_dml = truelog_select = falselog_transactions = falselog_auth = trueretention_days = 90async_buffer_size = 100enable_checksums = truemax_query_length = 10000
[audit.capture_metadata]capture_client_ip = truecapture_application_name = truecapture_database_name = truecapture_execution_time = truecapture_custom_fields = falseUsage
Initializing Audit Logger
use heliosdb_nano::{EmbeddedDatabase, Config};use heliosdb_nano::audit::{AuditLogger, AuditConfig};use std::sync::Arc;
// Create databaselet config = Config::in_memory();let db = EmbeddedDatabase::new_in_memory()?;
// Get storage referencelet storage = Arc::new( heliosdb_nano::storage::StorageEngine::open_in_memory(&config)?);
// Create audit loggerlet audit_config = AuditConfig::default();let logger = AuditLogger::new(storage, audit_config)?;Logging Operations
// Log DDL operationlogger.log_ddl( "CREATE TABLE", "users", "CREATE TABLE users (id INT, name TEXT)", true, // success None, // no error)?;
// Log DML operationlogger.log_dml( "INSERT", "users", "INSERT INTO users VALUES (1, 'Alice')", 1, // affected rows true, // success None, // no error)?;
// Log failed operationlogger.log_dml( "UPDATE", "nonexistent_table", "UPDATE nonexistent_table SET x=1", 0, // no rows affected false, // failed Some("Table does not exist"),)?;
// Log SELECT query (if enabled)logger.log_select( "users", "SELECT * FROM users WHERE id = 1", 1, // row count Some(50), // execution time in ms)?;Querying Audit Logs
Using SQL
-- Get all audit eventsSELECT * FROM __audit_log ORDER BY id DESC LIMIT 100;
-- Filter by operation typeSELECT * FROM __audit_log WHERE operation = 'INSERT';
-- Filter by userSELECT * FROM __audit_log WHERE user = 'alice';
-- Filter by time rangeSELECT * FROM __audit_logWHERE timestamp >= '2024-01-01T00:00:00Z' AND timestamp <= '2024-12-31T23:59:59Z';
-- Filter by success/failureSELECT * FROM __audit_log WHERE success = false;
-- Filter by target tableSELECT * FROM __audit_log WHERE target = 'users';Using Query Builder
use heliosdb_nano::audit::{AuditQuery, OperationType};use chrono::Utc;
// Build querylet query = AuditQuery::new() .with_operation(OperationType::Insert) .with_target("users".to_string()) .with_user("alice".to_string()) .with_success(true) .limit(100) .offset(0);
// Execute querylet sql = query.build_sql();let tuples = logger.query_audit_log(&sql)?;
// Parse eventslet events = AuditQuery::parse_events(tuples)?;
// Process eventsfor event in events { println!("{}: {} on {} by {}", event.timestamp, event.operation, event.target.unwrap_or_default(), event.user );}Audit Log Schema
The __audit_log table has the following schema:
| Column | Type | Description |
|---|---|---|
| id | INT8 | Unique event ID (auto-incrementing) |
| timestamp | TIMESTAMP | When the operation occurred |
| session_id | TEXT | Session identifier |
| user | TEXT | User who performed the operation |
| operation | TEXT | Operation type (INSERT, UPDATE, etc.) |
| target | TEXT | Target object (table name, etc.) |
| query | TEXT | Full SQL query |
| affected_rows | INT8 | Number of rows affected |
| success | BOOLEAN | Whether operation succeeded |
| error | TEXT | Error message (if failed) |
| checksum | TEXT | Cryptographic checksum (SHA-256) |
Security Features
Tamper-Proof Logging
The audit log is designed to be tamper-proof:
- Append-Only: Events can only be added, never modified or deleted
- Cryptographic Checksums: Each event has a SHA-256 checksum
- Separate Storage: Audit data stored in dedicated table
- Verification: Checksums can be verified to detect tampering
// Verify event integritylet event = /* ... get event from log ... */;if !event.verify_checksum() { eprintln!("WARNING: Audit event {} has been tampered with!", event.id);}Access Control
The __audit_log table should be protected:
- Read-only access for auditors
- No write/delete permissions for users
- System-level access only for the audit logger
Sensitive Data Protection
Configure the audit system to avoid logging sensitive data:
let config = AuditConfig { max_query_length: 1000, // Truncate long queries // Don't log SELECT queries (may contain sensitive data) log_select: false, ..Default::default()};Performance Considerations
Async Logging
The audit system uses asynchronous logging to minimize performance impact:
- Operations are logged to an in-memory buffer
- A background task flushes the buffer to storage
- Database operations are never blocked by audit logging
let config = AuditConfig { async_buffer_size: 500, // Larger buffer = fewer flushes ..Default::default()};Selective Logging
Disable verbose logging for performance:
let config = AuditConfig { log_select: false, // Don't log SELECT queries log_transactions: false, // Don't log BEGIN/COMMIT/ROLLBACK ..Default::default()};Query Truncation
Truncate long queries to save space:
let config = AuditConfig { max_query_length: 5000, // Truncate queries > 5KB ..Default::default()};Compliance
SOC2 Compliance
SOC2 requires tracking:
- Who accessed the system (user)
- What they did (operation)
- When they did it (timestamp)
- Whether it succeeded (success)
Use AuditConfig::compliance() for SOC2 compliance.
HIPAA Compliance
HIPAA requires:
- Audit trails for all access to protected health information (PHI)
- 7-year retention period
- Tamper-proof logging
Use AuditConfig::compliance() with retention_days: 2555 (7 years).
GDPR Compliance
GDPR requires:
- Logging of data access and modifications
- Ability to produce audit reports
- Retention periods (varies by jurisdiction)
Use AuditConfig::compliance() and adjust retention as needed.
Maintenance
Log Retention
Implement log retention policy:
-- Delete old audit events (run periodically)DELETE FROM __audit_logWHERE timestamp < NOW() - INTERVAL '90 days';Note: This breaks the append-only guarantee. Consider archiving instead:
-- Archive old events to separate tableINSERT INTO __audit_log_archiveSELECT * FROM __audit_logWHERE timestamp < NOW() - INTERVAL '90 days';
DELETE FROM __audit_logWHERE timestamp < NOW() - INTERVAL '90 days';Monitoring
Monitor audit log size and growth:
-- Count events by daySELECT DATE(timestamp) as day, COUNT(*) as eventsFROM __audit_logGROUP BY DATE(timestamp)ORDER BY day DESC;
-- Count events by operation typeSELECT operation, COUNT(*) as countFROM __audit_logGROUP BY operationORDER BY count DESC;
-- Find failed operationsSELECT * FROM __audit_logWHERE success = falseORDER BY timestamp DESC;Best Practices
- Enable audit logging in production: Always enable for compliance and security
- Use appropriate configuration: Choose minimal/default/verbose/compliance based on needs
- Protect the audit log: Restrict access to the
__audit_logtable - Monitor regularly: Review audit logs for anomalies and failed operations
- Archive old logs: Implement retention policies to manage storage
- Verify checksums: Periodically verify event checksums for tampering
- Don’t log sensitive data: Avoid logging passwords, API keys, or PII in queries
- Test audit configuration: Verify that the right events are being logged
Troubleshooting
Audit events not appearing
- Check if audit logging is enabled:
config.audit.enabled - Check if the operation type is enabled (e.g.,
log_dml,log_select) - Give async task time to flush (100-200ms in tests)
- Check for errors in audit logger initialization
Performance issues
- Reduce buffer size to flush more frequently (or increase to flush less)
- Disable verbose logging (SELECT queries, transactions)
- Increase
max_query_lengthto truncate queries earlier - Monitor buffer flush rate and adjust
async_buffer_size
Storage growing too fast
- Implement retention policy (delete/archive old events)
- Disable SELECT query logging
- Truncate long queries with
max_query_length - Reduce retention period
Examples
See tests/audit_tests.rs for examples.
API Reference
AuditLogger: Main audit logging interfaceAuditConfig: Configuration for audit loggingAuditEvent: Represents a single audit eventAuditQuery: Query builder for audit logsOperationType: Enum of all operation typesAuditMetadata: Additional metadata for events