referrerpolicy=no-referrer-when-downgrade

relay_utils/
metrics.rs

1// Copyright 2019-2021 Parity Technologies (UK) Ltd.
2// This file is part of Parity Bridges Common.
3
4// Parity Bridges Common is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Parity Bridges Common is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Parity Bridges Common.  If not, see <http://www.gnu.org/licenses/>.
16
17pub use float_json_value::FloatJsonValueMetric;
18pub use global::GlobalMetrics;
19pub use prometheus_endpoint::{
20	prometheus::core::{Atomic, Collector},
21	register, Counter, CounterVec, Gauge, GaugeVec, Opts, PrometheusError, Registry, F64, I64, U64,
22};
23
24use async_std::sync::{Arc, RwLock};
25use async_trait::async_trait;
26use std::{fmt::Debug, time::Duration};
27
28mod float_json_value;
29mod global;
30
31/// Shared reference to `f64` value that is updated by the metric.
32pub type F64SharedRef = Arc<RwLock<Option<f64>>>;
33/// Int gauge metric type.
34pub type IntGauge = Gauge<U64>;
35
36/// Unparsed address that needs to be used to expose Prometheus metrics.
37#[derive(Debug, Clone)]
38pub struct MetricsAddress {
39	/// Serve HTTP requests at given host.
40	pub host: String,
41	/// Serve HTTP requests at given port.
42	pub port: u16,
43}
44
45/// Prometheus endpoint MetricsParams.
46#[derive(Debug, Clone)]
47pub struct MetricsParams {
48	/// Interface and TCP port to be used when exposing Prometheus metrics.
49	pub address: Option<MetricsAddress>,
50	/// Metrics registry. May be `Some(_)` if several components share the same endpoint.
51	pub registry: Registry,
52}
53
54/// Metric API.
55pub trait Metric: Clone + Send + Sync + 'static {
56	fn register(&self, registry: &Registry) -> Result<(), PrometheusError>;
57}
58
59/// Standalone metric API.
60///
61/// Metrics of this kind know how to update themselves, so we may just spawn and forget the
62/// asynchronous self-update task.
63#[async_trait]
64pub trait StandaloneMetric: Metric {
65	/// Update metric values.
66	async fn update(&self);
67
68	/// Metrics update interval.
69	fn update_interval(&self) -> Duration;
70
71	/// Register and spawn metric. Metric is only spawned if it is registered for the first time.
72	fn register_and_spawn(self, registry: &Registry) -> Result<(), PrometheusError> {
73		match self.register(registry) {
74			Ok(()) => {
75				self.spawn();
76				Ok(())
77			},
78			Err(PrometheusError::AlreadyReg) => Ok(()),
79			Err(e) => Err(e),
80		}
81	}
82
83	/// Spawn the self update task that will keep update metric value at given intervals.
84	fn spawn(self) {
85		async_std::task::spawn(async move {
86			let update_interval = self.update_interval();
87			loop {
88				self.update().await;
89				async_std::task::sleep(update_interval).await;
90			}
91		});
92	}
93}
94
95impl Default for MetricsAddress {
96	fn default() -> Self {
97		MetricsAddress { host: "127.0.0.1".into(), port: 9616 }
98	}
99}
100
101impl MetricsParams {
102	/// Creates metrics params from metrics address.
103	pub fn new(
104		address: Option<MetricsAddress>,
105		relay_version: String,
106		relay_commit: String,
107	) -> Result<Self, PrometheusError> {
108		const BUILD_INFO_METRIC: &str = "substrate_relay_build_info";
109
110		let registry = Registry::new();
111		register(
112			Gauge::<U64>::with_opts(
113				Opts::new(
114					BUILD_INFO_METRIC,
115					"A metric with a constant '1' value labeled by version",
116				)
117				.const_label("version", &relay_version)
118				.const_label("commit", &relay_commit),
119			)?,
120			&registry,
121		)?
122		.set(1);
123
124		log::info!(
125			target: "bridge",
126			"Exposed {} metric: version={} commit={}",
127			BUILD_INFO_METRIC,
128			relay_version,
129			relay_commit,
130		);
131
132		Ok(MetricsParams { address, registry })
133	}
134
135	/// Creates metrics params so that metrics are not exposed.
136	pub fn disabled() -> Self {
137		MetricsParams { address: None, registry: Registry::new() }
138	}
139
140	/// Do not expose metrics.
141	#[must_use]
142	pub fn disable(mut self) -> Self {
143		self.address = None;
144		self
145	}
146}
147
148/// Returns metric name optionally prefixed with given prefix.
149pub fn metric_name(prefix: Option<&str>, name: &str) -> String {
150	if let Some(prefix) = prefix {
151		format!("{prefix}_{name}")
152	} else {
153		name.into()
154	}
155}
156
157/// Set value of gauge metric.
158///
159/// If value is `Ok(None)` or `Err(_)`, metric would have default value.
160pub fn set_gauge_value<T: Default + Debug, V: Atomic<T = T>, E: Debug>(
161	gauge: &Gauge<V>,
162	value: Result<Option<T>, E>,
163) {
164	gauge.set(match value {
165		Ok(Some(value)) => {
166			log::trace!(
167				target: "bridge-metrics",
168				"Updated value of metric '{:?}': {:?}",
169				gauge.desc().first().map(|d| &d.fq_name),
170				value,
171			);
172			value
173		},
174		Ok(None) => {
175			log::warn!(
176				target: "bridge-metrics",
177				"Failed to update metric '{:?}': value is empty",
178				gauge.desc().first().map(|d| &d.fq_name),
179			);
180			Default::default()
181		},
182		Err(error) => {
183			log::warn!(
184				target: "bridge-metrics",
185				"Failed to update metric '{:?}': {:?}",
186				gauge.desc().first().map(|d| &d.fq_name),
187				error,
188			);
189			Default::default()
190		},
191	})
192}