1use std::{
10 fmt,
11 net::SocketAddr,
12 sync::{Arc, OnceLock},
13};
14
15use iota_metrics::peak::IntGaugeWithPeakGauge;
16use prometheus_filtered::{
17 Histogram, IntCounterVec, MetricLevel, Registry, register_histogram_with_registry,
18 register_int_counter_vec_with_registry,
19};
20
21mod connection;
22mod io;
23mod scope;
24mod tls;
25
26pub(crate) use connection::ConnectionGuard;
27pub(crate) use io::TrackedIo;
28use scope::Scope;
29pub(crate) use tls::HandshakeResult;
30
31const CONNECTION_LIFETIME_SEC_BUCKETS: &[f64] =
33 &[1.0, 5.0, 30.0, 60.0, 300.0, 1800.0, 14400.0, 86400.0];
34
35#[derive(Clone)]
37pub struct ListenerMetrics(Arc<Inner>);
38
39impl fmt::Debug for ListenerMetrics {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 f.debug_struct("ListenerMetrics")
42 .field("listener", &self.0.prefix)
43 .finish_non_exhaustive()
44 }
45}
46
47impl ListenerMetrics {
48 pub fn new(prefix: &str, registry: &Registry, level: MetricLevel) -> Self {
55 Self(Arc::new(Inner {
56 prefix: prefix.to_owned(),
57 registry: registry.clone(),
58 level,
59 metrics: Metrics::new(prefix, registry, level),
60 tls: OnceLock::new(),
61 }))
62 }
63
64 pub(crate) fn enable_tls(&self) {
67 self.0.tls.get_or_init(|| {
68 Arc::new(tls::Metrics::new(
69 &self.0.prefix,
70 &self.0.registry,
71 self.0.level,
72 ))
73 });
74 }
75
76 fn record_accept(&self, remote: SocketAddr) -> ConnectionGuard {
79 let metrics = &self.0.metrics;
80 let scope: &str = Scope::of(remote.ip()).into();
81 metrics
82 .inbound_connections_accepted
83 .with_label_values(&[scope])
84 .inc();
85 metrics.inbound_connections.inc();
86 ConnectionGuard::new(self.0.clone())
87 }
88}
89
90struct Inner {
91 prefix: String,
92 registry: Registry,
93 level: MetricLevel,
94 metrics: Metrics,
95 tls: OnceLock<Arc<tls::Metrics>>,
97}
98
99struct Metrics {
100 inbound_connections_accepted: IntCounterVec,
101 inbound_connections: IntGaugeWithPeakGauge,
102 connection_lifetime_seconds: Histogram,
103}
104
105impl Metrics {
106 fn new(prefix: &str, registry: &Registry, level: MetricLevel) -> Self {
107 Self {
108 inbound_connections_accepted: register_int_counter_vec_with_registry!(
109 format!("{prefix}_inbound_connections_accepted"),
110 "Sockets accepted, counted from the TCP accept, before any TLS handshake. \
111 The scope is where the remote address is: loopback, private or public",
112 &["scope"],
113 registry;
114 level
115 )
116 .expect("the metrics of a listener register without collision"),
117 inbound_connections: IntGaugeWithPeakGauge::register(
118 (
119 &format!("{prefix}_inbound_connections"),
120 "The number of connections open now. A connection counts from its TCP accept \
121 to its close, TLS handshake included",
122 ),
123 (
124 &format!("{prefix}_inbound_connections_peak"),
125 "The most open connections over the last 2 minutes. A scrape reports at \
126 least the current number",
127 ),
128 module_path!(),
129 registry,
130 level,
131 ),
132 connection_lifetime_seconds: register_histogram_with_registry!(
133 format!("{prefix}_connection_lifetime_seconds"),
134 "Time from the TCP accept to the close, in seconds",
135 CONNECTION_LIFETIME_SEC_BUCKETS.to_vec(),
136 registry;
137 level
138 )
139 .expect("the metrics of a listener register without collision"),
140 }
141 }
142}
143
144#[cfg(test)]
145mod tests;