sz_rust_orm_facade/data_scope/
metrics.rs1use std::sync::atomic::{AtomicU64, Ordering};
7
8pub struct DataScopeMetrics {
10 hit_total: AtomicU64,
11 bypass_total: AtomicU64,
12 reject_total: AtomicU64,
13 eval_total_ms: AtomicU64,
14 eval_count: AtomicU64,
15}
16
17impl DataScopeMetrics {
18 pub fn new() -> Self {
20 Self {
21 hit_total: AtomicU64::new(0),
22 bypass_total: AtomicU64::new(0),
23 reject_total: AtomicU64::new(0),
24 eval_total_ms: AtomicU64::new(0),
25 eval_count: AtomicU64::new(0),
26 }
27 }
28
29 pub fn record_hit(&self, table: &str, mode: &str) {
31 self.hit_total.fetch_add(1, Ordering::Relaxed);
32 tracing::debug!(
33 target: "data_scope_metrics",
34 table = table,
35 mode = mode,
36 "data_scope_hit"
37 );
38 }
39
40 pub fn record_bypass(&self, user_id: i64, table: &str) {
42 self.bypass_total.fetch_add(1, Ordering::Relaxed);
43 tracing::info!(
44 target: "data_scope_audit",
45 user_id = user_id,
46 table = table,
47 "super bypass: user_id={}, table={}", user_id, table
48 );
49 }
50
51 pub fn record_reject(&self, error_code: &str, table: &str) {
53 self.reject_total.fetch_add(1, Ordering::Relaxed);
54 tracing::warn!(
55 target: "data_scope_metrics",
56 error_code = error_code,
57 table = table,
58 "data_scope_reject"
59 );
60 }
61
62 pub fn record_eval(&self, elapsed_ms: u64) {
64 self.eval_total_ms.fetch_add(elapsed_ms, Ordering::Relaxed);
65 self.eval_count.fetch_add(1, Ordering::Relaxed);
66 }
67
68 pub fn hit_total(&self) -> u64 {
70 self.hit_total.load(Ordering::Relaxed)
71 }
72
73 pub fn bypass_total(&self) -> u64 {
75 self.bypass_total.load(Ordering::Relaxed)
76 }
77
78 pub fn reject_total(&self) -> u64 {
80 self.reject_total.load(Ordering::Relaxed)
81 }
82
83 pub fn avg_eval_ms(&self) -> f64 {
85 let count = self.eval_count.load(Ordering::Relaxed);
86 if count == 0 {
87 0.0
88 } else {
89 self.eval_total_ms.load(Ordering::Relaxed) as f64 / count as f64
90 }
91 }
92}
93
94impl Default for DataScopeMetrics {
95 fn default() -> Self {
96 Self::new()
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 #[test]
105 fn test_metrics_record() {
106 let metrics = DataScopeMetrics::new();
107 metrics.record_hit("order", "dept");
108 metrics.record_hit("order", "dept");
109 metrics.record_bypass(1, "order");
110 metrics.record_reject("DATA_SCOPE_NO_USER_CONTEXT", "order");
111 metrics.record_eval(5);
112 metrics.record_eval(15);
113 assert_eq!(metrics.hit_total(), 2);
114 assert_eq!(metrics.bypass_total(), 1);
115 assert_eq!(metrics.reject_total(), 1);
116 assert_eq!(metrics.avg_eval_ms(), 10.0);
117 }
118
119 #[test]
120 fn test_avg_eval_ms_zero_count() {
121 let metrics = DataScopeMetrics::new();
122 assert_eq!(metrics.avg_eval_ms(), 0.0);
123 }
124
125 #[test]
126 fn test_default() {
127 let metrics = DataScopeMetrics::default();
128 assert_eq!(metrics.hit_total(), 0);
129 assert_eq!(metrics.avg_eval_ms(), 0.0);
130 }
131}