1#![deny(dead_code)]
18
19use prometheus::{IntCounterVec, IntGauge, Opts, Registry};
20
21use libp2p_core::PeerId;
22use libp2p_swarm::NetworkBehaviourAction;
23
24use crate::handler::{KademliaHandlerEvent, KademliaHandlerIn};
25use crate::{KademliaEvent, QueryId};
26
27pub(super) enum Kind {
28 Request,
29 Response,
30 Error,
31}
32
33struct InnerMetrics {
34 sent_requests: IntCounterVec,
36 sent_responses: IntCounterVec,
38 received_requests: IntCounterVec,
40 received_responses: IntCounterVec,
42 errors: IntCounterVec,
43 records_stored: IntGauge,
44 connected_nodes: IntGauge,
45 routing_table_size: IntGauge,
46 kademlia_events: IntCounterVec,
48 }
50
51enum Inner {
52 Disabled,
53 Enabled(InnerMetrics),
54}
55
56pub(super) struct Metrics {
57 inner: Inner,
58}
59
60impl Metrics {
61 pub(super) fn disabled() -> Self {
62 Self {
63 inner: Inner::Disabled,
64 }
65 }
66
67 pub(super) fn enabled(registry: &Registry, peer_id: &PeerId) -> Self {
68 let peer_id = peer_id.to_string();
69 let opts = |name: &str| -> Opts {
70 let mut opts = Opts::new(name, name)
71 .namespace("libp2p")
72 .subsystem("kad")
73 .const_label("peer_id", peer_id.as_str());
74 opts.name = name.into();
75 opts.help = name.into(); opts
77 };
78
79 let counter = |name: &str, label_names: &[&str]| -> IntCounterVec {
81 let counter = IntCounterVec::new(opts(name), label_names)
82 .expect(format!("create {}", name).as_str());
83
84 registry
85 .register(Box::new(counter.clone()))
86 .expect(format!("register {}", name).as_str());
87 counter
88 };
89
90 let gauge = |name: &str| -> IntGauge {
91 let gauge = IntGauge::with_opts(opts(name)).expect(format!("create {}", name).as_str());
92 registry
93 .register(Box::new(gauge.clone()))
94 .expect(format!("register {}", name).as_str());
95 gauge
96 };
97
98 let name = &["name"];
103
104 let sent_requests = counter("sent_requests", name);
105 let received_requests = counter("received_requests", name);
106 let sent_responses = counter("sent_responses", name);
107 let received_responses = counter("received_responses", name);
108 let kademlia_events = counter("kademlia_event", name);
109 let errors = counter("errors", name);
110 let records_stored = gauge("records_stored");
111 let connected_nodes = gauge("connected_nodes");
112 let routing_table_size = gauge("routing_table_size");
113
114 Self {
115 inner: Inner::Enabled(InnerMetrics {
116 sent_requests,
117 received_responses,
118 received_requests,
119 sent_responses,
120 errors,
121 records_stored,
122 connected_nodes,
123 kademlia_events,
124 routing_table_size,
125 }),
126 }
127 }
128
129 fn with_metrics<F>(&self, f: F)
130 where
131 F: FnOnce(&InnerMetrics),
132 {
133 if let Inner::Enabled(metrics) = &self.inner {
134 f(metrics)
135 }
136 }
137
138 fn inc_by_name(name: &str, counter: &IntCounterVec) {
139 match counter.get_metric_with_label_values(&[name]) {
140 Ok(c) => c.inc(),
141 Err(e) => log::warn!("failed to increment counter {}: {:?}", name, e),
142 }
143 }
144
145 pub(super) fn node_connected(&self) {
146 self.with_metrics(|m| m.connected_nodes.inc());
147 }
148
149 pub(super) fn received(&self, event: &KademliaHandlerEvent<QueryId>) {
150 use Kind::*;
151
152 let (name, kind) = match event {
153 KademliaHandlerEvent::FindNodeReq { .. } => ("find_node_req", Request),
155 KademliaHandlerEvent::GetProvidersReq { .. } => ("get_providers_req", Request),
156 KademliaHandlerEvent::AddProvider { .. } => ("add_provider", Request),
157 KademliaHandlerEvent::GetRecord { .. } => ("get_record", Request),
158 KademliaHandlerEvent::PutRecord { .. } => ("put_record", Request),
159
160 KademliaHandlerEvent::FindNodeRes { .. } => ("find_node_res", Response),
162 KademliaHandlerEvent::GetProvidersRes { .. } => ("get_providers_res", Response),
163 KademliaHandlerEvent::GetRecordRes { .. } => ("get_record_res", Response),
164 KademliaHandlerEvent::PutRecordRes { .. } => ("put_record_res", Response),
165 KademliaHandlerEvent::ProtocolConfirmed { .. } => ("protocol_confirmed", Response),
166
167 KademliaHandlerEvent::QueryError { .. } => ("query_error_from_handler", Error),
169
170 };
171
172 self.with_metrics(|m| {
173 let counter = match &kind {
174 Request => &m.sent_requests,
175 Response => &m.sent_responses,
176 Error => &m.errors,
177 };
178
179 Self::inc_by_name(name, counter);
180 });
181 }
182
183 pub(super) fn sent(&self, event: &KademliaHandlerIn<QueryId>) {
184 use Kind::*;
185
186 let (name, kind) = match event {
187 KademliaHandlerIn::FindNodeReq { .. } => ("find_node", Request),
189 KademliaHandlerIn::GetProvidersReq { .. } => ("get_providers", Request),
190 KademliaHandlerIn::AddProvider { .. } => ("add_provider", Request),
191 KademliaHandlerIn::GetRecord { .. } => ("get_record", Request),
192 KademliaHandlerIn::PutRecord { .. } => ("put_record", Request),
193
194 KademliaHandlerIn::GetProvidersRes { .. } => ("get_providers", Request),
196 KademliaHandlerIn::GetRecordRes { .. } => ("get_record", Request),
197 KademliaHandlerIn::PutRecordRes { .. } => ("put_record", Request),
198 KademliaHandlerIn::FindNodeRes { .. } => ("find_node", Request),
199
200 KademliaHandlerIn::Reset(_) => ("sent_reset", Request),
202 };
203
204 self.with_metrics(|m| {
205 let counter = match &kind {
206 Request => &m.received_requests,
207 Response => &m.received_responses,
208 Error => &m.errors,
209 };
210
211 Self::inc_by_name(name, counter);
212 });
213 }
214
215 pub(super) fn generated_event_name(event: &KademliaEvent) -> &str {
216 match event {
217 KademliaEvent::QueryResult { .. } => "query_result",
218 KademliaEvent::RoutingUpdated { .. } => "routing_updated",
219 KademliaEvent::UnroutablePeer { .. } => "unroutable_peer",
220 KademliaEvent::RoutablePeer { .. } => "routable_peer",
221 KademliaEvent::PendingRoutablePeer { .. } => "pending_routable_peer"
222 }
223 }
224
225 pub(super) fn polled_event(
226 &self,
227 event: &NetworkBehaviourAction<KademliaHandlerIn<QueryId>, KademliaEvent>,
228 ) {
229 let name = match event {
230 NetworkBehaviourAction::DialAddress { .. } => "dial_address",
231 NetworkBehaviourAction::DialPeer { .. } => "dial_peer",
232 NetworkBehaviourAction::ReportObservedAddr { .. } => "report_observed_addr",
233 NetworkBehaviourAction::GenerateEvent(e) => Self::generated_event_name(e),
234 NetworkBehaviourAction::NotifyHandler { event, .. } => {
235 self.sent(event);
236 return;
237 }
238 };
239
240 self.with_metrics(|m| Self::inc_by_name(name, &m.kademlia_events));
241 }
242
243 pub(super) fn store_put(&self) {
244 self.with_metrics(|m| m.records_stored.inc())
245 }
246
247 pub(super) fn record_removed(&self) {
248 self.with_metrics(|m| m.records_stored.dec())
249 }
250
251 pub(super) fn report_routing_table_size(&self, size: usize) {
252 self.with_metrics(|m| m.routing_table_size.set(size as i64))
253 }
254}