iota_faucet/
metrics.rs

1// Copyright (c) 2021, Facebook, Inc. and its affiliates
2// Copyright (c) Mysten Labs, Inc.
3// Modifications Copyright (c) 2024 IOTA Stiftung
4// SPDX-License-Identifier: Apache-2.0
5
6//! Prometheus metrics which can be displayed in Grafana, queried and alerted
7//! on.
8
9use std::time::Duration;
10
11use iota_network_stack::metrics::MetricsCallbackProvider;
12use prometheus::{
13    HistogramVec, IntCounterVec, IntGauge, IntGaugeVec, Registry,
14    register_histogram_vec_with_registry, register_int_counter_vec_with_registry,
15    register_int_gauge_vec_with_registry, register_int_gauge_with_registry,
16};
17use tonic::Code;
18
19/// Metrics relevant to the requests coming into the service
20#[derive(Clone, Debug)]
21pub struct RequestMetrics {
22    pub(crate) total_requests_received: IntCounterVec,
23    pub(crate) total_requests_succeeded: IntCounterVec,
24    pub(crate) total_requests_shed: IntCounterVec,
25    pub(crate) total_requests_failed: IntCounterVec,
26    pub(crate) total_requests_disconnected: IntCounterVec,
27    pub(crate) current_requests_in_flight: IntGaugeVec,
28    pub(crate) process_latency: HistogramVec,
29}
30
31/// Metrics relevant to the running of the service
32#[derive(Clone, Debug)]
33pub struct FaucetMetrics {
34    pub(crate) current_executions_in_flight: IntGauge,
35    pub(crate) total_available_coins: IntGauge,
36    pub(crate) total_discarded_coins: IntGauge,
37    pub(crate) total_coin_requests_succeeded: IntGauge,
38}
39
40const LATENCY_SEC_BUCKETS: &[f64] = &[
41    0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1., 2.5, 5., 10., 20., 30., 60., 90.,
42];
43
44impl RequestMetrics {
45    pub fn new(registry: &Registry) -> Self {
46        Self {
47            total_requests_received: register_int_counter_vec_with_registry!(
48                "total_requests_received",
49                "Total number of requests received in Faucet",
50                &["path"],
51                registry,
52            )
53            .unwrap(),
54            total_requests_succeeded: register_int_counter_vec_with_registry!(
55                "total_requests_succeeded",
56                "Total number of requests processed successfully in Faucet",
57                &["path"],
58                registry,
59            )
60            .unwrap(),
61            total_requests_shed: register_int_counter_vec_with_registry!(
62                "total_requests_shed",
63                "Total number of requests that were dropped because the service was saturated",
64                &["path"],
65                registry,
66            )
67            .unwrap(),
68            total_requests_failed: register_int_counter_vec_with_registry!(
69                "total_requests_failed",
70                "Total number of requests that started but failed with an uncaught error",
71                &["path"],
72                registry,
73            )
74            .unwrap(),
75            total_requests_disconnected: register_int_counter_vec_with_registry!(
76                "total_requests_disconnected",
77                "Total number of requests where the client disconnected before the service \
78                 returned a response",
79                &["path"],
80                registry,
81            )
82            .unwrap(),
83            current_requests_in_flight: register_int_gauge_vec_with_registry!(
84                "current_requests_in_flight",
85                "Current number of requests being processed in Faucet",
86                &["path"],
87                registry,
88            )
89            .unwrap(),
90            process_latency: register_histogram_vec_with_registry!(
91                "process_latency",
92                "Latency of processing a Faucet request",
93                &["path"],
94                LATENCY_SEC_BUCKETS.to_vec(),
95                registry,
96            )
97            .unwrap(),
98        }
99    }
100}
101
102impl FaucetMetrics {
103    pub fn new(registry: &Registry) -> Self {
104        Self {
105            current_executions_in_flight: register_int_gauge_with_registry!(
106                "current_executions_in_flight",
107                "Current number of transactions being executed in Faucet",
108                registry,
109            )
110            .unwrap(),
111            total_available_coins: register_int_gauge_with_registry!(
112                "total_available_coins",
113                "Total number of available coins in queue",
114                registry,
115            )
116            .unwrap(),
117            total_discarded_coins: register_int_gauge_with_registry!(
118                "total_discarded_coins",
119                "Total number of discarded coins",
120                registry,
121            )
122            .unwrap(),
123            total_coin_requests_succeeded: register_int_gauge_with_registry!(
124                "total_coin_requests_succeeded",
125                "Total number of requests processed successfully in Faucet (both batch and non_batched)",
126                registry,
127            )
128            .unwrap(),
129        }
130    }
131}
132
133impl MetricsCallbackProvider for RequestMetrics {
134    fn on_request(&self, path: String) {
135        self.total_requests_received
136            .with_label_values(&[path.as_str()])
137            .inc();
138    }
139
140    fn on_response(&self, path: String, latency: Duration, _status: u16, grpc_status_code: Code) {
141        self.process_latency
142            .with_label_values(&[path.as_str()])
143            .observe(latency.as_secs_f64());
144
145        match grpc_status_code {
146            Code::Ok => {
147                self.total_requests_succeeded
148                    .with_label_values(&[path.as_str()])
149                    .inc();
150            }
151            Code::Unavailable | Code::ResourceExhausted => {
152                self.total_requests_shed
153                    .with_label_values(&[path.as_str()])
154                    .inc();
155            }
156            _ => {
157                self.total_requests_failed
158                    .with_label_values(&[path.as_str()])
159                    .inc();
160            }
161        }
162    }
163
164    fn on_start(&self, path: &str) {
165        self.current_requests_in_flight
166            .with_label_values(&[path])
167            .inc();
168    }
169
170    fn on_drop(&self, path: &str) {
171        self.total_requests_disconnected
172            .with_label_values(&[path])
173            .inc();
174        self.current_requests_in_flight
175            .with_label_values(&[path])
176            .dec();
177    }
178}