avx_async/
health.rs

1//! Health check system for monitoring runtime health
2//!
3//! Industry 4.0 compliant health monitoring with readiness and liveness probes
4
5use std::sync::atomic::{AtomicBool, Ordering};
6use std::sync::Arc;
7use std::time::{Duration, Instant};
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq)]
10pub enum HealthStatus {
11    Healthy,
12    Degraded,
13    Unhealthy,
14}
15
16impl std::fmt::Display for HealthStatus {
17    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18        match self {
19            HealthStatus::Healthy => write!(f, "healthy"),
20            HealthStatus::Degraded => write!(f, "degraded"),
21            HealthStatus::Unhealthy => write!(f, "unhealthy"),
22        }
23    }
24}
25
26pub struct HealthCheck {
27    ready: Arc<AtomicBool>,
28    alive: Arc<AtomicBool>,
29    last_heartbeat: Arc<std::sync::Mutex<Instant>>,
30    checks: Arc<std::sync::Mutex<Vec<Check>>>,
31}
32
33struct Check {
34    name: String,
35    status: HealthStatus,
36    message: String,
37    last_check: Instant,
38}
39
40impl HealthCheck {
41    pub fn new() -> Self {
42        Self {
43            ready: Arc::new(AtomicBool::new(true)),
44            alive: Arc::new(AtomicBool::new(true)),
45            last_heartbeat: Arc::new(std::sync::Mutex::new(Instant::now())),
46            checks: Arc::new(std::sync::Mutex::new(Vec::new())),
47        }
48    }
49
50    /// Set readiness status (can accept new work)
51    pub fn set_ready(&self, ready: bool) {
52        self.ready.store(ready, Ordering::Release);
53    }
54
55    /// Check if runtime is ready
56    pub fn is_ready(&self) -> bool {
57        self.ready.load(Ordering::Acquire)
58    }
59
60    /// Set liveness status (runtime is functioning)
61    pub fn set_alive(&self, alive: bool) {
62        self.alive.store(alive, Ordering::Release);
63    }
64
65    /// Check if runtime is alive
66    pub fn is_alive(&self) -> bool {
67        self.alive.load(Ordering::Acquire)
68    }
69
70    /// Update heartbeat timestamp
71    pub fn heartbeat(&self) {
72        let mut last = self.last_heartbeat.lock().unwrap();
73        *last = Instant::now();
74    }
75
76    /// Check if heartbeat is recent (within threshold)
77    pub fn is_heartbeat_recent(&self, threshold: Duration) -> bool {
78        let last = self.last_heartbeat.lock().unwrap();
79        last.elapsed() < threshold
80    }
81
82    /// Add a custom health check
83    pub fn add_check(&self, name: impl Into<String>, status: HealthStatus, message: impl Into<String>) {
84        let mut checks = self.checks.lock().unwrap();
85        checks.push(Check {
86            name: name.into(),
87            status,
88            message: message.into(),
89            last_check: Instant::now(),
90        });
91    }
92
93    /// Get overall health status
94    pub fn get_status(&self) -> HealthStatus {
95        if !self.is_alive() {
96            return HealthStatus::Unhealthy;
97        }
98
99        if !self.is_ready() {
100            return HealthStatus::Degraded;
101        }
102
103        let checks = self.checks.lock().unwrap();
104        let has_unhealthy = checks.iter().any(|c| c.status == HealthStatus::Unhealthy);
105        let has_degraded = checks.iter().any(|c| c.status == HealthStatus::Degraded);
106
107        if has_unhealthy {
108            HealthStatus::Unhealthy
109        } else if has_degraded {
110            HealthStatus::Degraded
111        } else {
112            HealthStatus::Healthy
113        }
114    }
115
116    /// Get detailed health report
117    pub fn get_report(&self) -> HealthReport {
118        let checks = self.checks.lock().unwrap();
119        let last_heartbeat = self.last_heartbeat.lock().unwrap();
120
121        HealthReport {
122            status: self.get_status(),
123            ready: self.is_ready(),
124            alive: self.is_alive(),
125            last_heartbeat: last_heartbeat.elapsed(),
126            checks: checks
127                .iter()
128                .map(|c| CheckReport {
129                    name: c.name.clone(),
130                    status: c.status,
131                    message: c.message.clone(),
132                    age: c.last_check.elapsed(),
133                })
134                .collect(),
135        }
136    }
137
138    /// Clear all checks
139    pub fn clear_checks(&self) {
140        let mut checks = self.checks.lock().unwrap();
141        checks.clear();
142    }
143}
144
145impl Default for HealthCheck {
146    fn default() -> Self {
147        Self::new()
148    }
149}
150
151impl Clone for HealthCheck {
152    fn clone(&self) -> Self {
153        Self {
154            ready: Arc::clone(&self.ready),
155            alive: Arc::clone(&self.alive),
156            last_heartbeat: Arc::clone(&self.last_heartbeat),
157            checks: Arc::clone(&self.checks),
158        }
159    }
160}
161
162#[derive(Debug, Clone)]
163pub struct HealthReport {
164    pub status: HealthStatus,
165    pub ready: bool,
166    pub alive: bool,
167    pub last_heartbeat: Duration,
168    pub checks: Vec<CheckReport>,
169}
170
171#[derive(Debug, Clone)]
172pub struct CheckReport {
173    pub name: String,
174    pub status: HealthStatus,
175    pub message: String,
176    pub age: Duration,
177}
178
179impl std::fmt::Display for HealthReport {
180    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
181        writeln!(f, "Health Status: {}", self.status)?;
182        writeln!(f, "Ready: {} | Alive: {}", self.ready, self.alive)?;
183        writeln!(f, "Last Heartbeat: {:?} ago", self.last_heartbeat)?;
184
185        if !self.checks.is_empty() {
186            writeln!(f, "\nChecks:")?;
187            for check in &self.checks {
188                writeln!(
189                    f,
190                    "  - {} [{}]: {} (checked {:?} ago)",
191                    check.name, check.status, check.message, check.age
192                )?;
193            }
194        }
195
196        Ok(())
197    }
198}
199
200impl HealthReport {
201    /// Export health report as JSON
202    pub fn to_json(&self) -> String {
203        format!(
204            r#"{{"status":"{}","ready":{},"alive":{},"last_heartbeat_ms":{},"checks":[{}]}}"#,
205            self.status,
206            self.ready,
207            self.alive,
208            self.last_heartbeat.as_millis(),
209            self.checks
210                .iter()
211                .map(|c| format!(
212                    r#"{{"name":"{}","status":"{}","message":"{}"}}"#,
213                    c.name, c.status, c.message
214                ))
215                .collect::<Vec<_>>()
216                .join(",")
217        )
218    }
219}
220
221
222
223
224