gregg_protocol/health.rs
1//! Health and readiness response type.
2
3use serde::{Deserialize, Serialize};
4
5/// Coarse readiness state shared between the daemon and the client.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
7#[serde(rename_all = "snake_case")]
8pub enum ReadinessState {
9 /// The daemon has a valid cached snapshot and `/v1/status` will return it.
10 Ready,
11 /// The daemon is alive but the first counter delta is not yet available;
12 /// `/v1/status` returns `503`.
13 Warming,
14 /// The daemon's collector has failed; `/v1/status` returns `503`.
15 Failed,
16}
17
18/// Machine-readable category for a non-ready health response.
19///
20/// Categories are deliberately coarse so the client can render consistent
21/// diagnostics without leaking implementation details.
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
23#[serde(rename_all = "snake_case")]
24pub enum HealthCategory {
25 /// Counter delta is still being collected.
26 Warming,
27 /// The native collector reported an error.
28 CollectorFailure,
29 /// The daemon is shutting down or otherwise refusing traffic.
30 NotServing,
31}
32
33/// Health and readiness response served by the daemon.
34///
35/// The `Ready` variant carries a fresh snapshot. The other variants carry a
36/// short human-readable message and a [`HealthCategory`]; they never include
37/// filesystem paths, internal error chains, or platform-private structures.
38#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
39#[serde(rename_all = "snake_case")]
40pub struct HealthResponse {
41 /// Daemon schema version, always
42 /// [`crate::SCHEMA_VERSION_V1`].
43 pub schema_version: u16,
44 /// Current readiness state.
45 pub state: ReadinessState,
46 /// Coarse category for non-ready responses. `None` when `state == Ready`.
47 #[serde(default, skip_serializing_if = "Option::is_none")]
48 pub category: Option<HealthCategory>,
49 /// Short human-readable message. Never includes filesystem paths or
50 /// internal error chains.
51 #[serde(default, skip_serializing_if = "Option::is_none")]
52 pub message: Option<String>,
53 /// Cached snapshot, present only when `state == Ready`.
54 #[serde(default, skip_serializing_if = "Option::is_none")]
55 pub snapshot: Option<crate::StatusSnapshot>,
56}
57
58impl HealthResponse {
59 /// A `Ready` response wrapping the supplied snapshot.
60 #[must_use]
61 pub fn ready(snapshot: crate::StatusSnapshot) -> Self {
62 Self {
63 schema_version: crate::SCHEMA_VERSION_V1,
64 state: ReadinessState::Ready,
65 category: None,
66 message: None,
67 snapshot: Some(snapshot),
68 }
69 }
70
71 /// A `Warming` response with a default message.
72 #[must_use]
73 pub fn warming() -> Self {
74 Self::warming_with_message("collector warming up")
75 }
76
77 /// A `Warming` response with a custom message.
78 #[must_use]
79 pub fn warming_with_message(message: impl Into<String>) -> Self {
80 Self {
81 schema_version: crate::SCHEMA_VERSION_V1,
82 state: ReadinessState::Warming,
83 category: Some(HealthCategory::Warming),
84 message: Some(message.into()),
85 snapshot: None,
86 }
87 }
88
89 /// A `Failed` response with the given category and message.
90 #[must_use]
91 pub fn failed(category: HealthCategory, message: impl Into<String>) -> Self {
92 Self {
93 schema_version: crate::SCHEMA_VERSION_V1,
94 state: ReadinessState::Failed,
95 category: Some(category),
96 message: Some(message.into()),
97 snapshot: None,
98 }
99 }
100}