pub use float_json_value::FloatJsonValueMetric;
pub use global::GlobalMetrics;
pub use prometheus_endpoint::{
prometheus::core::{Atomic, Collector},
register, Counter, CounterVec, Gauge, GaugeVec, Opts, PrometheusError, Registry, F64, I64, U64,
};
use async_std::sync::{Arc, RwLock};
use async_trait::async_trait;
use std::{fmt::Debug, time::Duration};
mod float_json_value;
mod global;
pub type F64SharedRef = Arc<RwLock<Option<f64>>>;
pub type IntGauge = Gauge<U64>;
#[derive(Debug, Clone)]
pub struct MetricsAddress {
pub host: String,
pub port: u16,
}
#[derive(Debug, Clone)]
pub struct MetricsParams {
pub address: Option<MetricsAddress>,
pub registry: Registry,
}
pub trait Metric: Clone + Send + Sync + 'static {
fn register(&self, registry: &Registry) -> Result<(), PrometheusError>;
}
#[async_trait]
pub trait StandaloneMetric: Metric {
async fn update(&self);
fn update_interval(&self) -> Duration;
fn register_and_spawn(self, registry: &Registry) -> Result<(), PrometheusError> {
match self.register(registry) {
Ok(()) => {
self.spawn();
Ok(())
},
Err(PrometheusError::AlreadyReg) => Ok(()),
Err(e) => Err(e),
}
}
fn spawn(self) {
async_std::task::spawn(async move {
let update_interval = self.update_interval();
loop {
self.update().await;
async_std::task::sleep(update_interval).await;
}
});
}
}
impl Default for MetricsAddress {
fn default() -> Self {
MetricsAddress { host: "127.0.0.1".into(), port: 9616 }
}
}
impl MetricsParams {
pub fn new(
address: Option<MetricsAddress>,
relay_version: String,
relay_commit: String,
) -> Result<Self, PrometheusError> {
const BUILD_INFO_METRIC: &str = "substrate_relay_build_info";
let registry = Registry::new();
register(
Gauge::<U64>::with_opts(
Opts::new(
BUILD_INFO_METRIC,
"A metric with a constant '1' value labeled by version",
)
.const_label("version", &relay_version)
.const_label("commit", &relay_commit),
)?,
®istry,
)?
.set(1);
log::info!(
target: "bridge",
"Exposed {} metric: version={} commit={}",
BUILD_INFO_METRIC,
relay_version,
relay_commit,
);
Ok(MetricsParams { address, registry })
}
pub fn disabled() -> Self {
MetricsParams { address: None, registry: Registry::new() }
}
#[must_use]
pub fn disable(mut self) -> Self {
self.address = None;
self
}
}
pub fn metric_name(prefix: Option<&str>, name: &str) -> String {
if let Some(prefix) = prefix {
format!("{prefix}_{name}")
} else {
name.into()
}
}
pub fn set_gauge_value<T: Default + Debug, V: Atomic<T = T>, E: Debug>(
gauge: &Gauge<V>,
value: Result<Option<T>, E>,
) {
gauge.set(match value {
Ok(Some(value)) => {
log::trace!(
target: "bridge-metrics",
"Updated value of metric '{:?}': {:?}",
gauge.desc().first().map(|d| &d.fq_name),
value,
);
value
},
Ok(None) => {
log::warn!(
target: "bridge-metrics",
"Failed to update metric '{:?}': value is empty",
gauge.desc().first().map(|d| &d.fq_name),
);
Default::default()
},
Err(error) => {
log::warn!(
target: "bridge-metrics",
"Failed to update metric '{:?}': {:?}",
gauge.desc().first().map(|d| &d.fq_name),
error,
);
Default::default()
},
})
}