canic_core/model/metrics/
icc.rs1use candid::{CandidType, Principal};
2use serde::{Deserialize, Serialize};
3use std::{cell::RefCell, collections::HashMap};
4
5thread_local! {
6 static ICC_METRICS: RefCell<HashMap<IccMetricKey, u64>> = RefCell::new(HashMap::new());
7}
8
9#[derive(CandidType, Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
15pub struct IccMetricKey {
16 pub target: Principal,
17 pub method: String,
18}
19
20#[derive(CandidType, Clone, Debug, Deserialize, Serialize)]
26pub struct IccMetricEntry {
27 pub target: Principal,
28 pub method: String,
29 pub count: u64,
30}
31
32pub type IccMetricsSnapshot = Vec<IccMetricEntry>;
37
38pub struct IccMetrics;
44
45impl IccMetrics {
46 pub fn increment(target: Principal, method: &str) {
48 ICC_METRICS.with_borrow_mut(|counts| {
49 let key = IccMetricKey {
50 target,
51 method: method.to_string(),
52 };
53 let entry = counts.entry(key).or_insert(0);
54 *entry = entry.saturating_add(1);
55 });
56 }
57
58 #[must_use]
60 pub fn snapshot() -> IccMetricsSnapshot {
61 ICC_METRICS.with_borrow(|counts| {
62 counts
63 .iter()
64 .map(|(key, count)| IccMetricEntry {
65 target: key.target,
66 method: key.method.clone(),
67 count: *count,
68 })
69 .collect()
70 })
71 }
72
73 #[cfg(test)]
74 pub fn reset() {
75 ICC_METRICS.with_borrow_mut(HashMap::clear);
76 }
77}
78
79#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn icc_metrics_track_target_and_method() {
89 IccMetrics::reset();
90
91 let t1 = Principal::from_slice(&[1; 29]);
92 let t2 = Principal::from_slice(&[2; 29]);
93
94 IccMetrics::increment(t1, "foo");
95 IccMetrics::increment(t1, "foo");
96 IccMetrics::increment(t1, "bar");
97 IccMetrics::increment(t2, "foo");
98
99 let snapshot = IccMetrics::snapshot();
100 let mut map: HashMap<(Principal, String), u64> = snapshot
101 .into_iter()
102 .map(|entry| ((entry.target, entry.method), entry.count))
103 .collect();
104
105 assert_eq!(map.remove(&(t1, "foo".to_string())), Some(2));
106 assert_eq!(map.remove(&(t1, "bar".to_string())), Some(1));
107 assert_eq!(map.remove(&(t2, "foo".to_string())), Some(1));
108 assert!(map.is_empty());
109 }
110}