use std::cell::RefCell;
use std::time::Duration;
use crate::instruments::{
fundamentals::buckets::SecondsBuckets, AcceptAllLabels, Instrument, LabelFilter,
LabelPredicate, Update, Updates,
};
use crate::snapshot::Snapshot;
use crate::util;
use crate::{Descriptive, ObservedValue, PutsSnapshot, TimeUnit};
pub use gauge_adapter::*;
use tracking::*;
mod gauge_adapter;
mod tracking;
pub struct Gauge {
name: String,
title: Option<String>,
description: Option<String>,
value: Option<i64>,
tracking: Option<RefCell<SecondsBuckets<Bucket>>>,
display_time_unit: TimeUnit,
}
impl Gauge {
pub fn new<T: Into<String>>(name: T) -> Gauge {
Gauge {
name: name.into(),
title: None,
description: None,
value: None,
tracking: None,
display_time_unit: TimeUnit::default(),
}
}
pub fn new_with_defaults<T: Into<String>>(name: T) -> Gauge {
Self::new(name)
}
pub fn get_name(&self) -> &str {
&self.name
}
pub fn set_name<T: Into<String>>(&mut self, name: T) {
self.name = name.into();
}
pub fn name<T: Into<String>>(mut self, name: T) -> Self {
self.set_name(name);
self
}
pub fn set_title<T: Into<String>>(&mut self, title: T) {
self.title = Some(title.into())
}
pub fn title<T: Into<String>>(mut self, title: T) -> Self {
self.set_title(title);
self
}
pub fn set_description<T: Into<String>>(&mut self, description: T) {
self.description = Some(description.into())
}
pub fn description<T: Into<String>>(mut self, description: T) -> Self {
self.set_description(description);
self
}
#[deprecated(since = "0.10.5", note = "use method `set_tracking`")]
pub fn set_memorize_extrema(&mut self, d: Duration) {
self.set_tracking(std::cmp::max(1, d.as_secs() as usize));
}
#[deprecated(since = "0.10.5", note = "use method `tracking`")]
pub fn memorize_extrema(mut self, d: Duration) -> Self {
self.set_tracking(std::cmp::max(1, d.as_secs() as usize));
self
}
pub fn tracking(mut self, for_seconds: usize) -> Self {
self.set_tracking(for_seconds);
self
}
pub fn set_tracking(&mut self, for_seconds: usize) {
if for_seconds != 0 {
self.tracking = Some(RefCell::new(SecondsBuckets::new(for_seconds)))
}
}
pub fn set_display_time_unit(&mut self, display_time_unit: TimeUnit) {
self.display_time_unit = display_time_unit
}
pub fn display_time_unit(mut self, display_time_unit: TimeUnit) -> Self {
self.set_display_time_unit(display_time_unit);
self
}
pub fn accept<L: Eq + Send + 'static, F: Into<LabelFilter<L>>>(
self,
accept: F,
) -> GaugeAdapter<L> {
GaugeAdapter::accept(accept, self)
}
pub fn for_label<L: Eq + Send + 'static>(self, label: L) -> GaugeAdapter<L> {
self.accept(label)
}
pub fn for_labels<L: Eq + Send + 'static>(self, labels: Vec<L>) -> GaugeAdapter<L> {
self.accept(labels)
}
pub fn for_all_labels<L: Eq + Send + 'static>(self) -> GaugeAdapter<L> {
self.accept(AcceptAllLabels)
}
pub fn for_labels_by_predicate<L, P>(self, label_predicate: P) -> GaugeAdapter<L>
where
L: Eq + Send + 'static,
P: Fn(&L) -> bool + Send + 'static,
{
self.accept(LabelPredicate(label_predicate))
}
pub fn adapter<L: Eq + Send + 'static>(self) -> GaugeAdapter<L> {
GaugeAdapter::deaf(self)
}
pub fn deltas_only<L: Eq + Send + 'static, F: Into<LabelFilter<L>>>(
self,
accept: F,
) -> GaugeAdapter<L> {
GaugeAdapter::deltas_only(accept, self)
}
pub fn for_label_deltas_only<L: Eq + Send + 'static>(self, label: L) -> GaugeAdapter<L> {
self.deltas_only(label)
}
pub fn for_labels_deltas_only<L: Eq + Send + 'static>(self, labels: Vec<L>) -> GaugeAdapter<L> {
self.deltas_only(labels)
}
pub fn for_labels_deltas_only_by_predicate<L, P>(self, label_predicate: P) -> GaugeAdapter<L>
where
L: Eq + Send + 'static,
P: Fn(&L) -> bool + Send + 'static,
{
self.deltas_only(LabelPredicate(label_predicate))
}
pub fn inc_dec_on<
L: Eq + Send + 'static,
INCR: Into<LabelFilter<L>>,
DECR: Into<LabelFilter<L>>,
>(
self,
accept_incr: INCR,
accept_decr: DECR,
) -> GaugeAdapter<L> {
GaugeAdapter::inc_dec_on(accept_incr, accept_decr, self)
}
pub fn inc_dec_on_many<L: Eq + Send + 'static>(
self,
increment_on: Vec<L>,
decrement_on: Vec<L>,
) -> GaugeAdapter<L> {
self.inc_dec_on(increment_on, decrement_on)
}
pub fn inc_dec_by_predicates<L, PINC, PDEC>(
self,
predicate_incr: PINC,
predicate_decr: PDEC,
) -> GaugeAdapter<L>
where
L: Eq + Send + 'static,
PINC: Fn(&L) -> bool + Send + 'static,
PDEC: Fn(&L) -> bool + Send + 'static,
{
self.inc_dec_on(
LabelPredicate(predicate_incr),
LabelPredicate(predicate_decr),
)
}
pub fn set(&mut self, observed: ObservedValue) {
if let Some(value) = self.value.take() {
let next_value = if let Some(next_value) = self.next_value(Some(value), observed) {
if let Some(ref buckets) = self.tracking {
match buckets.try_borrow_mut() {
Ok(mut borrowed) => borrowed.current_mut().update(next_value),
Err(_err) => crate::util::log_error("borrow mut in gauge::set failed!"),
}
}
next_value
} else {
value
};
self.value = Some(next_value);
} else {
self.value = self.next_value(None, observed).map(|next_value| {
if let Some(ref buckets) = self.tracking {
match buckets.try_borrow_mut() {
Ok(mut borrowed) => borrowed.current_mut().update(next_value),
Err(_err) => crate::util::log_error("borrow mut in gauge::set failed!"),
}
}
next_value
});
}
}
pub fn get(&self) -> Option<i64> {
self.value
}
fn next_value(&self, current: Option<i64>, observed: ObservedValue) -> Option<i64> {
match observed {
ObservedValue::ChangedBy(d) => current.map(|c| c + d).or_else(|| Some(d)),
ObservedValue::Duration(time, unit) => {
let value = super::duration_to_display_value(time, unit, self.display_time_unit);
Some(value as i64)
}
x => x.convert_to_i64().or_else(|| current),
}
}
}
impl Instrument for Gauge {}
impl PutsSnapshot for Gauge {
fn put_snapshot(&self, into: &mut Snapshot, descriptive: bool) {
util::put_postfixed_descriptives(self, &self.name, into, descriptive);
if let Some(value) = self.value {
into.items.push((self.name.clone(), value.into()));
if let Some(ref buckets) = self.tracking {
match buckets.try_borrow_mut() {
Ok(mut borrowed) => BucketsStats::from_buckets(&mut *borrowed)
.into_iter()
.for_each(|stats| stats.add_to_snapshot(into, &self.name)),
Err(_err) => {
crate::util::log_error("borrow mut in gauge::put_snapshot failed!")
}
}
}
}
}
}
impl Updates for Gauge {
fn update(&mut self, with: &Update) -> usize {
match *with {
Update::ObservationWithValue(v, _) => {
self.set(v);
1
}
_ => 0,
}
}
}
impl Descriptive for Gauge {
fn title(&self) -> Option<&str> {
self.title.as_ref().map(|n| &**n)
}
fn description(&self) -> Option<&str> {
self.description.as_ref().map(|n| &**n)
}
}
#[cfg(test)]
mod test;