Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion crates/charon-p2p/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,20 @@ license.workspace = true
publish.workspace = true

[dependencies]
chrono.workspace = true
libp2p.workspace = true
thiserror.workspace = true
k256.workspace = true
charon-eth2.workspace = true
charon-k1util.workspace = true
chrono.workspace = true
vise.workspace = true
tokio.workspace = true
rand.workspace = true

[dev-dependencies]
charon-testutil.workspace = true
tempfile.workspace = true
vise-exporter.workspace = true

[lints]
workspace = true
58 changes: 58 additions & 0 deletions crates/charon-p2p/examples/metrics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//! Example demonstrating the charon-p2p metrics functionality.
//!
//! To run this example, run the local Prometheus and Grafana containers:
//! ```bash
//! docker compose -f test-infra/docker-compose.yml up -d
//! ```
//!
//! Then run the example:
//! ```bash
//! cargo run --example metrics -p charon-p2p
//! ```
//!
//! Metrics will be available in Grafana at http://localhost:3000.

use std::net::SocketAddr;

use charon_p2p::metrics::{
ConnectionType, Direction, P2P_METRICS, PeerConnectionLabels, PeerNetworkLabels,
PeerStreamLabels, Protocol, RelayConnectionLabels,
};
use vise_exporter::MetricsExporter;

#[tokio::main]
async fn main() {
let bind_address = SocketAddr::from(([0, 0, 0, 0], 9464));

let exporter = MetricsExporter::default()
.bind(bind_address)
.await
.expect("Failed to bind metrics exporter");
tokio::spawn(async move {
exporter
.start()
.await
.expect("Failed to start metrics exporter");
});

P2P_METRICS.ping_latency_secs["rust"].observe(1.0);
P2P_METRICS.ping_error_total["rust"].inc();
P2P_METRICS.ping_success["rust"].set(1);
P2P_METRICS.reachability_status.set(1);
P2P_METRICS.relay_connections["rust"].set(1);
P2P_METRICS.peer_connection_types
[&PeerConnectionLabels::new("rust", ConnectionType::Direct, Protocol::Tcp)]
.set(1);
P2P_METRICS.relay_connection_types
[&RelayConnectionLabels::new("rust", ConnectionType::Direct, Protocol::Tcp)]
.set(1);
P2P_METRICS.peer_streams[&PeerStreamLabels::new("rust", Direction::Inbound, Protocol::Tcp)]
.set(1);
P2P_METRICS.peer_connection_total["rust"].inc();
P2P_METRICS.peer_network_receive_bytes_total[&PeerNetworkLabels::new("rust", Protocol::Tcp)]
.inc();
P2P_METRICS.peer_network_sent_bytes_total[&PeerNetworkLabels::new("rust", Protocol::Tcp)].inc();

// Wait for 20 seconds to see the logs in Loki
std::thread::sleep(std::time::Duration::from_secs(20));
}
3 changes: 3 additions & 0 deletions crates/charon-p2p/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,8 @@ pub mod name;
/// P2P configuration.
pub mod config;

/// Metrics.
pub mod metrics;

/// K1 utilities.
pub mod k1;
149 changes: 149 additions & 0 deletions crates/charon-p2p/src/metrics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
use vise::*;

const BUCKETS: [f64; 11] = [
0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0,
];

/// Metrics for the P2P layer.
#[derive(Debug, Metrics)]
#[metrics(prefix = "p2p")]
pub struct P2PMetrics {
/// Ping latencies in seconds per peer
#[metrics(buckets = &BUCKETS, labels = ["peer"])]
pub ping_latency_secs: LabeledFamily<String, Histogram>,

/// Total number of ping errors per peer
#[metrics(labels = ["peer"])]
pub ping_error_total: LabeledFamily<String, Counter>,

/// Whether the last ping was successful (1) or not (0). Can be used as
/// proxy for connected peers
#[metrics(labels = ["peer"])]
pub ping_success: LabeledFamily<String, Gauge>,

/// Current libp2p reachability status of this node as detected by autonat:
/// unknown(0), public(1) or private(2).
pub reachability_status: Gauge,

/// Connected relays by name
#[metrics(labels = ["peer"])]
pub relay_connections: LabeledFamily<String, Gauge>,

/// Current number of libp2p connections by peer, type (`direct` or
/// `relay`), and protocol (`tcp`, `quic`). Note that peers may have
/// multiple connections.
pub peer_connection_types: Family<PeerConnectionLabels, Gauge>,

/// Current number of libp2p connections by relay, type (`direct` or
/// `relay`), and protocol (`tcp`, `quic`). Note that peers may have
/// multiple connections.
pub relay_connection_types: Family<RelayConnectionLabels, Gauge>,

/// Current number of libp2p streams by peer, direction ('inbound' or
/// 'outbound' or 'unknown') and protocol.
pub peer_streams: Family<PeerStreamLabels, Gauge>,

/// Total number of libp2p connections per peer.
#[metrics(labels = ["peer"])]
pub peer_connection_total: LabeledFamily<String, Counter>,

/// Total number of network bytes received from the peer by protocol.
pub peer_network_receive_bytes_total: Family<PeerNetworkLabels, Counter>,

/// Total number of network bytes sent to the peer by protocol.
pub peer_network_sent_bytes_total: Family<PeerNetworkLabels, Counter>,
}

/// The type of connection.
#[derive(Debug, Clone, PartialEq, Eq, Hash, EncodeLabelValue)]
#[metrics(rename_all = "snake_case")]
pub enum ConnectionType {
/// A direct connection to a peer.
Direct,
/// A connection to a relay.
Relay,
}

/// The direction of a connection.
#[derive(Debug, Clone, PartialEq, Eq, Hash, EncodeLabelValue)]
#[metrics(rename_all = "snake_case")]
pub enum Direction {
/// An inbound connection.
Inbound,
/// An outbound connection.
Outbound,
/// An unknown connection.
Unknown,
}

/// The protocol of a connection.
#[derive(Debug, Clone, PartialEq, Eq, Hash, EncodeLabelValue)]
#[metrics(rename_all = "snake_case")]
pub enum Protocol {
/// A TCP connection.
Tcp,
/// A QUIC connection.
Quic,
}

/// Labels for peer connections.
#[derive(Debug, Clone, PartialEq, Eq, Hash, EncodeLabelSet)]
pub struct PeerConnectionLabels {
peer: String,
r#type: ConnectionType,
protocol: Protocol,
}

impl PeerConnectionLabels {
/// Creates a new peer connection labels.
pub fn new(peer: &str, r#type: ConnectionType, protocol: Protocol) -> Self {
Self {
peer: peer.to_string(),
r#type,
protocol,
}
}
}

/// Relay connection labels
pub type RelayConnectionLabels = PeerConnectionLabels;

/// Labels for peer streams.
#[derive(Debug, Clone, PartialEq, Eq, Hash, EncodeLabelSet)]
pub struct PeerStreamLabels {
peer: String,
direction: Direction,
protocol: Protocol,
}

impl PeerStreamLabels {
/// Creates a new peer stream labels.
pub fn new(peer: &str, direction: Direction, protocol: Protocol) -> Self {
Self {
peer: peer.to_string(),
direction,
protocol,
}
}
}

/// Labels for peer network.
#[derive(Debug, Clone, PartialEq, Eq, Hash, EncodeLabelSet)]
pub struct PeerNetworkLabels {
peer: String,
protocol: Protocol,
}

impl PeerNetworkLabels {
/// Creates a new peer network labels.
pub fn new(peer: &str, protocol: Protocol) -> Self {
Self {
peer: peer.to_string(),
protocol,
}
}
}

/// Global metrics for the P2P layer.
#[vise::register]
pub static P2P_METRICS: Global<P2PMetrics> = Global::new();
File renamed without changes.