Skip to main content

featherbit/metrics/
mod.rs

1//! Prometheus metrics for the gateway: per-route request counters and
2//! latency histograms plus per-node execution metrics, rendered in the
3//! Prometheus text format at the Admin API's `/metrics` endpoint.
4
5use prometheus::{
6    Encoder, HistogramOpts, HistogramVec, IntCounterVec, Opts, Registry, TextEncoder,
7};
8
9/// Global metrics registry for the gateway.
10///
11/// Owns a dedicated Prometheus [`Registry`] with all collectors
12/// pre-registered. All collectors are internally thread-safe, so a single
13/// instance is shared (behind an `Arc`) between the data-plane and the
14/// Admin API.
15pub struct GatewayMetrics {
16    /// Registry holding every collector below; gathered by [`Self::render`].
17    pub registry: Registry,
18    /// `gateway_requests_total` — requests by `route`, `method`, `status`.
19    pub request_count: IntCounterVec,
20    /// `gateway_request_duration_seconds` — end-to-end request latency
21    /// histogram per `route` (buckets 1ms to 5s).
22    pub request_duration: HistogramVec,
23    /// `gateway_request_errors_total` — failed requests by `route` and
24    /// `error_code`.
25    pub request_errors: IntCounterVec,
26    /// `gateway_node_executions_total` — graph node executions by `policy`,
27    /// `node_id`, `node_type`.
28    pub node_execution_count: IntCounterVec,
29    /// `gateway_node_duration_seconds` — per-node execution latency
30    /// histogram by `policy` and `node_id` (buckets 0.1ms to 500ms).
31    pub node_execution_duration: HistogramVec,
32    /// `gateway_node_errors_total` — node failures by `policy`, `node_id`,
33    /// `error_code`.
34    pub node_errors: IntCounterVec,
35    /// `gateway_consumer_requests_total` — requests attributed to an
36    /// authenticated `consumer`, by `route` (parity with APISIX's prometheus
37    /// per-consumer counter). Recorded by the `prometheus` node, which must be
38    /// placed after the auth node that attaches the consumer.
39    pub consumer_requests: IntCounterVec,
40}
41
42impl GatewayMetrics {
43    /// Creates a fresh registry with all gateway collectors registered.
44    ///
45    /// Panics only if a collector cannot be built or registered, which is
46    /// impossible with these fixed names/labels — treated as a programmer
47    /// error at startup.
48    pub fn new() -> Self {
49        let registry = Registry::new();
50
51        let request_count = IntCounterVec::new(
52            Opts::new("gateway_requests_total", "Total number of requests"),
53            &["route", "method", "status"],
54        )
55        .unwrap();
56
57        let request_duration = HistogramVec::new(
58            HistogramOpts::new(
59                "gateway_request_duration_seconds",
60                "Request duration in seconds",
61            )
62            .buckets(vec![
63                0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 5.0,
64            ]),
65            &["route"],
66        )
67        .unwrap();
68
69        let request_errors = IntCounterVec::new(
70            Opts::new(
71                "gateway_request_errors_total",
72                "Total number of request errors",
73            ),
74            &["route", "error_code"],
75        )
76        .unwrap();
77
78        let node_execution_count = IntCounterVec::new(
79            Opts::new("gateway_node_executions_total", "Total node executions"),
80            &["policy", "node_id", "node_type"],
81        )
82        .unwrap();
83
84        let node_execution_duration = HistogramVec::new(
85            HistogramOpts::new("gateway_node_duration_seconds", "Node execution duration")
86                .buckets(vec![0.0001, 0.0005, 0.001, 0.005, 0.01, 0.05, 0.1, 0.5]),
87            &["policy", "node_id"],
88        )
89        .unwrap();
90
91        let node_errors = IntCounterVec::new(
92            Opts::new("gateway_node_errors_total", "Total node errors"),
93            &["policy", "node_id", "error_code"],
94        )
95        .unwrap();
96
97        let consumer_requests = IntCounterVec::new(
98            Opts::new(
99                "gateway_consumer_requests_total",
100                "Total requests per consumer",
101            ),
102            &["consumer", "route"],
103        )
104        .unwrap();
105
106        registry.register(Box::new(request_count.clone())).unwrap();
107        registry
108            .register(Box::new(request_duration.clone()))
109            .unwrap();
110        registry.register(Box::new(request_errors.clone())).unwrap();
111        registry
112            .register(Box::new(node_execution_count.clone()))
113            .unwrap();
114        registry
115            .register(Box::new(node_execution_duration.clone()))
116            .unwrap();
117        registry.register(Box::new(node_errors.clone())).unwrap();
118        registry
119            .register(Box::new(consumer_requests.clone()))
120            .unwrap();
121
122        Self {
123            registry,
124            request_count,
125            request_duration,
126            request_errors,
127            node_execution_count,
128            node_execution_duration,
129            node_errors,
130            consumer_requests,
131        }
132    }
133
134    /// Renders all registered metrics in the Prometheus text exposition
135    /// format, as served by the Admin API's `/metrics` endpoint.
136    pub fn render(&self) -> String {
137        let encoder = TextEncoder::new();
138        let metric_families = self.registry.gather();
139        let mut buffer = Vec::new();
140        encoder.encode(&metric_families, &mut buffer).unwrap();
141        String::from_utf8(buffer).unwrap()
142    }
143}