canic_core/model/metrics/
access.rs1use candid::CandidType;
2use serde::{Deserialize, Serialize};
3use std::{cell::RefCell, collections::HashMap};
4
5thread_local! {
6 static ACCESS_METRICS: RefCell<HashMap<AccessMetricKey, u64>> = RefCell::new(HashMap::new());
7}
8
9#[derive(
15 CandidType, Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize,
16)]
17#[remain::sorted]
18pub enum AccessMetricKind {
19 Auth,
20 Guard,
21 Policy,
22}
23
24#[derive(CandidType, Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
30pub struct AccessMetricKey {
31 pub endpoint: String,
32 pub kind: AccessMetricKind,
33}
34
35#[derive(CandidType, Clone, Debug, Deserialize, Serialize)]
41pub struct AccessMetricEntry {
42 pub endpoint: String,
43 pub kind: AccessMetricKind,
44 pub count: u64,
45}
46
47pub type AccessMetricsSnapshot = Vec<AccessMetricEntry>;
52
53pub struct AccessMetrics;
59
60impl AccessMetrics {
61 pub fn increment(endpoint: &str, kind: AccessMetricKind) {
63 ACCESS_METRICS.with_borrow_mut(|counts| {
64 let key = AccessMetricKey {
65 endpoint: endpoint.to_string(),
66 kind,
67 };
68
69 let entry = counts.entry(key).or_insert(0);
70 *entry = entry.saturating_add(1);
71 });
72 }
73
74 #[must_use]
76 pub fn snapshot() -> AccessMetricsSnapshot {
77 ACCESS_METRICS.with_borrow(|counts| {
78 counts
79 .iter()
80 .map(|(key, count)| AccessMetricEntry {
81 endpoint: key.endpoint.clone(),
82 kind: key.kind,
83 count: *count,
84 })
85 .collect()
86 })
87 }
88
89 #[cfg(test)]
90 pub fn reset() {
91 ACCESS_METRICS.with_borrow_mut(HashMap::clear);
92 }
93}
94
95#[cfg(test)]
100mod tests {
101 use super::*;
102 use std::collections::HashMap;
103
104 #[test]
105 fn access_metrics_track_endpoint_and_stage() {
106 AccessMetrics::reset();
107
108 AccessMetrics::increment("foo", AccessMetricKind::Guard);
109 AccessMetrics::increment("foo", AccessMetricKind::Guard);
110 AccessMetrics::increment("foo", AccessMetricKind::Auth);
111 AccessMetrics::increment("bar", AccessMetricKind::Policy);
112
113 let snapshot = AccessMetrics::snapshot();
114 let mut map: HashMap<(String, AccessMetricKind), u64> = snapshot
115 .into_iter()
116 .map(|entry| ((entry.endpoint, entry.kind), entry.count))
117 .collect();
118
119 assert_eq!(
120 map.remove(&("foo".to_string(), AccessMetricKind::Guard)),
121 Some(2)
122 );
123 assert_eq!(
124 map.remove(&("foo".to_string(), AccessMetricKind::Auth)),
125 Some(1)
126 );
127 assert_eq!(
128 map.remove(&("bar".to_string(), AccessMetricKind::Policy)),
129 Some(1)
130 );
131 assert!(map.is_empty());
132 }
133}