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