1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
use crate::common::ValueSnapshot;
use metrics_core::{Key, Recorder, Snapshot as MetricsSnapshot};
use std::borrow::Cow;

/// A point-in-time view of metric data.
#[derive(Default, Debug)]
pub struct Snapshot {
    measurements: Vec<(String, ValueSnapshot)>,
}

impl Snapshot {
    pub(crate) fn from(from: Vec<(String, ValueSnapshot)>) -> Self {
        Snapshot { measurements: from }
    }
}

impl MetricsSnapshot for Snapshot {
    /// Records the snapshot to the given recorder.
    fn record<R: Recorder>(&self, recorder: &mut R) {
        for (key, snapshot) in &self.measurements {
            // TODO: switch this to Key::Owned once type_alias_enum_variants lands
            // in 1.37.0 (#61682)
            let owned_key: Key = Cow::Owned(key.clone());
            match snapshot {
                ValueSnapshot::Counter(value) => recorder.record_counter(owned_key.clone(), *value),
                ValueSnapshot::Gauge(value) => recorder.record_gauge(owned_key.clone(), *value),
                ValueSnapshot::Histogram(stream) => stream.decompress_with(|values| {
                    recorder.record_histogram(owned_key.clone(), values);
                }),
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{MetricsSnapshot, Recorder, Snapshot, ValueSnapshot};
    use metrics_core::Key;
    use metrics_util::StreamingIntegers;
    use std::collections::HashMap;

    #[derive(Default)]
    struct MockRecorder {
        counter: HashMap<String, u64>,
        gauge: HashMap<String, i64>,
        histogram: HashMap<String, Vec<u64>>,
    }

    impl MockRecorder {
        pub fn get_counter_value(&self, key: &String) -> Option<&u64> {
            self.counter.get(key)
        }

        pub fn get_gauge_value(&self, key: &String) -> Option<&i64> {
            self.gauge.get(key)
        }

        pub fn get_histogram_values(&self, key: &String) -> Option<&Vec<u64>> {
            self.histogram.get(key)
        }
    }

    impl Recorder for MockRecorder {
        fn record_counter<K: Into<Key>>(&mut self, key: K, value: u64) {
            let _ = self.counter.insert(key.into().to_string(), value);
        }

        fn record_gauge<K: Into<Key>>(&mut self, key: K, value: i64) {
            let _ = self.gauge.insert(key.into().to_string(), value);
        }

        fn record_histogram<K: Into<Key>>(&mut self, key: K, values: &[u64]) {
            let _ = self
                .histogram
                .insert(key.into().to_string(), values.to_vec());
        }
    }

    #[test]
    fn test_snapshot_recorder() {
        let key = "ok".to_owned();
        let mut measurements = Vec::new();
        measurements.push((key.clone(), ValueSnapshot::Counter(7)));
        measurements.push((key.clone(), ValueSnapshot::Gauge(42)));

        let hvalues = vec![10, 25, 42, 97];
        let mut stream = StreamingIntegers::new();
        stream.compress(&hvalues);
        measurements.push((key.clone(), ValueSnapshot::Histogram(stream)));

        let snapshot: Snapshot = Snapshot::from(measurements);

        let mut recorder = MockRecorder::default();
        snapshot.record(&mut recorder);

        assert_eq!(recorder.get_counter_value(&key), Some(&7));
        assert_eq!(recorder.get_gauge_value(&key), Some(&42));

        let hsum = recorder.get_histogram_values(&key).map(|x| x.iter().sum());
        assert_eq!(hsum, Some(174));
    }
}