Skip to main content

canic_core/ops/runtime/metrics/
lifecycle.rs

1//! Module: ops::runtime::metrics::lifecycle
2//!
3//! Responsibility: record and snapshot low-cardinality runtime metrics for the lifecycle family.
4//! Does not own: workflow decisions, persisted records, or endpoint DTOs.
5//! Boundary: ops-layer metrics consumed by workflow metrics projection.
6
7use std::{cell::RefCell, collections::HashMap};
8
9thread_local! {
10    static LIFECYCLE_METRICS: RefCell<HashMap<LifecycleMetricKey, u64>> =
11        RefCell::new(HashMap::new());
12}
13
14///
15/// LifecycleMetricPhase
16///
17/// Lifecycle phase dimension used by public metrics projection.
18///
19
20#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
21#[remain::sorted]
22pub enum LifecycleMetricPhase {
23    Init,
24    PostUpgrade,
25}
26
27impl LifecycleMetricPhase {
28    #[must_use]
29    pub const fn metric_label(self) -> &'static str {
30        match self {
31            Self::Init => "init",
32            Self::PostUpgrade => "post_upgrade",
33        }
34    }
35}
36
37///
38/// LifecycleMetricRole
39///
40/// Lifecycle canister role dimension used by public metrics projection.
41///
42
43#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
44#[remain::sorted]
45pub enum LifecycleMetricRole {
46    Nonroot,
47    Root,
48}
49
50impl LifecycleMetricRole {
51    #[must_use]
52    pub const fn metric_label(self) -> &'static str {
53        match self {
54            Self::Nonroot => "nonroot",
55            Self::Root => "root",
56        }
57    }
58}
59
60///
61/// LifecycleMetricStage
62///
63/// Lifecycle stage dimension used by public metrics projection.
64///
65
66#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
67#[remain::sorted]
68pub enum LifecycleMetricStage {
69    Bootstrap,
70    Runtime,
71}
72
73impl LifecycleMetricStage {
74    #[must_use]
75    pub const fn metric_label(self) -> &'static str {
76        match self {
77            Self::Bootstrap => "bootstrap",
78            Self::Runtime => "runtime",
79        }
80    }
81}
82
83///
84/// LifecycleMetricOutcome
85///
86/// Lifecycle outcome dimension used by public metrics projection.
87///
88
89#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
90#[remain::sorted]
91pub enum LifecycleMetricOutcome {
92    Completed,
93    Failed,
94    Scheduled,
95    Skipped,
96    Started,
97    Waiting,
98}
99
100impl LifecycleMetricOutcome {
101    #[must_use]
102    pub const fn metric_label(self) -> &'static str {
103        match self {
104            Self::Completed => "completed",
105            Self::Failed => "failed",
106            Self::Scheduled => "scheduled",
107            Self::Skipped => "skipped",
108            Self::Started => "started",
109            Self::Waiting => "waiting",
110        }
111    }
112}
113
114///
115/// LifecycleMetricKey
116///
117/// Composite key for one low-cardinality lifecycle counter.
118///
119
120#[derive(Clone, Copy, Eq, Hash, PartialEq)]
121pub struct LifecycleMetricKey {
122    pub phase: LifecycleMetricPhase,
123    pub role: LifecycleMetricRole,
124    pub stage: LifecycleMetricStage,
125    pub outcome: LifecycleMetricOutcome,
126}
127
128///
129/// LifecycleMetrics
130///
131/// Operations-layer recorder for lifecycle runtime counters.
132///
133
134pub struct LifecycleMetrics;
135
136impl LifecycleMetrics {
137    /// Record one lifecycle stage event.
138    pub fn record(
139        phase: LifecycleMetricPhase,
140        role: LifecycleMetricRole,
141        stage: LifecycleMetricStage,
142        outcome: LifecycleMetricOutcome,
143    ) {
144        LIFECYCLE_METRICS.with_borrow_mut(|counts| {
145            let key = LifecycleMetricKey {
146                phase,
147                role,
148                stage,
149                outcome,
150            };
151            let entry = counts.entry(key).or_insert(0);
152            *entry = entry.saturating_add(1);
153        });
154    }
155
156    /// Snapshot the current lifecycle metric table as stable rows.
157    #[must_use]
158    pub fn snapshot() -> Vec<(LifecycleMetricKey, u64)> {
159        LIFECYCLE_METRICS
160            .with_borrow(std::clone::Clone::clone)
161            .into_iter()
162            .collect()
163    }
164
165    /// Test-only helper: clear all lifecycle metrics.
166    #[cfg(test)]
167    pub fn reset() {
168        LIFECYCLE_METRICS.with_borrow_mut(HashMap::clear);
169    }
170}