faucet_cli/serve/triggers/
metrics.rs1use metrics::{counter, describe_counter, describe_gauge, gauge};
5
6pub fn describe() {
7 describe_counter!(
8 "faucet_serve_triggers_fired_total",
9 "Events that warranted a fire, by trigger/type"
10 );
11 describe_counter!(
12 "faucet_serve_trigger_runs_enqueued_total",
13 "Fires that enqueued a run"
14 );
15 describe_counter!(
16 "faucet_serve_trigger_runs_coalesced_total",
17 "Fires deduped/coalesced (idempotency/debounce)"
18 );
19 describe_counter!(
20 "faucet_serve_trigger_runs_dropped_total",
21 "Fires dropped (e.g. queue_full)"
22 );
23 describe_counter!(
24 "faucet_serve_trigger_errors_total",
25 "Watcher poll/serve/fire errors"
26 );
27 describe_gauge!(
28 "faucet_serve_triggers_active",
29 "Number of active triggers (including webhook routes, which spawn no watcher task)"
30 );
31 describe_gauge!(
32 "faucet_serve_trigger_healthy",
33 "1 if the watcher is healthy, else 0"
34 );
35 describe_gauge!(
36 "faucet_serve_trigger_last_fire_unix_seconds",
37 "Unix time of the watcher's last fire"
38 );
39}
40
41pub fn active(n: usize) {
42 gauge!("faucet_serve_triggers_active").set(n as f64);
43}
44
45pub fn preinit(trigger: &str, kind: &'static str) {
52 counter!("faucet_serve_triggers_fired_total", "trigger" => trigger.to_string(), "type" => kind)
53 .increment(0);
54 counter!("faucet_serve_trigger_runs_enqueued_total", "trigger" => trigger.to_string())
55 .increment(0);
56 counter!("faucet_serve_trigger_runs_coalesced_total", "trigger" => trigger.to_string())
57 .increment(0);
58 counter!("faucet_serve_trigger_runs_dropped_total", "trigger" => trigger.to_string(), "reason" => "queue_full")
59 .increment(0);
60 counter!("faucet_serve_trigger_errors_total", "trigger" => trigger.to_string(), "type" => kind)
61 .increment(0);
62 healthy(trigger, true);
63}
64pub fn fired(trigger: &str, kind: &'static str) {
65 counter!("faucet_serve_triggers_fired_total", "trigger" => trigger.to_string(), "type" => kind)
66 .increment(1);
67}
68pub fn enqueued(trigger: &str) {
69 counter!("faucet_serve_trigger_runs_enqueued_total", "trigger" => trigger.to_string())
70 .increment(1);
71}
72pub fn coalesced(trigger: &str) {
73 counter!("faucet_serve_trigger_runs_coalesced_total", "trigger" => trigger.to_string())
74 .increment(1);
75}
76pub fn dropped(trigger: &str, reason: &'static str) {
77 counter!("faucet_serve_trigger_runs_dropped_total", "trigger" => trigger.to_string(), "reason" => reason).increment(1);
78}
79pub fn error(trigger: &str, kind: &'static str) {
80 counter!("faucet_serve_trigger_errors_total", "trigger" => trigger.to_string(), "type" => kind)
81 .increment(1);
82}
83pub fn healthy(trigger: &str, ok: bool) {
84 gauge!("faucet_serve_trigger_healthy", "trigger" => trigger.to_string()).set(if ok {
85 1.0
86 } else {
87 0.0
88 });
89}
90pub fn last_fire(trigger: &str, unix_secs: i64) {
91 gauge!("faucet_serve_trigger_last_fire_unix_seconds", "trigger" => trigger.to_string())
92 .set(unix_secs as f64);
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98 use metrics::with_local_recorder;
99 use metrics_util::debugging::{DebuggingRecorder, Snapshotter};
100
101 #[test]
102 fn emits_fired_counter() {
103 let recorder = DebuggingRecorder::new();
104 let snap: Snapshotter = recorder.snapshotter();
105 with_local_recorder(&recorder, || {
106 fired("t", "webhook");
107 });
108 let metrics = snap.snapshot().into_vec();
109 assert!(
110 metrics
111 .iter()
112 .any(|(k, _, _, _)| k.key().name() == "faucet_serve_triggers_fired_total"),
113 "fired counter not emitted"
114 );
115 }
116
117 #[test]
118 fn preinit_emits_every_per_trigger_series_at_zero() {
119 let recorder = DebuggingRecorder::new();
120 let snap: Snapshotter = recorder.snapshotter();
121 with_local_recorder(&recorder, || {
122 preinit("t", "webhook");
123 });
124 let metrics = snap.snapshot().into_vec();
125 let names: std::collections::BTreeSet<_> =
126 metrics.iter().map(|(k, _, _, _)| k.key().name()).collect();
127 for expected in [
128 "faucet_serve_triggers_fired_total",
129 "faucet_serve_trigger_runs_enqueued_total",
130 "faucet_serve_trigger_runs_coalesced_total",
131 "faucet_serve_trigger_runs_dropped_total",
132 "faucet_serve_trigger_errors_total",
133 "faucet_serve_trigger_healthy",
134 ] {
135 assert!(
136 names.contains(expected),
137 "preinit missing series {expected}"
138 );
139 }
140 assert!(
142 !names.contains("faucet_serve_trigger_last_fire_unix_seconds"),
143 "last_fire must not be pre-emitted"
144 );
145 }
146}