use std::sync::Arc;
use metrics::{
Counter, CounterFn, Gauge, GaugeFn, Histogram, HistogramFn, Key, KeyName, Recorder, Unit,
};
struct FanoutCounter {
counters: Vec<Counter>,
}
impl FanoutCounter {
pub fn from_counters(counters: Vec<Counter>) -> Self {
Self { counters }
}
}
impl CounterFn for FanoutCounter {
fn increment(&self, value: u64) {
for counter in &self.counters {
counter.increment(value);
}
}
fn absolute(&self, value: u64) {
for counter in &self.counters {
counter.absolute(value);
}
}
}
impl From<FanoutCounter> for Counter {
fn from(counter: FanoutCounter) -> Counter {
Counter::from_arc(Arc::new(counter))
}
}
struct FanoutGauge {
gauges: Vec<Gauge>,
}
impl FanoutGauge {
pub fn from_gauges(gauges: Vec<Gauge>) -> Self {
Self { gauges }
}
}
impl GaugeFn for FanoutGauge {
fn increment(&self, value: f64) {
for gauge in &self.gauges {
gauge.increment(value);
}
}
fn decrement(&self, value: f64) {
for gauge in &self.gauges {
gauge.decrement(value);
}
}
fn set(&self, value: f64) {
for gauge in &self.gauges {
gauge.set(value);
}
}
}
impl From<FanoutGauge> for Gauge {
fn from(gauge: FanoutGauge) -> Gauge {
Gauge::from_arc(Arc::new(gauge))
}
}
struct FanoutHistogram {
histograms: Vec<Histogram>,
}
impl FanoutHistogram {
pub fn from_histograms(histograms: Vec<Histogram>) -> Self {
Self { histograms }
}
}
impl HistogramFn for FanoutHistogram {
fn record(&self, value: f64) {
for histogram in &self.histograms {
histogram.record(value);
}
}
}
impl From<FanoutHistogram> for Histogram {
fn from(histogram: FanoutHistogram) -> Histogram {
Histogram::from_arc(Arc::new(histogram))
}
}
pub struct Fanout {
recorders: Vec<Box<dyn Recorder>>,
}
impl Recorder for Fanout {
fn describe_counter(&self, key_name: KeyName, unit: Option<Unit>, description: &'static str) {
for recorder in &self.recorders {
recorder.describe_counter(key_name.clone(), unit, description);
}
}
fn describe_gauge(&self, key_name: KeyName, unit: Option<Unit>, description: &'static str) {
for recorder in &self.recorders {
recorder.describe_gauge(key_name.clone(), unit, description);
}
}
fn describe_histogram(&self, key_name: KeyName, unit: Option<Unit>, description: &'static str) {
for recorder in &self.recorders {
recorder.describe_histogram(key_name.clone(), unit, description);
}
}
fn register_counter(&self, key: &Key) -> Counter {
let counters =
self.recorders.iter().map(|recorder| recorder.register_counter(key)).collect();
FanoutCounter::from_counters(counters).into()
}
fn register_gauge(&self, key: &Key) -> Gauge {
let gauges = self.recorders.iter().map(|recorder| recorder.register_gauge(key)).collect();
FanoutGauge::from_gauges(gauges).into()
}
fn register_histogram(&self, key: &Key) -> Histogram {
let histograms =
self.recorders.iter().map(|recorder| recorder.register_histogram(key)).collect();
FanoutHistogram::from_histograms(histograms).into()
}
}
#[derive(Default)]
pub struct FanoutBuilder {
recorders: Vec<Box<dyn Recorder>>,
}
impl FanoutBuilder {
pub fn add_recorder<R>(mut self, recorder: R) -> FanoutBuilder
where
R: Recorder + 'static,
{
self.recorders.push(Box::new(recorder));
self
}
pub fn build(self) -> Fanout {
Fanout { recorders: self.recorders }
}
}
#[cfg(test)]
mod tests {
use super::FanoutBuilder;
use crate::test_util::*;
use metrics::{Counter, Gauge, Histogram, Unit};
#[test]
fn test_basic_functionality() {
let operations = vec![
RecorderOperation::DescribeCounter(
"counter_key".into(),
Some(Unit::Count),
"counter desc",
),
RecorderOperation::DescribeGauge("gauge_key".into(), Some(Unit::Bytes), "gauge desc"),
RecorderOperation::DescribeHistogram(
"histogram_key".into(),
Some(Unit::Nanoseconds),
"histogram desc",
),
RecorderOperation::RegisterCounter("counter_key".into(), Counter::noop()),
RecorderOperation::RegisterGauge("gauge_key".into(), Gauge::noop()),
RecorderOperation::RegisterHistogram("histogram_key".into(), Histogram::noop()),
];
let recorder1 = MockBasicRecorder::from_operations(operations.clone());
let recorder2 = MockBasicRecorder::from_operations(operations.clone());
let fanout =
FanoutBuilder::default().add_recorder(recorder1).add_recorder(recorder2).build();
for operation in operations {
operation.apply_to_recorder(&fanout);
}
}
}