canic_core/model/metrics/
system.rs

1use candid::CandidType;
2use serde::{Deserialize, Serialize};
3use std::{cell::RefCell, collections::HashMap};
4
5thread_local! {
6    static SYSTEM_METRICS: RefCell<HashMap<SystemMetricKind, u64>> = RefCell::new(HashMap::new());
7}
8
9///
10/// SystemMetricKind
11/// Enumerates the resource-heavy actions we track.
12///
13
14#[derive(
15    CandidType, Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize,
16)]
17#[remain::sorted]
18pub enum SystemMetricKind {
19    CanisterCall,
20    CanisterStatus,
21    CreateCanister,
22    DeleteCanister,
23    DepositCycles,
24    HttpOutcall,
25    InstallCode,
26    ReinstallCode,
27    TimerScheduled,
28    UninstallCode,
29    UpdateSettings,
30    UpgradeCode,
31}
32
33///
34/// SystemMetricEntry
35/// Snapshot entry pairing a metric kind with its count.
36///
37
38#[derive(CandidType, Clone, Debug, Deserialize, Serialize)]
39pub struct SystemMetricEntry {
40    pub kind: SystemMetricKind,
41    pub count: u64,
42}
43
44///
45/// SystemMetricsSnapshot
46///
47
48pub type SystemMetricsSnapshot = Vec<SystemMetricEntry>;
49
50///
51/// SystemMetrics
52/// Thin facade over the action metrics counters.
53///
54
55pub struct SystemMetrics;
56
57impl SystemMetrics {
58    /// Increment a counter and return the new value.
59    pub fn increment(kind: SystemMetricKind) {
60        SYSTEM_METRICS.with_borrow_mut(|counts| {
61            let entry = counts.entry(kind).or_insert(0);
62            *entry = entry.saturating_add(1);
63        });
64    }
65
66    /// Return a snapshot of all counters.
67    #[must_use]
68    pub fn snapshot() -> Vec<SystemMetricEntry> {
69        SYSTEM_METRICS.with_borrow(|counts| {
70            counts
71                .iter()
72                .map(|(kind, count)| SystemMetricEntry {
73                    kind: *kind,
74                    count: *count,
75                })
76                .collect()
77        })
78    }
79
80    #[cfg(test)]
81    pub fn reset() {
82        SYSTEM_METRICS.with_borrow_mut(HashMap::clear);
83    }
84}
85
86///
87/// TESTS
88///
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93    use std::collections::HashMap;
94
95    #[test]
96    fn system_metrics_increments_and_snapshots() {
97        SystemMetrics::reset();
98
99        SystemMetrics::increment(SystemMetricKind::CreateCanister);
100        SystemMetrics::increment(SystemMetricKind::CreateCanister);
101        SystemMetrics::increment(SystemMetricKind::InstallCode);
102
103        let snapshot = SystemMetrics::snapshot();
104        let as_map: HashMap<SystemMetricKind, u64> = snapshot
105            .into_iter()
106            .map(|entry| (entry.kind, entry.count))
107            .collect();
108
109        assert_eq!(as_map.get(&SystemMetricKind::CreateCanister), Some(&2));
110        assert_eq!(as_map.get(&SystemMetricKind::InstallCode), Some(&1));
111        assert!(!as_map.contains_key(&SystemMetricKind::CanisterCall));
112    }
113}