bbox_map_server/
metrics.rs

1use once_cell::sync::OnceCell;
2use prometheus::{HistogramVec, IntCounterVec, IntGaugeVec, Registry};
3
4#[derive(Clone)]
5pub struct WmsMetrics {
6    pub wms_requests_counter: IntCounterVec,
7    pub fcgi_client_pool_available: Vec<IntGaugeVec>,
8    pub fcgi_client_wait_seconds: Vec<HistogramVec>,
9    pub fcgi_cache_count: Vec<IntGaugeVec>,
10    pub fcgi_cache_hit: Vec<IntGaugeVec>,
11}
12
13impl Default for WmsMetrics {
14    fn default() -> Self {
15        WmsMetrics {
16            wms_requests_counter: IntCounterVec::new(
17                prometheus::core::Opts::new("dummy", "dummy"),
18                &[],
19            )
20            .unwrap(),
21            fcgi_client_pool_available: Vec::new(),
22            fcgi_client_wait_seconds: Vec::new(),
23            fcgi_cache_count: Vec::new(),
24            fcgi_cache_hit: Vec::new(),
25        }
26    }
27}
28
29pub fn wms_metrics(num_fcgi_processes: usize) -> &'static WmsMetrics {
30    static METRICS: OnceCell<WmsMetrics> = OnceCell::new();
31    METRICS.get_or_init(|| {
32        let opts = prometheus::opts!("requests_total", "Total number of WMS requests")
33            .namespace("bbox_wms");
34        let wms_requests_counter =
35            IntCounterVec::new(opts, &["endpoint", "backend", "fcgino"]).unwrap();
36        let fcgi_cache_count = (0..num_fcgi_processes)
37            .map(|fcgino| {
38                let opts = prometheus::opts!(
39                    format!("fcgi_cache_count_{fcgino}"),
40                    "FCGI project cache size"
41                )
42                .namespace("bbox_wms");
43                IntGaugeVec::new(opts, &["backend"]).unwrap()
44            })
45            .collect();
46        let fcgi_client_pool_available = (0..num_fcgi_processes)
47            .map(|fcgino| {
48                let opts = prometheus::opts!(
49                    format!("fcgi_client_pool_available_{fcgino}"),
50                    "FCGI clients available in pool"
51                )
52                .namespace("bbox_wms");
53                IntGaugeVec::new(opts, &["backend"]).unwrap()
54            })
55            .collect();
56        let fcgi_client_wait_seconds = (0..num_fcgi_processes)
57            .map(|fcgino| {
58                let opts = prometheus::opts!(
59                    format!("fcgi_client_wait_seconds_{fcgino}"),
60                    "FCGI client wait time"
61                )
62                .namespace("bbox_wms");
63                HistogramVec::new(opts.into(), &["backend"]).unwrap()
64            })
65            .collect();
66        let fcgi_cache_hit = (0..num_fcgi_processes)
67            .map(|fcgino| {
68                let opts =
69                    prometheus::opts!(format!("fcgi_cache_hit_{fcgino}"), "FCGI project cache hit")
70                        .namespace("bbox_wms");
71                IntGaugeVec::new(opts, &["backend"]).unwrap()
72            })
73            .collect();
74        WmsMetrics {
75            wms_requests_counter,
76            fcgi_client_pool_available,
77            fcgi_client_wait_seconds,
78            fcgi_cache_count,
79            fcgi_cache_hit,
80        }
81    })
82}
83
84pub fn register_metrics(prometheus: &Registry, metrics: &WmsMetrics) {
85    // We use the Prometheus API, using
86    // https://docs.rs/opentelemetry-prometheus/
87    // would be more portable
88    prometheus
89        .register(Box::new(metrics.wms_requests_counter.clone()))
90        .unwrap();
91    for no in 0..metrics.fcgi_cache_count.len() {
92        prometheus
93            .register(Box::new(metrics.fcgi_client_pool_available[no].clone()))
94            .unwrap();
95        prometheus
96            .register(Box::new(metrics.fcgi_client_wait_seconds[no].clone()))
97            .unwrap();
98        prometheus
99            .register(Box::new(metrics.fcgi_cache_count[no].clone()))
100            .unwrap();
101        prometheus
102            .register(Box::new(metrics.fcgi_cache_hit[no].clone()))
103            .unwrap();
104    }
105}