Skip to main content

faucet_cli/notify/
metrics.rs

1//! Prometheus surface for notifications (#280).
2//!
3//! - `faucet_notifications_sent_total{channel,event,outcome}` — deliveries
4//!   attempted; `outcome` ∈ `ok` | `error`.
5//! - `faucet_notifications_dropped_total{channel,reason}` — events not
6//!   delivered; `reason` ∈ `coalesced` | `channel_error` | `severity_gated`.
7//! - `faucet_notification_dispatch_duration_seconds{channel}` — per-delivery
8//!   latency.
9//!
10//! Follows the CLI-side convention (`faucet_schedule_*`, `faucet_serve_*`,
11//! `faucet_pipeline_sla_*`): plain `metrics` macros, low-cardinality labels
12//! only (never pipeline/row/record values as labels — those stay in logs).
13
14use metrics::{counter, describe_counter, describe_histogram, histogram};
15use std::sync::Once;
16
17static DESCRIBE: Once = Once::new();
18
19fn describe() {
20    DESCRIBE.call_once(|| {
21        describe_counter!(
22            "faucet_notifications_sent_total",
23            "Notification deliveries attempted, by channel/event/outcome"
24        );
25        describe_counter!(
26            "faucet_notifications_dropped_total",
27            "Notifications dropped before/at delivery, by channel/reason"
28        );
29        describe_histogram!(
30            "faucet_notification_dispatch_duration_seconds",
31            "Per-delivery notification dispatch latency in seconds"
32        );
33    });
34}
35
36/// Record a delivery attempt outcome.
37pub fn record_sent(channel: &'static str, event: &'static str, ok: bool) {
38    describe();
39    counter!(
40        "faucet_notifications_sent_total",
41        "channel" => channel,
42        "event" => event,
43        "outcome" => if ok { "ok" } else { "error" },
44    )
45    .increment(1);
46}
47
48/// Record a dropped notification.
49pub fn record_dropped(channel: &'static str, reason: &'static str) {
50    describe();
51    counter!(
52        "faucet_notifications_dropped_total",
53        "channel" => channel,
54        "reason" => reason,
55    )
56    .increment(1);
57}
58
59/// Record per-delivery dispatch latency.
60pub fn record_duration(channel: &'static str, secs: f64) {
61    describe();
62    histogram!("faucet_notification_dispatch_duration_seconds", "channel" => channel).record(secs);
63}
64
65#[cfg(test)]
66mod tests {
67    // Emit helpers must be callable without an installed recorder (the `metrics`
68    // macros no-op) — a panic here would take down every run in a build without
69    // observability installed.
70    #[test]
71    fn emitting_without_recorder_is_a_noop() {
72        super::record_sent("slack", "run_failure", true);
73        super::record_dropped("slack", "coalesced");
74        super::record_duration("slack", 0.01);
75    }
76}