use dashmap::DashMap;
use std::collections::HashMap;
use std::fmt::{self, Debug, Formatter};
use std::sync::Arc;
#[cfg(feature = "ser")]
use serde::ser::SerializeMap;
#[cfg(feature = "ser")]
use serde::{Serialize, Serializer};
use crate::filter::MetricsFilter;
use crate::key::{Key, Tag};
use crate::metrics::*;
use crate::mset::MetricsSet;
#[derive(Default)]
pub struct MetricsRegistry {
inner: Arc<Inner>,
filter: Option<Box<dyn MetricsFilter + 'static>>,
}
impl Debug for MetricsRegistry {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("MetricsRegistry")
.field("inner", &self.inner)
.finish()
}
}
#[derive(Default, Debug)]
struct Inner {
metrics: DashMap<Key, Metric>,
mset: DashMap<String, Arc<dyn MetricsSet + 'static>>,
}
impl MetricsRegistry {
pub fn new() -> MetricsRegistry {
MetricsRegistry::default()
}
pub fn arc() -> Arc<MetricsRegistry> {
Arc::new(MetricsRegistry::default())
}
pub fn meter(&self, name: &str) -> Arc<Meter> {
let key = Key::from_name(name);
self.do_meter(key)
}
pub fn meter_with_tags(&self, name: &str, tags: Vec<Tag>) -> Arc<Meter> {
let key = Key::from(name, tags);
self.do_meter(key)
}
fn do_meter(&self, key: Key) -> Arc<Meter> {
let meter = {
self.inner
.metrics
.get(&key)
.as_deref()
.map(|metric| match metric {
Metric::Meter(ref m) => m.clone(),
_ => {
panic!("A metric with same name and different type is already registered.")
}
})
};
if let Some(m) = meter {
m
} else {
let meter = Arc::new(Meter::new());
self.inner.metrics.insert(key, Metric::Meter(meter.clone()));
meter
}
}
pub fn histogram(&self, name: &str) -> Arc<Histogram> {
let key = Key::from_name(name);
self.do_histogram(key)
}
pub fn histogram_with_tags(&self, name: &str, tags: Vec<Tag>) -> Arc<Histogram> {
let key = Key::from(name, tags);
self.do_histogram(key)
}
fn do_histogram(&self, key: Key) -> Arc<Histogram> {
let histo = {
self.inner
.metrics
.get(&key)
.as_deref()
.map(|metric| match metric {
Metric::Histogram(ref m) => m.clone(),
_ => {
panic!("A metric with same name and different type is already registered.")
}
})
};
if let Some(m) = histo {
m
} else {
let histo = Arc::new(Histogram::new());
self.inner
.metrics
.insert(key, Metric::Histogram(histo.clone()));
histo
}
}
pub fn counter(&self, name: &str) -> Arc<Counter> {
let key = Key::from_name(name);
self.do_counter(key)
}
pub fn counter_with_tags(&self, name: &str, tags: Vec<Tag>) -> Arc<Counter> {
let key = Key::from(name, tags);
self.do_counter(key)
}
fn do_counter(&self, key: Key) -> Arc<Counter> {
let counter = {
self.inner
.metrics
.get(&key)
.as_deref()
.map(|metric| match metric {
Metric::Counter(ref m) => m.clone(),
_ => {
panic!("A metric with same name and different type is already registered.")
}
})
};
if let Some(m) = counter {
m
} else {
let counter = Arc::new(Counter::new());
self.inner
.metrics
.insert(key, Metric::Counter(counter.clone()));
counter
}
}
pub fn timer(&self, name: &str) -> Arc<Timer> {
let key = Key::from_name(name);
self.do_timer(key)
}
pub fn timer_with_tags(&self, name: &str, tags: Vec<Tag>) -> Arc<Timer> {
let key = Key::from(name, tags);
self.do_timer(key)
}
fn do_timer(&self, key: Key) -> Arc<Timer> {
let timer = {
self.inner
.metrics
.get(&key)
.as_deref()
.map(|metric| match metric {
Metric::Timer(ref m) => m.clone(),
_ => {
panic!("A metric with same name and different type is already registered.")
}
})
};
if let Some(m) = timer {
m
} else {
let timer = Arc::new(Timer::new());
self.inner.metrics.insert(key, Metric::Timer(timer.clone()));
timer
}
}
pub fn gauge(&self, name: &str, func: Box<dyn GaugeFn>) {
let key = Key::from_name(name);
self.do_gauge(key, func)
}
pub fn gauge_with_tags(&self, name: &str, tags: Vec<Tag>, func: Box<dyn GaugeFn>) {
let key = Key::from(name, tags);
self.do_gauge(key, func)
}
fn do_gauge(&self, key: Key, func: Box<dyn GaugeFn>) {
self.inner
.metrics
.insert(key, Metric::Gauge(Arc::new(Gauge::new(func))));
}
pub fn snapshots(&self) -> HashMap<Key, Metric> {
let filter = self.filter.as_ref();
let mut results: HashMap<Key, Metric> = HashMap::new();
let metrics = self.inner.metrics.clone();
for (k, v) in metrics.into_read_only().iter() {
if filter.map(|f| f.accept(k.name.as_str(), v)).unwrap_or(true) {
results.insert(k.to_owned(), v.clone());
}
}
let mset = self.inner.mset.clone();
for metrics_set in mset.into_read_only().values() {
let metrics = metrics_set.get_all();
for (k, v) in metrics.iter() {
if filter.map(|f| f.accept(k, v)).unwrap_or(true) {
results.insert(Key::from_name(k), v.clone());
}
}
}
results
}
pub fn set_filter(&mut self, filter: Option<Box<dyn MetricsFilter + 'static>>) {
self.filter = filter;
}
pub fn register_metrics_set(&self, name: &str, mset: Arc<dyn MetricsSet + 'static>) {
self.inner.mset.insert(name.to_owned(), mset);
}
pub fn unregister_metrics_set(&self, name: &str) {
self.inner.mset.remove(name);
}
}
#[cfg(test)]
mod test {
use crate::filter::MetricsFilter;
use crate::metrics::Metric;
use crate::registry::MetricsRegistry;
#[test]
fn test_metrics_filter() {
let mut registry = MetricsRegistry::new();
registry.meter("l1.tomcat.request").mark();
registry.meter("l1.jetty.request").mark();
registry.meter("l2.tomcat.request").mark();
registry.meter("l2.jetty.request").mark();
struct NameFilter;
impl MetricsFilter for NameFilter {
fn accept(&self, name: &str, _: &Metric) -> bool {
name.starts_with("l1")
}
}
registry.set_filter(Some(Box::new(NameFilter)));
let snapshot = registry.snapshots();
assert_eq!(2, snapshot.len());
}
}
#[cfg(feature = "ser")]
impl Serialize for MetricsRegistry {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let snapshot = self.snapshots();
let mut map = serializer.serialize_map(Some(snapshot.len()))?;
for (k, v) in snapshot.iter() {
map.serialize_entry(k, v)?;
}
map.end()
}
}