1use crate::{AuditEntry, AuditError, AuditResult, AuditSystem, system::AuditTargetMetricSnapshot};
16use rustfs_config::server_config::Config;
17use std::sync::{Arc, OnceLock};
18use tracing::{debug, error, trace};
19
20const LOG_COMPONENT_AUDIT: &str = "audit";
21const LOG_SUBSYSTEM_GLOBAL: &str = "global";
22const EVENT_AUDIT_GLOBAL_SKIPPED: &str = "audit_global_skipped";
23const EVENT_AUDIT_ENTRY_DROPPED: &str = "audit_entry_dropped";
24const EVENT_AUDIT_DISPATCH_FAILED: &str = "audit_dispatch_failed";
25
26static AUDIT_SYSTEM: OnceLock<Arc<AuditSystem>> = OnceLock::new();
28
29pub fn init_audit_system() -> Arc<AuditSystem> {
31 AUDIT_SYSTEM.get_or_init(|| Arc::new(AuditSystem::new())).clone()
32}
33
34pub fn audit_system() -> Option<Arc<AuditSystem>> {
36 AUDIT_SYSTEM.get().cloned()
37}
38
39macro_rules! with_audit_system {
42 ($async_closure:expr) => {
43 if let Some(system) = audit_system() {
44 (async move { $async_closure(system).await }).await
45 } else {
46 debug!(
47 event = EVENT_AUDIT_GLOBAL_SKIPPED,
48 component = LOG_COMPONENT_AUDIT,
49 subsystem = LOG_SUBSYSTEM_GLOBAL,
50 reason = "system_not_initialized",
51 "Skipped audit system operation"
52 );
53 Ok(())
54 }
55 };
56}
57
58pub async fn start_audit_system(config: Config) -> AuditResult<()> {
60 let system = init_audit_system();
61 system.start(config).await
62}
63
64pub async fn stop_audit_system() -> AuditResult<()> {
66 with_audit_system!(|system: Arc<AuditSystem>| async move { system.close().await })
67}
68
69pub async fn pause_audit_system() -> AuditResult<()> {
71 with_audit_system!(|system: Arc<AuditSystem>| async move { system.pause().await })
72}
73
74pub async fn resume_audit_system() -> AuditResult<()> {
76 with_audit_system!(|system: Arc<AuditSystem>| async move { system.resume().await })
77}
78
79pub async fn dispatch_audit_log(entry: Arc<AuditEntry>) -> AuditResult<()> {
81 let Some(system) = audit_system() else {
82 debug!(
83 event = EVENT_AUDIT_ENTRY_DROPPED,
84 component = LOG_COMPONENT_AUDIT,
85 subsystem = LOG_SUBSYSTEM_GLOBAL,
86 reason = "system_not_initialized",
87 "Dropped audit entry"
88 );
89 return Ok(());
90 };
91
92 match system.dispatch(entry).await {
100 Ok(()) => Ok(()),
101 Err(AuditError::NotInitialized(_)) | Err(AuditError::Paused) => {
102 trace!(
103 event = EVENT_AUDIT_ENTRY_DROPPED,
104 component = LOG_COMPONENT_AUDIT,
105 subsystem = LOG_SUBSYSTEM_GLOBAL,
106 reason = "system_not_running",
107 "Dropped audit entry"
108 );
109 Ok(())
110 }
111 Err(e) => Err(e),
112 }
113}
114
115pub async fn reload_audit_config(config: Config) -> AuditResult<()> {
117 with_audit_system!(|system: Arc<AuditSystem>| async move { system.reload_config(config).await })
118}
119
120pub async fn audit_target_metrics() -> Vec<AuditTargetMetricSnapshot> {
122 if let Some(system) = audit_system() {
123 system.snapshot_target_metrics().await
124 } else {
125 Vec::new()
126 }
127}
128
129pub async fn is_audit_system_running() -> bool {
131 if let Some(system) = audit_system() {
132 system.is_running().await
133 } else {
134 false
135 }
136}
137
138pub struct AuditLogger;
140
141impl AuditLogger {
142 pub async fn log(entry: AuditEntry) {
144 if let Err(e) = dispatch_audit_log(Arc::new(entry)).await {
145 error!(
146 event = EVENT_AUDIT_DISPATCH_FAILED,
147 component = LOG_COMPONENT_AUDIT,
148 subsystem = LOG_SUBSYSTEM_GLOBAL,
149 error = %e,
150 "Failed to dispatch audit entry"
151 );
152 }
153 }
154
155 pub async fn is_enabled() -> bool {
157 is_audit_system_running().await
158 }
159
160 pub fn instance() -> &'static Self {
162 static INSTANCE: AuditLogger = AuditLogger;
163 &INSTANCE
164 }
165}