Skip to main content

reliakit_health/
lib.rs

1//! Health status types and a criticality-aware aggregator.
2//!
3//! `reliakit-health` answers one question: *given the state of my components,
4//! what is the overall health of the service?* It is plain data — it does not
5//! run checks, read the clock, or perform I/O. You report each component's
6//! status; it rolls them up into one [`Health`], applying per-component
7//! [`Criticality`] so a non-critical dependency going down *degrades* the
8//! service instead of *downing* it.
9//!
10//! Typical homes for it: a `/health` or `/readyz` endpoint, a Kubernetes
11//! readiness/liveness probe, or a status page.
12//!
13//! # Two ways to aggregate
14//!
15//! - Allocation-free: build a fixed array of [`Check`]s (which borrow their
16//!   strings) and call [`aggregate`]. Works in `no_std` without `alloc`.
17//! - Owned and dynamic: build a [`HealthReport`] with the
18//!   `critical`/`optional`/`with` builder, then ask for
19//!   [`overall`](HealthReport::overall), a [`summary`](HealthReport::summary),
20//!   or the [`reasons`](HealthReport::reasons). Requires `alloc`.
21//!
22//! # Roll-up rules
23//!
24//! The overall status is the worst (most severe) effective status, where
25//! `Healthy < Degraded < Unhealthy`. A `Critical` component contributes its
26//! status unchanged; an `Optional` component's status is capped at `Degraded`.
27//! An empty set is `Healthy`.
28//!
29//! # Example
30//!
31//! ```
32//! use reliakit_health::{Health, HealthReport};
33//!
34//! let report = HealthReport::new()
35//!     .critical("database", Health::Healthy)
36//!     .optional("cache", Health::Unhealthy) // non-critical: only degrades
37//!     .critical("queue", Health::Degraded)
38//!     .detail("redelivery backlog");
39//!
40//! let overall = report.overall();
41//! assert_eq!(overall, Health::Degraded);
42//!
43//! // Map to an HTTP status for a /health endpoint.
44//! let http = if overall.is_operational() { 200 } else { 503 };
45//! assert_eq!(http, 200);
46//! ```
47//!
48//! # Composing with the other resilience crates
49//!
50//! The other `reliakit-*` resilience crates produce signals this crate
51//! summarizes: a tripped circuit breaker maps to an `Unhealthy` (or `Degraded`)
52//! component, a full bulkhead or rate limiter to `Degraded`, an expired deadline
53//! to `Degraded`. `reliakit-health` only reports; it never changes behavior.
54//!
55//! # Feature flags
56//!
57//! - `std` (default) enables the standard library and implies `alloc`.
58//! - `alloc` enables [`HealthReport`], [`Component`], and [`Summary`]. The
59//!   [`Health`], [`Criticality`], and [`Check`] types and [`aggregate`] need
60//!   neither.
61//!
62//! # `no_std`
63//!
64//! The crate is `no_std`-friendly. With `--no-default-features` you get the
65//! allocation-free core ([`Health`], [`Criticality`], [`Check`], [`aggregate`]);
66//! add `alloc` for the owned [`HealthReport`].
67
68#![cfg_attr(not(feature = "std"), no_std)]
69#![forbid(unsafe_code)]
70#![warn(missing_docs)]
71
72#[cfg(feature = "alloc")]
73extern crate alloc;
74
75mod check;
76mod health;
77#[cfg(feature = "alloc")]
78mod report;
79
80pub use check::{aggregate, Check};
81pub use health::{Criticality, Health};
82#[cfg(feature = "alloc")]
83pub use report::{Component, HealthReport, Summary};