libp2p_kad/
metrics.rs

1/*
2 * Copyright 2020 Fluence Labs Limited
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#![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    // Requests sent via NotifyHandler (KademliaHandlerIn)
35    sent_requests: IntCounterVec,
36    // Responses sent via NotifyHandler (KademliaHandlerIn)
37    sent_responses: IntCounterVec,
38    // Requests received via inject_event
39    received_requests: IntCounterVec,
40    // Responses received via inject_event
41    received_responses: IntCounterVec,
42    errors: IntCounterVec,
43    records_stored: IntGauge,
44    connected_nodes: IntGauge,
45    routing_table_size: IntGauge,
46    // Kademlia events (except NotifyHandler)
47    kademlia_events: IntCounterVec,
48    // TODO: nodes received in queries?
49}
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(); // TODO: better help?
76            opts
77        };
78
79        // Creates and registers counter in registry
80        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 requests = &["find_node", "get_providers", "add_provider", "get_record", "put_record"];
99        // let responses = &["find_node", "get_providers", "get_record", "put_record"];
100        // let errors = &["todo"]; // TODO: fill error types
101
102        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            // requests
154            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            // responses
161            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            // error
168            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            // requests
188            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            // responses
195            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            // error
201            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}