liminal_server/
metrics.rs1use std::sync::OnceLock;
10
11use liminal::metrics::{
12 CounterHandle, GaugeHandle, MetricsRegistry, global_registry, install_global_registry,
13};
14
15const CONNECTIONS_ACTIVE: &str = "liminal_connections_active";
16const PUBLISHES_TOTAL: &str = "liminal_publishes_total";
17const DELIVERIES_TOTAL: &str = "liminal_deliveries_total";
18
19static SERVER_METRICS: OnceLock<ServerMetrics> = OnceLock::new();
20
21#[derive(Clone, Debug)]
23struct ServerMetrics {
24 connections_active: GaugeHandle,
25 publishes_total: CounterHandle,
26 deliveries_total: CounterHandle,
27}
28
29pub fn init() {
35 if SERVER_METRICS.get().is_some() {
36 return;
37 }
38 let Some(registry) = global_or_install() else {
39 return;
40 };
41 if let Some(metrics) = ServerMetrics::register(registry) {
42 let _ = SERVER_METRICS.set(metrics);
43 }
44}
45
46pub fn connection_spawned() {
49 if let Some(metrics) = SERVER_METRICS.get() {
50 metrics.connections_active.increment();
51 }
52}
53
54pub fn connection_closed() {
57 if let Some(metrics) = SERVER_METRICS.get() {
58 metrics.connections_active.decrement();
59 }
60}
61
62pub fn publish_accepted() {
65 if let Some(metrics) = SERVER_METRICS.get() {
66 metrics.publishes_total.increment();
67 }
68}
69
70pub fn deliveries_recorded(count: u64) {
74 if count == 0 {
75 return;
76 }
77 if let Some(metrics) = SERVER_METRICS.get() {
78 metrics.deliveries_total.increment_by(count);
79 }
80}
81
82#[cfg(test)]
90pub(crate) fn publishes_total_value() -> Option<u64> {
91 use liminal::metrics::MetricValue;
92
93 let registry = global_registry()?;
94 registry
95 .snapshot()
96 .metrics()
97 .iter()
98 .find(|metric| metric.name == PUBLISHES_TOTAL)
99 .and_then(|metric| match metric.value {
100 MetricValue::Counter(value) => Some(value),
101 MetricValue::Gauge(_) | MetricValue::Histogram(_) => None,
102 })
103}
104
105impl ServerMetrics {
106 fn register(registry: &MetricsRegistry) -> Option<Self> {
107 let connections_active = registry
108 .register_gauge(CONNECTIONS_ACTIVE, no_labels())
109 .ok()?;
110 let publishes_total = registry
111 .register_counter(PUBLISHES_TOTAL, no_labels())
112 .ok()?;
113 let deliveries_total = registry
114 .register_counter(DELIVERIES_TOTAL, no_labels())
115 .ok()?;
116 Some(Self {
117 connections_active,
118 publishes_total,
119 deliveries_total,
120 })
121 }
122}
123
124const fn no_labels() -> std::iter::Empty<(&'static str, &'static str)> {
125 std::iter::empty()
126}
127
128fn global_or_install() -> Option<&'static MetricsRegistry> {
133 if let Some(registry) = global_registry() {
134 return Some(registry);
135 }
136 let _ = install_global_registry(MetricsRegistry::new());
139 global_registry()
140}
141
142#[cfg(test)]
143mod tests {
144 use super::{
145 CONNECTIONS_ACTIVE, DELIVERIES_TOTAL, PUBLISHES_TOTAL, connection_spawned,
146 deliveries_recorded, init, publish_accepted,
147 };
148 use liminal::metrics::{global_registry, render};
149
150 #[test]
151 fn init_registers_the_three_server_families_on_the_global_registry()
152 -> Result<(), Box<dyn std::error::Error>> {
153 init();
154 connection_spawned();
155 publish_accepted();
156 deliveries_recorded(2);
157
158 let registry =
159 global_registry().ok_or("init must install and enable the global registry")?;
160 let exposition = render(®istry.snapshot());
161
162 assert!(exposition.contains(CONNECTIONS_ACTIVE));
163 assert!(exposition.contains(PUBLISHES_TOTAL));
164 assert!(exposition.contains(DELIVERIES_TOTAL));
165
166 Ok(())
167 }
168}