// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see .
//! Metrics that are collected from existing sources.
use prometheus::core::{Collector, Desc, Describer, Number, Opts};
use prometheus::proto;
use std::{cmp::Ordering, marker::PhantomData};
/// A counter whose values are obtained from an existing source.
///
/// > **Note*: The counter values provided by the source `S`
/// > must be monotonically increasing. Otherwise use a
/// > [`SourcedGauge`] instead.
pub type SourcedCounter = SourcedMetric;
/// A gauge whose values are obtained from an existing source.
pub type SourcedGauge = SourcedMetric;
/// The type of a sourced counter.
#[derive(Copy, Clone)]
pub enum Counter {}
/// The type of a sourced gauge.
#[derive(Copy, Clone)]
pub enum Gauge {}
/// A metric whose values are obtained from an existing source,
/// instead of being independently recorded.
#[derive(Debug, Clone)]
pub struct SourcedMetric {
source: S,
desc: Desc,
_type: PhantomData,
}
/// A source of values for a [`SourcedMetric`].
pub trait MetricSource: Sync + Send + Clone {
/// The type of the collected values.
type N: Number;
/// Collects the current values of the metrics from the source.
fn collect(&self, set: impl FnMut(&[&str], Self::N));
}
impl SourcedMetric {
/// Creates a new metric that obtains its values from the given source.
pub fn new(opts: &Opts, source: S) -> prometheus::Result {
let desc = opts.describe()?;
Ok(Self { source, desc, _type: PhantomData })
}
}
impl Collector for SourcedMetric {
fn desc(&self) -> Vec<&Desc> {
vec![&self.desc]
}
fn collect(&self) -> Vec {
let mut counters = Vec::new();
self.source.collect(|label_values, value| {
let mut m = proto::Metric::default();
match T::proto() {
proto::MetricType::COUNTER => {
let mut c = proto::Counter::default();
c.set_value(value.into_f64());
m.set_counter(c);
}
proto::MetricType::GAUGE => {
let mut g = proto::Gauge::default();
g.set_value(value.into_f64());
m.set_gauge(g);
}
t => {
log::error!("Unsupported sourced metric type: {:?}", t);
}
}
debug_assert_eq!(self.desc.variable_labels.len(), label_values.len());
match self.desc.variable_labels.len().cmp(&label_values.len()) {
Ordering::Greater =>
log::warn!("Missing label values for sourced metric {}", self.desc.fq_name),
Ordering::Less =>
log::warn!("Too many label values for sourced metric {}", self.desc.fq_name),
Ordering::Equal => {}
}
m.set_label(self.desc.variable_labels.iter().zip(label_values)
.map(|(l_name, l_value)| {
let mut l = proto::LabelPair::default();
l.set_name(l_name.to_string());
l.set_value(l_value.to_string());
l
})
.chain(self.desc.const_label_pairs.iter().cloned())
.collect::>());
counters.push(m);
});
let mut m = proto::MetricFamily::default();
m.set_name(self.desc.fq_name.clone());
m.set_help(self.desc.help.clone());
m.set_field_type(T::proto());
m.set_metric(counters);
vec![m]
}
}
/// Types of metrics that can obtain their values from an existing source.
pub trait SourcedType: private::Sealed + Sync + Send {
#[doc(hidden)]
fn proto() -> proto::MetricType;
}
impl SourcedType for Counter {
fn proto() -> proto::MetricType { proto::MetricType::COUNTER }
}
impl SourcedType for Gauge {
fn proto() -> proto::MetricType { proto::MetricType::GAUGE }
}
mod private {
pub trait Sealed {}
impl Sealed for super::Counter {}
impl Sealed for super::Gauge {}
}