Skip to content

Commit a655969

Browse files
committed
feat: expose prometheus metrics
1 parent cbf0baf commit a655969

File tree

8 files changed

+1265
-1759
lines changed

8 files changed

+1265
-1759
lines changed

Cargo.lock

Lines changed: 1162 additions & 1748 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,24 @@ members = [".", "examples/dcutr", "examples/chat", "examples/autonat"]
33

44
[package]
55
name = "boot-node"
6-
version = "0.5.2"
6+
version = "0.6.0"
77
authors = ["Calimero Limited <[email protected]>"]
88
edition = "2021"
99
repository = "https://github.com/calimero-network/boot-node"
1010
license = "MIT OR Apache-2.0"
1111

1212
[dependencies]
13+
axum = "0.7"
1314
camino = "1.1.6"
1415
clap = { version = "4.5.4", features = ["derive", "env"] }
1516
eyre = "0.6.12"
1617
futures-util = "0.3.30"
17-
libp2p = { version = "0.53.2", features = [
18+
libp2p = { version = "0.56.0", features = [
1819
"autonat",
1920
"identify",
2021
"kad",
2122
"macros",
23+
"metrics",
2224
"noise",
2325
"ping",
2426
"quic",
@@ -29,7 +31,9 @@ libp2p = { version = "0.53.2", features = [
2931
"tls",
3032
"yamux",
3133
] }
34+
libp2p-metrics = "0.17.0"
3235
multiaddr = "0.18.1"
36+
prometheus-client = "0.23"
3337
serde = "1.0.196"
3438
serde_json = "1.0.113"
3539
tokio = { version = "1.35.1", features = ["macros", "rt", "rt-multi-thread"] }

examples/autonat/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ version = "0.1.0"
44
edition = "2021"
55

66
[dependencies]
7-
libp2p = { version = "0.55.0", features = [
7+
libp2p = { version = "0.56.0", features = [
88
"dns",
99
"rsa",
1010
"rendezvous",

examples/chat/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ bytes = "1.6.0"
1111
clap = { version = "4.5.4", features = ["derive", "env"] }
1212
eyre = "0.6.12"
1313
futures-util = { version = "0.3.30" }
14-
libp2p = { version = "0.53.2", features = [
14+
libp2p = { version = "0.56.0", features = [
1515
"dcutr",
1616
"dns",
1717
"gossipsub",

examples/dcutr/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ license = "MIT OR Apache-2.0"
1010
camino = "1.1.6"
1111
clap = { version = "4.5.4", features = ["derive", "env"] }
1212
eyre = "0.6.12"
13-
libp2p = { version = "0.53.2", features = [
13+
libp2p = { version = "0.56.0", features = [
1414
"dcutr",
1515
"dns",
1616
"identify",

rust-toolchain.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[toolchain]
2+
channel = "1.89.0"
3+
components = ["cargo", "clippy"]
4+
5+
[profile.dev]
6+
opt-level = 0
7+
8+
[profile.release]
9+
opt-level = 3

src/http_service.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use std::{
2+
net::SocketAddr,
3+
sync::{Arc, Mutex},
4+
};
5+
6+
use axum::{extract::State, http::StatusCode, response::IntoResponse, routing::get, Router};
7+
use prometheus_client::{encoding::text::encode, registry::Registry};
8+
use tokio::net::TcpListener;
9+
10+
const METRICS_CONTENT_TYPE: &str = "application/openmetrics-text;charset=utf-8;version=1.0.0";
11+
12+
pub(crate) async fn metrics_server(registry: Registry) -> Result<(), std::io::Error> {
13+
// Serve on localhost.
14+
let addr: SocketAddr = ([127, 0, 0, 1], 0).into();
15+
let service = MetricService::new(registry);
16+
let server = Router::new()
17+
.route("/metrics", get(respond_with_metrics))
18+
.with_state(service);
19+
let tcp_listener = TcpListener::bind(addr).await?;
20+
let local_addr = tcp_listener.local_addr()?;
21+
tracing::info!(metrics_server=%format!("http://{}/metrics", local_addr));
22+
axum::serve(tcp_listener, server.into_make_service()).await?;
23+
Ok(())
24+
}
25+
26+
#[derive(Clone)]
27+
pub(crate) struct MetricService {
28+
reg: Arc<Mutex<Registry>>,
29+
}
30+
31+
async fn respond_with_metrics(state: State<MetricService>) -> impl IntoResponse {
32+
let mut sink = String::new();
33+
let reg = state.get_reg();
34+
encode(&mut sink, &reg.lock().unwrap()).unwrap();
35+
36+
(
37+
StatusCode::OK,
38+
[(axum::http::header::CONTENT_TYPE, METRICS_CONTENT_TYPE)],
39+
sink,
40+
)
41+
}
42+
43+
type SharedRegistry = Arc<Mutex<Registry>>;
44+
45+
impl MetricService {
46+
fn new(registry: Registry) -> Self {
47+
Self {
48+
reg: Arc::new(Mutex::new(registry)),
49+
}
50+
}
51+
52+
fn get_reg(&self) -> SharedRegistry {
53+
Arc::clone(&self.reg)
54+
}
55+
}

src/main.rs

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@ use libp2p::swarm::{NetworkBehaviour, SwarmEvent};
66
use libp2p::{
77
autonat, identify, identity, kad, ping, relay, rendezvous, Multiaddr, StreamProtocol, Swarm,
88
};
9+
use libp2p_metrics::{Metrics, Recorder, Registry};
910
use tracing::info;
1011
use tracing_subscriber::prelude::*;
1112
use tracing_subscriber::EnvFilter;
1213

14+
mod http_service;
15+
1316
const PROTOCOL_VERSION: &str = concat!("/", env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
1417
const CALIMERO_KAD_PROTO_NAME: StreamProtocol = StreamProtocol::new("/calimero/kad/1.0.0");
1518
const MAX_RELAY_CIRCUIT_BYTES: u64 = 100 << 20; // 100 MiB
@@ -56,6 +59,8 @@ async fn main() -> eyre::Result<()> {
5659

5760
info!("Peer id: {:?}", peer_id);
5861

62+
let mut metric_registry = Registry::default();
63+
5964
let mut swarm = libp2p::SwarmBuilder::with_existing_identity(keypair)
6065
.with_tokio()
6166
.with_tcp(
@@ -64,15 +69,15 @@ async fn main() -> eyre::Result<()> {
6469
libp2p::yamux::Config::default,
6570
)?
6671
.with_quic()
72+
.with_bandwidth_metrics(&mut metric_registry)
6773
.with_behaviour(|keypair| Behaviour {
6874
autonat: autonat::Behaviour::new(peer_id.clone(), Default::default()),
6975
identify: identify::Behaviour::new(identify::Config::new(
7076
PROTOCOL_VERSION.to_owned(),
7177
keypair.public(),
7278
)),
7379
kad: {
74-
let mut kademlia_config = kad::Config::default();
75-
kademlia_config.set_protocol_names(vec![CALIMERO_KAD_PROTO_NAME]);
80+
let mut kademlia_config = kad::Config::new(CALIMERO_KAD_PROTO_NAME);
7681
// Instantly remove records and provider records.
7782
// TODO: figure out what to do with these values, ref: https://github.com/libp2p/rust-libp2p/blob/1aa016e1c7e3976748a726eab37af44d1c5b7a6e/misc/server/src/behaviour.rs#L38
7883
kademlia_config.set_record_ttl(Some(std::time::Duration::from_secs(0)));
@@ -116,16 +121,28 @@ async fn main() -> eyre::Result<()> {
116121
.with(multiaddr::Protocol::QuicV1);
117122
swarm.listen_on(listen_addr_quic)?;
118123

124+
let metrics = Metrics::new(&mut metric_registry);
125+
tokio::spawn(http_service::metrics_server(metric_registry));
126+
119127
loop {
120128
let event = swarm.next().await;
121-
handle_swarm_event(&mut swarm, event.expect("Swarm stream to be infinite.")).await;
129+
handle_swarm_event(
130+
&mut swarm,
131+
event.expect("Swarm stream to be infinite."),
132+
&metrics,
133+
)
134+
.await;
122135
}
123136
}
124137

125-
async fn handle_swarm_event(swarm: &mut Swarm<Behaviour>, event: SwarmEvent<BehaviourEvent>) {
138+
async fn handle_swarm_event(
139+
swarm: &mut Swarm<Behaviour>,
140+
event: SwarmEvent<BehaviourEvent>,
141+
metrics: &Metrics,
142+
) {
126143
match event {
127144
SwarmEvent::Behaviour(event) => {
128-
handle_swarm_behaviour_event(swarm, event).await;
145+
handle_swarm_behaviour_event(swarm, event, metrics).await;
129146
}
130147
SwarmEvent::NewListenAddr { address, .. } => {
131148
info!("Listening on {address:?}");
@@ -134,12 +151,17 @@ async fn handle_swarm_event(swarm: &mut Swarm<Behaviour>, event: SwarmEvent<Beha
134151
}
135152
}
136153

137-
async fn handle_swarm_behaviour_event(swarm: &mut Swarm<Behaviour>, event: BehaviourEvent) {
154+
async fn handle_swarm_behaviour_event(
155+
swarm: &mut Swarm<Behaviour>,
156+
event: BehaviourEvent,
157+
metrics: &Metrics,
158+
) {
138159
match event {
139160
BehaviourEvent::Autonat(event) => {
140161
info!("AutoNat event: {event:?}");
141162
}
142163
BehaviourEvent::Identify(event) => {
164+
metrics.record(&event);
143165
info!("Identify event: {event:?}");
144166
match event {
145167
identify::Event::Received {
@@ -153,9 +175,11 @@ async fn handle_swarm_behaviour_event(swarm: &mut Swarm<Behaviour>, event: Behav
153175
}
154176
}
155177
BehaviourEvent::Kad(event) => {
178+
metrics.record(&event);
156179
info!("Kad event: {event:?}");
157180
}
158181
BehaviourEvent::Relay(event) => {
182+
metrics.record(&event);
159183
info!("Relay event: {event:?}");
160184
}
161185
BehaviourEvent::Rendezvous(event) => {

0 commit comments

Comments
 (0)