use crate::{AuditEntry, AuditError, AuditResult, AuditSystem, system::AuditTargetMetricSnapshot};
use rustfs_config::server_config::Config;
use std::sync::{Arc, OnceLock};
use tracing::{debug, error, trace};
const LOG_COMPONENT_AUDIT: &str = "audit";
const LOG_SUBSYSTEM_GLOBAL: &str = "global";
const EVENT_AUDIT_GLOBAL_SKIPPED: &str = "audit_global_skipped";
const EVENT_AUDIT_ENTRY_DROPPED: &str = "audit_entry_dropped";
const EVENT_AUDIT_DISPATCH_FAILED: &str = "audit_dispatch_failed";
static AUDIT_SYSTEM: OnceLock<Arc<AuditSystem>> = OnceLock::new();
pub fn init_audit_system() -> Arc<AuditSystem> {
AUDIT_SYSTEM.get_or_init(|| Arc::new(AuditSystem::new())).clone()
}
pub fn audit_system() -> Option<Arc<AuditSystem>> {
AUDIT_SYSTEM.get().cloned()
}
macro_rules! with_audit_system {
($async_closure:expr) => {
if let Some(system) = audit_system() {
(async move { $async_closure(system).await }).await
} else {
debug!(
event = EVENT_AUDIT_GLOBAL_SKIPPED,
component = LOG_COMPONENT_AUDIT,
subsystem = LOG_SUBSYSTEM_GLOBAL,
reason = "system_not_initialized",
"Skipped audit system operation"
);
Ok(())
}
};
}
pub async fn start_audit_system(config: Config) -> AuditResult<()> {
let system = init_audit_system();
system.start(config).await
}
pub async fn stop_audit_system() -> AuditResult<()> {
with_audit_system!(|system: Arc<AuditSystem>| async move { system.close().await })
}
pub async fn pause_audit_system() -> AuditResult<()> {
with_audit_system!(|system: Arc<AuditSystem>| async move { system.pause().await })
}
pub async fn resume_audit_system() -> AuditResult<()> {
with_audit_system!(|system: Arc<AuditSystem>| async move { system.resume().await })
}
pub async fn dispatch_audit_log(entry: Arc<AuditEntry>) -> AuditResult<()> {
let Some(system) = audit_system() else {
debug!(
event = EVENT_AUDIT_ENTRY_DROPPED,
component = LOG_COMPONENT_AUDIT,
subsystem = LOG_SUBSYSTEM_GLOBAL,
reason = "system_not_initialized",
"Dropped audit entry"
);
return Ok(());
};
match system.dispatch(entry).await {
Ok(()) => Ok(()),
Err(AuditError::NotInitialized(_)) | Err(AuditError::Paused) => {
trace!(
event = EVENT_AUDIT_ENTRY_DROPPED,
component = LOG_COMPONENT_AUDIT,
subsystem = LOG_SUBSYSTEM_GLOBAL,
reason = "system_not_running",
"Dropped audit entry"
);
Ok(())
}
Err(e) => Err(e),
}
}
pub async fn reload_audit_config(config: Config) -> AuditResult<()> {
with_audit_system!(|system: Arc<AuditSystem>| async move { system.reload_config(config).await })
}
pub async fn audit_target_metrics() -> Vec<AuditTargetMetricSnapshot> {
if let Some(system) = audit_system() {
system.snapshot_target_metrics().await
} else {
Vec::new()
}
}
pub async fn is_audit_system_running() -> bool {
if let Some(system) = audit_system() {
system.is_running().await
} else {
false
}
}
pub struct AuditLogger;
impl AuditLogger {
pub async fn log(entry: AuditEntry) {
if let Err(e) = dispatch_audit_log(Arc::new(entry)).await {
error!(
event = EVENT_AUDIT_DISPATCH_FAILED,
component = LOG_COMPONENT_AUDIT,
subsystem = LOG_SUBSYSTEM_GLOBAL,
error = %e,
"Failed to dispatch audit entry"
);
}
}
pub async fn is_enabled() -> bool {
is_audit_system_running().await
}
pub fn instance() -> &'static Self {
static INSTANCE: AuditLogger = AuditLogger;
&INSTANCE
}
}