Skip to main content

rocketmq_observability/metrics/
auth.rs

1// Copyright 2023 The RocketMQ Rust Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::sync::atomic::AtomicU64;
16use std::sync::atomic::Ordering;
17use std::sync::Arc;
18
19#[derive(Clone, Default)]
20pub struct AuthMetrics {
21    inner: Arc<AuthMetricsInner>,
22}
23
24#[derive(Default)]
25struct AuthMetricsInner {
26    acl_reload_attempts: AtomicU64,
27    acl_reload_successes: AtomicU64,
28    acl_reload_failures: AtomicU64,
29    acl_reload_skipped: AtomicU64,
30    cache_hits: AtomicU64,
31    cache_misses: AtomicU64,
32    cache_invalidations: AtomicU64,
33    signature_successes: AtomicU64,
34    signature_failures: AtomicU64,
35    whitelist_hits: AtomicU64,
36    whitelist_misses: AtomicU64,
37    authentication_successes: AtomicU64,
38    authentication_failures: AtomicU64,
39    authorization_successes: AtomicU64,
40    authorization_failures: AtomicU64,
41}
42
43#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
44pub struct AuthMetricsSnapshot {
45    pub acl_reload_attempts: u64,
46    pub acl_reload_successes: u64,
47    pub acl_reload_failures: u64,
48    pub acl_reload_skipped: u64,
49    pub cache_hits: u64,
50    pub cache_misses: u64,
51    pub cache_invalidations: u64,
52    pub signature_successes: u64,
53    pub signature_failures: u64,
54    pub whitelist_hits: u64,
55    pub whitelist_misses: u64,
56    pub authentication_successes: u64,
57    pub authentication_failures: u64,
58    pub authorization_successes: u64,
59    pub authorization_failures: u64,
60}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub struct AuthMetricSample {
64    pub name: &'static str,
65    pub value: u64,
66}
67
68impl AuthMetricsSnapshot {
69    pub const SAMPLE_COUNT: usize = 15;
70
71    pub fn samples(&self) -> [AuthMetricSample; Self::SAMPLE_COUNT] {
72        [
73            AuthMetricSample {
74                name: "acl_reload_attempts",
75                value: self.acl_reload_attempts,
76            },
77            AuthMetricSample {
78                name: "acl_reload_successes",
79                value: self.acl_reload_successes,
80            },
81            AuthMetricSample {
82                name: "acl_reload_failures",
83                value: self.acl_reload_failures,
84            },
85            AuthMetricSample {
86                name: "acl_reload_skipped",
87                value: self.acl_reload_skipped,
88            },
89            AuthMetricSample {
90                name: "cache_hits",
91                value: self.cache_hits,
92            },
93            AuthMetricSample {
94                name: "cache_misses",
95                value: self.cache_misses,
96            },
97            AuthMetricSample {
98                name: "cache_invalidations",
99                value: self.cache_invalidations,
100            },
101            AuthMetricSample {
102                name: "signature_successes",
103                value: self.signature_successes,
104            },
105            AuthMetricSample {
106                name: "signature_failures",
107                value: self.signature_failures,
108            },
109            AuthMetricSample {
110                name: "whitelist_hits",
111                value: self.whitelist_hits,
112            },
113            AuthMetricSample {
114                name: "whitelist_misses",
115                value: self.whitelist_misses,
116            },
117            AuthMetricSample {
118                name: "authentication_successes",
119                value: self.authentication_successes,
120            },
121            AuthMetricSample {
122                name: "authentication_failures",
123                value: self.authentication_failures,
124            },
125            AuthMetricSample {
126                name: "authorization_successes",
127                value: self.authorization_successes,
128            },
129            AuthMetricSample {
130                name: "authorization_failures",
131                value: self.authorization_failures,
132            },
133        ]
134    }
135}
136
137impl AuthMetrics {
138    pub fn snapshot(&self) -> AuthMetricsSnapshot {
139        AuthMetricsSnapshot {
140            acl_reload_attempts: self.inner.acl_reload_attempts.load(Ordering::Relaxed),
141            acl_reload_successes: self.inner.acl_reload_successes.load(Ordering::Relaxed),
142            acl_reload_failures: self.inner.acl_reload_failures.load(Ordering::Relaxed),
143            acl_reload_skipped: self.inner.acl_reload_skipped.load(Ordering::Relaxed),
144            cache_hits: self.inner.cache_hits.load(Ordering::Relaxed),
145            cache_misses: self.inner.cache_misses.load(Ordering::Relaxed),
146            cache_invalidations: self.inner.cache_invalidations.load(Ordering::Relaxed),
147            signature_successes: self.inner.signature_successes.load(Ordering::Relaxed),
148            signature_failures: self.inner.signature_failures.load(Ordering::Relaxed),
149            whitelist_hits: self.inner.whitelist_hits.load(Ordering::Relaxed),
150            whitelist_misses: self.inner.whitelist_misses.load(Ordering::Relaxed),
151            authentication_successes: self.inner.authentication_successes.load(Ordering::Relaxed),
152            authentication_failures: self.inner.authentication_failures.load(Ordering::Relaxed),
153            authorization_successes: self.inner.authorization_successes.load(Ordering::Relaxed),
154            authorization_failures: self.inner.authorization_failures.load(Ordering::Relaxed),
155        }
156    }
157
158    #[inline]
159    pub fn record_acl_reload_attempt(&self) {
160        self.inner.acl_reload_attempts.fetch_add(1, Ordering::Relaxed);
161    }
162
163    #[inline]
164    pub fn record_acl_reload_success(&self) {
165        self.inner.acl_reload_successes.fetch_add(1, Ordering::Relaxed);
166    }
167
168    #[inline]
169    pub fn record_acl_reload_failure(&self) {
170        self.inner.acl_reload_failures.fetch_add(1, Ordering::Relaxed);
171    }
172
173    #[inline]
174    pub fn record_acl_reload_skipped(&self) {
175        self.inner.acl_reload_skipped.fetch_add(1, Ordering::Relaxed);
176    }
177
178    #[inline]
179    pub fn record_cache_hit(&self) {
180        self.inner.cache_hits.fetch_add(1, Ordering::Relaxed);
181    }
182
183    #[inline]
184    pub fn record_cache_miss(&self) {
185        self.inner.cache_misses.fetch_add(1, Ordering::Relaxed);
186    }
187
188    #[inline]
189    pub fn record_cache_invalidation(&self) {
190        self.inner.cache_invalidations.fetch_add(1, Ordering::Relaxed);
191    }
192
193    #[inline]
194    pub fn record_signature_verification(&self, success: bool) {
195        if success {
196            self.inner.signature_successes.fetch_add(1, Ordering::Relaxed);
197        } else {
198            self.inner.signature_failures.fetch_add(1, Ordering::Relaxed);
199        }
200    }
201
202    #[inline]
203    pub fn record_whitelist_check(&self, matched: bool) {
204        if matched {
205            self.inner.whitelist_hits.fetch_add(1, Ordering::Relaxed);
206        } else {
207            self.inner.whitelist_misses.fetch_add(1, Ordering::Relaxed);
208        }
209    }
210
211    #[inline]
212    pub fn record_authentication_result(&self, success: bool) {
213        if success {
214            self.inner.authentication_successes.fetch_add(1, Ordering::Relaxed);
215        } else {
216            self.inner.authentication_failures.fetch_add(1, Ordering::Relaxed);
217        }
218    }
219
220    #[inline]
221    pub fn record_authorization_result(&self, success: bool) {
222        if success {
223            self.inner.authorization_successes.fetch_add(1, Ordering::Relaxed);
224        } else {
225            self.inner.authorization_failures.fetch_add(1, Ordering::Relaxed);
226        }
227    }
228}
229
230#[cfg(test)]
231mod tests {
232    use super::AuthMetrics;
233    use super::AuthMetricsSnapshot;
234
235    #[test]
236    fn auth_metrics_snapshot_tracks_all_counter_groups() {
237        let metrics = AuthMetrics::default();
238
239        metrics.record_acl_reload_attempt();
240        metrics.record_acl_reload_success();
241        metrics.record_acl_reload_failure();
242        metrics.record_acl_reload_skipped();
243        metrics.record_cache_hit();
244        metrics.record_cache_miss();
245        metrics.record_cache_invalidation();
246        metrics.record_signature_verification(true);
247        metrics.record_signature_verification(false);
248        metrics.record_whitelist_check(true);
249        metrics.record_whitelist_check(false);
250        metrics.record_authentication_result(true);
251        metrics.record_authentication_result(false);
252        metrics.record_authorization_result(true);
253        metrics.record_authorization_result(false);
254
255        let snapshot = metrics.snapshot();
256        assert_eq!(snapshot.acl_reload_attempts, 1);
257        assert_eq!(snapshot.acl_reload_successes, 1);
258        assert_eq!(snapshot.acl_reload_failures, 1);
259        assert_eq!(snapshot.acl_reload_skipped, 1);
260        assert_eq!(snapshot.cache_hits, 1);
261        assert_eq!(snapshot.cache_misses, 1);
262        assert_eq!(snapshot.cache_invalidations, 1);
263        assert_eq!(snapshot.signature_successes, 1);
264        assert_eq!(snapshot.signature_failures, 1);
265        assert_eq!(snapshot.whitelist_hits, 1);
266        assert_eq!(snapshot.whitelist_misses, 1);
267        assert_eq!(snapshot.authentication_successes, 1);
268        assert_eq!(snapshot.authentication_failures, 1);
269        assert_eq!(snapshot.authorization_successes, 1);
270        assert_eq!(snapshot.authorization_failures, 1);
271    }
272
273    #[test]
274    fn auth_metrics_snapshot_exports_stable_samples() {
275        let snapshot = AuthMetricsSnapshot {
276            acl_reload_attempts: 1,
277            authorization_failures: 15,
278            ..AuthMetricsSnapshot::default()
279        };
280
281        let samples = snapshot.samples();
282
283        assert_eq!(samples.len(), AuthMetricsSnapshot::SAMPLE_COUNT);
284        assert_eq!(samples[0].name, "acl_reload_attempts");
285        assert_eq!(samples[0].value, 1);
286        assert_eq!(samples[14].name, "authorization_failures");
287        assert_eq!(samples[14].value, 15);
288    }
289}