use super::funejson::Value; use super::monitor_series_window::{samples_from_value, MetricSample}; /// How much a counter grew over the samples, Prometheus-style: a drop means /// the counter restarted from zero, so the value after it is all increase. No /// extrapolation to window edges, so the answer is a whole number. /// /// # Panics /// Panics on misordered samples or a negative counter value. pub fn counter_increase(samples: &[MetricSample]) -> CounterIncrease { let mut increase: i64 = 0; let mut resets: i64 = 0; for (i, s) in samples.iter().enumerate() { if i > 0 && s.at <= samples[i - 1].at { panic!("samples must be in strictly ascending time order: {} follows {}", s.at, samples[i - 1].at); } if s.value < 0 { panic!("counter value must not be negative: {} at {}", s.value, s.at); } if i == 0 { continue; } let prev = samples[i - 1].value; if s.value >= prev { increase += s.value - prev; } else { resets += 1; increase += s.value; } } if samples.is_empty() { return CounterIncrease { increase: 0, resets: 0, rate_per_second_milli: None, first_at: None, last_at: None }; } let first_at = samples[0].at; let last_at = samples[samples.len() - 1].at; let rate = if samples.len() >= 2 { // increase * 1000 can pass i64 for a large byte counter: i128. let n = increase as i128 * 1000; let d = (last_at - first_at) as i128; Some(((2 * n + d) / (2 * d)) as i64) } else { None }; CounterIncrease { increase, resets, rate_per_second_milli: rate, first_at: Some(first_at), last_at: Some(last_at) } } fn opt(v: Option) -> Value { match v { Some(i) => Value::Int(i), None => Value::Null, } } pub fn counter_increase_to_value(c: &CounterIncrease) -> Value { Value::obj(vec![ ("increase", Value::Int(c.increase)), ("resets", Value::Int(c.resets)), ("ratePerSecondMilli", opt(c.rate_per_second_milli)), ("firstAt", opt(c.first_at)), ("lastAt", opt(c.last_at)), ]) } pub fn fune_vector(args: &[Value]) -> Value { counter_increase_to_value(&counter_increase(&samples_from_value(&args[0]))) }