blob: e3337b8137a524ae64c0bff5a05ba5aa84988efa [file] [log] [blame] [edit]
// Copyright 2014 The Prometheus Authors
// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
use std::cmp::{Eq, Ord, Ordering, PartialOrd};
use std::collections::HashMap;
use crate::desc::{Desc, Describer};
use crate::errors::Result;
use crate::proto::{self, LabelPair};
use crate::timer;
use std::cell::Cell;
pub const SEPARATOR_BYTE: u8 = 0xFF;
/// An interface for collecting metrics.
pub trait Collector: Sync + Send {
/// Return descriptors for metrics.
fn desc(&self) -> Vec<&Desc>;
/// Collect metrics.
fn collect(&self) -> Vec<proto::MetricFamily>;
}
/// An interface models a single sample value with its meta data being exported to Prometheus.
pub trait Metric: Sync + Send + Clone {
/// Return the protocol Metric.
fn metric(&self) -> proto::Metric;
}
/// An interface models a Metric only usable in single thread environment.
pub trait LocalMetric {
/// Flush the local metrics to the global one.
fn flush(&self);
}
/// An interface models a LocalMetric with try to flush functions.
/// Not intend to be implemented by user manually, used in macro generated code.
pub trait MayFlush: LocalMetric {
/// If the LocalMetric is already flushed in last `flush_interval_sec` seconds, then do nothing,
/// else flush and update last flush time.
fn try_flush(&self, last_flush: &Cell<u64>, flush_interval_millis: u64) {
let now = timer::recent_millis();
let last_tick = last_flush.get();
if now < last_tick + flush_interval_millis {
return;
}
self.flush();
last_flush.set(now);
}
/// Open to implementation to fill try_flush parameters
fn may_flush(&self);
}
/// A struct that bundles the options for creating most [`Metric`] types.
#[derive(Debug, Clone)]
pub struct Opts {
/// namespace, subsystem, and name are components of the fully-qualified
/// name of the [`Metric`] (created by joining these components with
/// "_"). Only Name is mandatory, the others merely help structuring the
/// name. Note that the fully-qualified name of the metric must be a
/// valid Prometheus metric name.
pub namespace: String,
/// namespace, subsystem, and name are components of the fully-qualified
/// name of the [`Metric`] (created by joining these components with
/// "_"). Only Name is mandatory, the others merely help structuring the
/// name. Note that the fully-qualified name of the metric must be a
/// valid Prometheus metric name.
pub subsystem: String,
/// namespace, subsystem, and name are components of the fully-qualified
/// name of the [`Metric`] (created by joining these components with
/// "_"). Only Name is mandatory, the others merely help structuring the
/// name. Note that the fully-qualified name of the metric must be a
/// valid Prometheus metric name.
pub name: String,
/// help provides information about this metric. Mandatory!
///
/// Metrics with the same fully-qualified name must have the same Help
/// string.
pub help: String,
/// const_labels are used to attach fixed labels to this metric. Metrics
/// with the same fully-qualified name must have the same label names in
/// their ConstLabels.
///
/// Note that in most cases, labels have a value that varies during the
/// lifetime of a process. Those labels are usually managed with a metric
/// vector collector (like CounterVec, GaugeVec). ConstLabels
/// serve only special purposes. One is for the special case where the
/// value of a label does not change during the lifetime of a process,
/// e.g. if the revision of the running binary is put into a
/// label. Another, more advanced purpose is if more than one [`Collector`]
/// needs to collect Metrics with the same fully-qualified name. In that
/// case, those Metrics must differ in the values of their
/// ConstLabels. See the [`Collector`] examples.
///
/// If the value of a label never changes (not even between binaries),
/// that label most likely should not be a label at all (but part of the
/// metric name).
pub const_labels: HashMap<String, String>,
/// variable_labels contains names of labels for which the metric maintains
/// variable values. Metrics with the same fully-qualified name must have
/// the same label names in their variable_labels.
///
/// Note that variable_labels is used in `MetricVec`. To create a single
/// metric must leave it empty.
pub variable_labels: Vec<String>,
}
impl Opts {
/// `new` creates the Opts with the `name` and `help` arguments.
pub fn new<S1: Into<String>, S2: Into<String>>(name: S1, help: S2) -> Opts {
Opts {
namespace: "".to_owned(),
subsystem: "".to_owned(),
name: name.into(),
help: help.into(),
const_labels: HashMap::new(),
variable_labels: Vec::new(),
}
}
/// `namespace` sets the namespace.
pub fn namespace<S: Into<String>>(mut self, namespace: S) -> Self {
self.namespace = namespace.into();
self
}
/// `subsystem` sets the sub system.
pub fn subsystem<S: Into<String>>(mut self, subsystem: S) -> Self {
self.subsystem = subsystem.into();
self
}
/// `const_labels` sets the const labels.
pub fn const_labels(mut self, const_labels: HashMap<String, String>) -> Self {
self.const_labels = const_labels;
self
}
/// `const_label` adds a const label.
pub fn const_label<S1: Into<String>, S2: Into<String>>(mut self, name: S1, value: S2) -> Self {
self.const_labels.insert(name.into(), value.into());
self
}
/// `variable_labels` sets the variable labels.
pub fn variable_labels(mut self, variable_labels: Vec<String>) -> Self {
self.variable_labels = variable_labels;
self
}
/// `variable_label` adds a variable label.
pub fn variable_label<S: Into<String>>(mut self, name: S) -> Self {
self.variable_labels.push(name.into());
self
}
/// `fq_name` returns the fq_name.
pub fn fq_name(&self) -> String {
build_fq_name(&self.namespace, &self.subsystem, &self.name)
}
}
impl Describer for Opts {
fn describe(&self) -> Result<Desc> {
Desc::new(
self.fq_name(),
self.help.clone(),
self.variable_labels.clone(),
self.const_labels.clone(),
)
}
}
impl Ord for LabelPair {
fn cmp(&self, other: &LabelPair) -> Ordering {
self.get_name().cmp(other.get_name())
}
}
impl Eq for LabelPair {}
impl PartialOrd for LabelPair {
fn partial_cmp(&self, other: &LabelPair) -> Option<Ordering> {
Some(self.cmp(other))
}
}
/// `build_fq_name` joins the given three name components by "_". Empty name
/// components are ignored. If the name parameter itself is empty, an empty
/// string is returned, no matter what. [`Metric`] implementations included in this
/// library use this function internally to generate the fully-qualified metric
/// name from the name component in their Opts. Users of the library will only
/// need this function if they implement their own [`Metric`] or instantiate a Desc
/// directly.
fn build_fq_name(namespace: &str, subsystem: &str, name: &str) -> String {
if name.is_empty() {
return "".to_owned();
}
if !namespace.is_empty() && !subsystem.is_empty() {
return format!("{}_{}_{}", namespace, subsystem, name);
} else if !namespace.is_empty() {
return format!("{}_{}", namespace, name);
} else if !subsystem.is_empty() {
return format!("{}_{}", subsystem, name);
}
name.to_owned()
}
#[cfg(test)]
mod tests {
use std::cmp::{Ord, Ordering};
use super::*;
use crate::proto::LabelPair;
fn new_label_pair(name: &str, value: &str) -> LabelPair {
let mut l = LabelPair::default();
l.set_name(name.to_owned());
l.set_value(value.to_owned());
l
}
#[test]
fn test_label_cmp() {
let tbl = vec![
("k1", "k2", Ordering::Less),
("k1", "k1", Ordering::Equal),
("k1", "k0", Ordering::Greater),
];
for (l1, l2, order) in tbl {
let lhs = new_label_pair(l1, l1);
let rhs = new_label_pair(l2, l2);
assert_eq!(lhs.cmp(&rhs), order);
}
}
#[test]
fn test_build_fq_name() {
let tbl = vec![
("a", "b", "c", "a_b_c"),
("", "b", "c", "b_c"),
("a", "", "c", "a_c"),
("", "", "c", "c"),
("a", "b", "", ""),
("a", "", "", ""),
("", "b", "", ""),
(" ", "", "", ""),
];
for (namespace, subsystem, name, res) in tbl {
assert_eq!(&build_fq_name(namespace, subsystem, name), res);
}
}
#[test]
fn test_different_generic_types() {
Opts::new(format!("{}_{}", "string", "label"), "&str_label");
}
}