1use crate::{service::traits::BandwidthSink, ProtocolName};
20
21use prometheus_endpoint::{
22 self as prometheus, Counter, CounterVec, Gauge, GaugeVec, HistogramOpts, MetricSource, Opts,
23 PrometheusError, Registry, SourcedCounter, U64,
24};
25
26use std::{str, sync::Arc};
27
28pub use prometheus_endpoint::{Histogram, HistogramVec};
29
30pub fn register(registry: &Registry, sources: MetricSources) -> Result<Metrics, PrometheusError> {
32 BandwidthCounters::register(registry, sources.bandwidth)?;
33 Metrics::register(registry)
34}
35
36pub fn register_without_sources(registry: &Registry) -> Result<Metrics, PrometheusError> {
38 Metrics::register(registry)
39}
40
41pub struct MetricSources {
43 pub bandwidth: Arc<dyn BandwidthSink>,
44}
45
46impl MetricSources {
47 pub fn register(
48 registry: &Registry,
49 bandwidth: Arc<dyn BandwidthSink>,
50 ) -> Result<(), PrometheusError> {
51 BandwidthCounters::register(registry, bandwidth)
52 }
53}
54
55#[derive(Clone)]
57pub struct Metrics {
58 pub connections_closed_total: CounterVec<U64>,
60 pub connections_opened_total: CounterVec<U64>,
61 pub distinct_peers_connections_closed_total: Counter<U64>,
62 pub distinct_peers_connections_opened_total: Counter<U64>,
63 pub incoming_connections_errors_total: CounterVec<U64>,
64 pub incoming_connections_total: Counter<U64>,
65 pub kademlia_query_duration: HistogramVec,
66 pub kademlia_random_queries_total: Counter<U64>,
67 pub kademlia_records_count: Gauge<U64>,
68 pub kademlia_records_sizes_total: Gauge<U64>,
69 pub kbuckets_num_nodes: GaugeVec<U64>,
70 pub listeners_local_addresses: Gauge<U64>,
71 pub listeners_errors_total: Counter<U64>,
72 pub pending_connections: Gauge<U64>,
73 pub pending_connections_errors_total: CounterVec<U64>,
74 pub requests_in_failure_total: CounterVec<U64>,
75 pub requests_in_success_total: HistogramVec,
76 pub requests_out_failure_total: CounterVec<U64>,
77 pub requests_out_success_total: HistogramVec,
78 pub requests_response_bytes_total: CounterVec<U64>,
79}
80
81impl Metrics {
82 fn register(registry: &Registry) -> Result<Self, PrometheusError> {
83 Ok(Self {
84 connections_closed_total: prometheus::register(CounterVec::new(
86 Opts::new(
87 "substrate_sub_libp2p_connections_closed_total",
88 "Total number of connections closed, by direction and reason"
89 ),
90 &["direction", "reason"]
91 )?, registry)?,
92 connections_opened_total: prometheus::register(CounterVec::new(
93 Opts::new(
94 "substrate_sub_libp2p_connections_opened_total",
95 "Total number of connections opened by direction"
96 ),
97 &["direction"]
98 )?, registry)?,
99 distinct_peers_connections_closed_total: prometheus::register(Counter::new(
100 "substrate_sub_libp2p_distinct_peers_connections_closed_total",
101 "Total number of connections closed with distinct peers"
102 )?, registry)?,
103 distinct_peers_connections_opened_total: prometheus::register(Counter::new(
104 "substrate_sub_libp2p_distinct_peers_connections_opened_total",
105 "Total number of connections opened with distinct peers"
106 )?, registry)?,
107 incoming_connections_errors_total: prometheus::register(CounterVec::new(
108 Opts::new(
109 "substrate_sub_libp2p_incoming_connections_handshake_errors_total",
110 "Total number of incoming connections that have failed during the \
111 initial handshake"
112 ),
113 &["reason"]
114 )?, registry)?,
115 incoming_connections_total: prometheus::register(Counter::new(
116 "substrate_sub_libp2p_incoming_connections_total",
117 "Total number of incoming connections on the listening sockets"
118 )?, registry)?,
119 kademlia_query_duration: prometheus::register(HistogramVec::new(
120 HistogramOpts {
121 common_opts: Opts::new(
122 "substrate_sub_libp2p_kademlia_query_duration",
123 "Duration of Kademlia queries per query type"
124 ),
125 buckets: prometheus::exponential_buckets(0.5, 2.0, 10)
126 .expect("parameters are always valid values; qed"),
127 },
128 &["type"]
129 )?, registry)?,
130 kademlia_random_queries_total: prometheus::register(Counter::new(
131 "substrate_sub_libp2p_kademlia_random_queries_total",
132 "Number of random Kademlia queries started",
133 )?, registry)?,
134 kademlia_records_count: prometheus::register(Gauge::new(
135 "substrate_sub_libp2p_kademlia_records_count",
136 "Number of records in the Kademlia records store",
137 )?, registry)?,
138 kademlia_records_sizes_total: prometheus::register(Gauge::new(
139 "substrate_sub_libp2p_kademlia_records_sizes_total",
140 "Total size of all the records in the Kademlia records store",
141 )?, registry)?,
142 kbuckets_num_nodes: prometheus::register(GaugeVec::new(
143 Opts::new(
144 "substrate_sub_libp2p_kbuckets_num_nodes",
145 "Number of nodes per kbucket per Kademlia instance"
146 ),
147 &["lower_ilog2_bucket_bound"]
148 )?, registry)?,
149 listeners_local_addresses: prometheus::register(Gauge::new(
150 "substrate_sub_libp2p_listeners_local_addresses",
151 "Number of local addresses we're listening on"
152 )?, registry)?,
153 listeners_errors_total: prometheus::register(Counter::new(
154 "substrate_sub_libp2p_listeners_errors_total",
155 "Total number of non-fatal errors reported by a listener"
156 )?, registry)?,
157 pending_connections: prometheus::register(Gauge::new(
158 "substrate_sub_libp2p_pending_connections",
159 "Number of connections in the process of being established",
160 )?, registry)?,
161 pending_connections_errors_total: prometheus::register(CounterVec::new(
162 Opts::new(
163 "substrate_sub_libp2p_pending_connections_errors_total",
164 "Total number of pending connection errors"
165 ),
166 &["reason"]
167 )?, registry)?,
168 requests_in_failure_total: prometheus::register(CounterVec::new(
169 Opts::new(
170 "substrate_sub_libp2p_requests_in_failure_total",
171 "Total number of incoming requests that the node has failed to answer"
172 ),
173 &["protocol", "reason"]
174 )?, registry)?,
175 requests_in_success_total: prometheus::register(HistogramVec::new(
176 HistogramOpts {
177 common_opts: Opts::new(
178 "substrate_sub_libp2p_requests_in_success_total",
179 "For successful incoming requests, time between receiving the request and \
180 starting to send the response"
181 ),
182 buckets: prometheus::exponential_buckets(0.001, 2.0, 16)
183 .expect("parameters are always valid values; qed"),
184 },
185 &["protocol"]
186 )?, registry)?,
187 requests_out_failure_total: prometheus::register(CounterVec::new(
188 Opts::new(
189 "substrate_sub_libp2p_requests_out_failure_total",
190 "Total number of requests that have failed"
191 ),
192 &["protocol", "reason"]
193 )?, registry)?,
194 requests_out_success_total: prometheus::register(HistogramVec::new(
195 HistogramOpts {
196 common_opts: Opts::new(
197 "substrate_sub_libp2p_requests_out_success_total",
198 "For successful outgoing requests, time between a request's start and finish"
199 ),
200 buckets: prometheus::exponential_buckets(0.001, 2.0, 16)
201 .expect("parameters are always valid values; qed"),
202 },
203 &["protocol"]
204 )?, registry)?,
205 requests_response_bytes_total: prometheus::register(CounterVec::new(
206 Opts::new(
207 "substrate_sub_libp2p_requests_response_bytes_total",
208 "Total bytes sent and received by request-response protocols"
209 ),
210 &["direction", "protocol"]
211 )?, registry)?,
212 })
213 }
214}
215
216#[derive(Clone, Debug)]
218pub struct PeerStoreMetrics {
219 pub num_banned_peers: Gauge<U64>,
220 pub num_discovered: Gauge<U64>,
221}
222
223impl PeerStoreMetrics {
224 pub fn register(registry: &Registry) -> Result<Self, PrometheusError> {
225 Ok(Self {
226 num_banned_peers: prometheus::register(
227 Gauge::new(
228 "substrate_sub_libp2p_peerset_num_banned_peers",
229 "Number of banned peers stored in the peerset manager",
230 )?,
231 registry,
232 )?,
233 num_discovered: prometheus::register(
234 Gauge::new(
235 "substrate_sub_libp2p_peerset_num_discovered",
236 "Number of nodes stored in the peerset manager",
237 )?,
238 registry,
239 )?,
240 })
241 }
242}
243
244#[derive(Clone)]
246pub struct BandwidthCounters(Arc<dyn BandwidthSink>);
247
248impl BandwidthCounters {
249 fn register(registry: &Registry, sinks: Arc<dyn BandwidthSink>) -> Result<(), PrometheusError> {
252 prometheus::register(
253 SourcedCounter::new(
254 &Opts::new("substrate_sub_libp2p_network_bytes_total", "Total bandwidth usage")
255 .variable_label("direction"),
256 BandwidthCounters(sinks),
257 )?,
258 registry,
259 )?;
260
261 Ok(())
262 }
263}
264
265impl MetricSource for BandwidthCounters {
266 type N = u64;
267
268 fn collect(&self, mut set: impl FnMut(&[&str], Self::N)) {
269 set(&["in"], self.0.total_inbound());
270 set(&["out"], self.0.total_outbound());
271 }
272}
273
274#[derive(Debug, Clone)]
278pub struct NotificationMetrics {
279 metrics: Option<InnerNotificationMetrics>,
281}
282
283impl NotificationMetrics {
284 pub fn new(registry: Option<&Registry>) -> NotificationMetrics {
286 let metrics = match registry {
287 Some(registry) => InnerNotificationMetrics::register(registry).ok(),
288 None => None,
289 };
290
291 Self { metrics }
292 }
293
294 pub fn register_substream_opened(&self, protocol: &ProtocolName) {
296 if let Some(metrics) = &self.metrics {
297 metrics.notifications_streams_opened_total.with_label_values(&[&protocol]).inc();
298 }
299 }
300
301 pub fn register_substream_closed(&self, protocol: &ProtocolName) {
303 if let Some(metrics) = &self.metrics {
304 metrics
305 .notifications_streams_closed_total
306 .with_label_values(&[&protocol[..]])
307 .inc();
308 }
309 }
310
311 pub fn register_notification_sent(&self, protocol: &ProtocolName, size: usize) {
313 if let Some(metrics) = &self.metrics {
314 metrics
315 .notifications_sizes
316 .with_label_values(&["out", protocol])
317 .observe(size as f64);
318 }
319 }
320
321 pub fn register_notification_received(&self, protocol: &ProtocolName, size: usize) {
323 if let Some(metrics) = &self.metrics {
324 metrics
325 .notifications_sizes
326 .with_label_values(&["in", protocol])
327 .observe(size as f64);
328 }
329 }
330
331 pub fn set_peerset_num_connected(
333 &self,
334 protocol: &ProtocolName,
335 in_reserved: usize,
336 in_non_reserved: usize,
337 out_reserved: usize,
338 out_non_reserved: usize,
339 num_disconnected: usize,
340 num_backoff: usize,
341 ) {
342 if let Some(metrics) = &self.metrics {
343 metrics
344 .peerset_num_connected
345 .with_label_values(&["in", "reserved", protocol])
346 .set(in_reserved as u64);
347 metrics
348 .peerset_num_connected
349 .with_label_values(&["in", "non-reserved", protocol])
350 .set(in_non_reserved as u64);
351 metrics
352 .peerset_num_connected
353 .with_label_values(&["out", "reserved", protocol])
354 .set(out_reserved as u64);
355 metrics
356 .peerset_num_connected
357 .with_label_values(&["out", "non-reserved", protocol])
358 .set(out_non_reserved as u64);
359
360 metrics
361 .peerset_num_state
362 .with_label_values(&["disconnected", protocol])
363 .set(num_disconnected as u64);
364 metrics
365 .peerset_num_state
366 .with_label_values(&["backoff", protocol])
367 .set(num_backoff as u64);
368 }
369 }
370}
371
372#[derive(Debug, Clone)]
374struct InnerNotificationMetrics {
375 pub notifications_streams_opened_total: CounterVec<U64>,
377
378 pub notifications_streams_closed_total: CounterVec<U64>,
380
381 pub notifications_sizes: HistogramVec,
383
384 pub peerset_num_connected: GaugeVec<U64>,
386
387 pub peerset_num_state: GaugeVec<U64>,
389}
390
391impl InnerNotificationMetrics {
392 fn register(registry: &Registry) -> Result<Self, PrometheusError> {
393 Ok(Self {
394 notifications_sizes: prometheus::register(
395 HistogramVec::new(
396 HistogramOpts {
397 common_opts: Opts::new(
398 "substrate_sub_libp2p_notifications_sizes",
399 "Sizes of the notifications send to and received from all nodes",
400 ),
401 buckets: prometheus::exponential_buckets(64.0, 4.0, 8)
402 .expect("parameters are always valid values; qed"),
403 },
404 &["direction", "protocol"],
405 )?,
406 registry,
407 )?,
408 notifications_streams_closed_total: prometheus::register(
409 CounterVec::new(
410 Opts::new(
411 "substrate_sub_libp2p_notifications_streams_closed_total",
412 "Total number of notification substreams that have been closed",
413 ),
414 &["protocol"],
415 )?,
416 registry,
417 )?,
418 notifications_streams_opened_total: prometheus::register(
419 CounterVec::new(
420 Opts::new(
421 "substrate_sub_libp2p_notifications_streams_opened_total",
422 "Total number of notification substreams that have been opened",
423 ),
424 &["protocol"],
425 )?,
426 registry,
427 )?,
428 peerset_num_connected: prometheus::register(
429 GaugeVec::new(
430 Opts::new(
431 "substrate_sub_libp2p_peerset_num_connected",
432 "Number of connected peers per direction, reservation status and protocol",
433 ),
434 &["direction", "kind", "protocol"],
435 )?,
436 registry,
437 )?,
438 peerset_num_state: prometheus::register(
439 GaugeVec::new(
440 Opts::new(
441 "substrate_sub_libp2p_peerset_num_state",
442 "Number of peers per state in the peerset manager",
443 ),
444 &["state", "protocol"],
445 )?,
446 registry,
447 )?,
448 })
449 }
450}