use parking_lot::RwLockReadGuard;
use std::any::Any;
use std::borrow::Cow;
mod counter;
mod gauge;
mod heatmap;
mod lazy;
extern crate self as metriken;
pub mod dynmetrics;
pub use crate::counter::Counter;
pub use crate::dynmetrics::{DynBoxedMetric, DynPinnedMetric};
pub use crate::gauge::Gauge;
pub use crate::heatmap::Heatmap;
pub use crate::lazy::{Lazy, Relaxed};
pub use metriken_derive::metric;
pub extern crate clocksource as time;
#[doc(hidden)]
pub use metriken_derive::to_lowercase;
#[doc(hidden)]
pub mod export {
pub extern crate linkme;
pub use clocksource::{Duration, Nanoseconds};
#[linkme::distributed_slice]
pub static METRICS: [crate::MetricEntry] = [..];
}
#[macro_export]
#[rustfmt::skip]
macro_rules! counter {
($name:ident) => {
#[$crate::metric(
name = $crate::to_lowercase!($name),
crate = $crate
)]
pub static $name: $crate::Counter = $crate::Counter::new();
};
($name:ident, $description:tt) => {
#[$crate::metric(
name = $crate::to_lowercase!($name),
description = $description,
crate = $crate
)]
pub static $name: $crate::Counter = $crate::Counter::new();
};
}
#[macro_export]
#[rustfmt::skip]
macro_rules! gauge {
($name:ident) => {
#[$crate::metric(
name = $crate::to_lowercase!($name),
crate = $crate
)]
pub static $name: $crate::Gauge = $crate::Gauge::new();
};
($name:ident, $description:tt) => {
#[$crate::metric(
name = $crate::to_lowercase!($name),
description = $description,
crate = $crate
)]
pub static $name: $crate::Gauge = $crate::Gauge::new();
};
}
#[macro_export]
#[rustfmt::skip]
macro_rules! heatmap {
($name:ident, $max:expr) => {
#[$crate::metric(
name = $crate::to_lowercase!($name),
crate = $crate
)]
pub static $name: $crate::Relaxed<$crate::Heatmap> = $crate::Relaxed::new(|| {
$crate::Heatmap::builder()
.maximum_value($max as _)
.min_resolution(1)
.min_resolution_range(1024)
.span($crate::export::Duration::<$crate::export::Nanoseconds<u64>>::from_secs(60))
.resolution($crate::export::Duration::<$crate::export::Nanoseconds<u64>>::from_secs(1))
.build()
.expect("bad heatmap configuration")
});
};
($name:ident, $max:expr, $description:tt) => {
#[$crate::metric(
name = $crate::to_lowercase!($name),
description = $description,
crate = $crate
)]
pub static $name: $crate::Relaxed<$crate::Heatmap> = $crate::Relaxed::new(|| {
$crate::Heatmap::builder()
.maximum_value($max as _)
.min_resolution(1)
.min_resolution_range(1024)
.span($crate::export::Duration::<$crate::export::Nanoseconds<u64>>::from_secs(60))
.resolution($crate::export::Duration::<$crate::export::Nanoseconds<u64>>::from_secs(1))
.build()
.expect("bad heatmap configuration")
});
};
}
pub trait Metric: Send + Sync + 'static {
fn is_enabled(&self) -> bool {
self.as_any().is_some()
}
fn as_any(&self) -> Option<&dyn Any>;
}
pub struct MetricEntry {
metric: MetricWrapper,
name: Cow<'static, str>,
namespace: Option<&'static str>,
description: Option<&'static str>,
}
impl MetricEntry {
#[doc(hidden)]
pub const fn _new_const(
metric: MetricWrapper,
name: &'static str,
namespace: &'static str,
description: &'static str,
) -> Self {
let namespace = if namespace.is_empty() {
None
} else {
Some(namespace)
};
let description = if description.is_empty() {
None
} else {
Some(description)
};
Self {
metric,
name: Cow::Borrowed(name),
namespace,
description,
}
}
pub fn new(metric: &'static dyn Metric, name: Cow<'static, str>) -> Self {
unsafe { Self::new_unchecked(metric, name) }
}
pub unsafe fn new_unchecked(metric: *const dyn Metric, name: Cow<'static, str>) -> Self {
Self {
metric: MetricWrapper(metric),
name,
namespace: None,
description: None,
}
}
pub fn metric(&self) -> &dyn Metric {
unsafe { &*self.metric.0 }
}
pub fn name(&self) -> &str {
&*self.name
}
pub fn namespace(&self) -> Option<&str> {
self.namespace
}
pub fn description(&self) -> Option<&str> {
self.description
}
}
unsafe impl Send for MetricEntry {}
unsafe impl Sync for MetricEntry {}
impl std::ops::Deref for MetricEntry {
type Target = dyn Metric;
#[inline]
fn deref(&self) -> &Self::Target {
self.metric()
}
}
impl std::fmt::Debug for MetricEntry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MetricEntry")
.field("name", &self.name())
.field("metric", &"<dyn Metric>")
.finish()
}
}
#[doc(hidden)]
pub struct MetricWrapper(pub *const dyn Metric);
pub fn metrics() -> Metrics {
Metrics {
dyn_metrics: crate::dynmetrics::get_registry(),
}
}
pub struct Metrics {
dyn_metrics: RwLockReadGuard<'static, dynmetrics::DynMetricsRegistry>,
}
impl Metrics {
pub fn static_metrics(&self) -> &'static [MetricEntry] {
&*crate::export::METRICS
}
pub fn dynamic_metrics(&self) -> &[MetricEntry] {
self.dyn_metrics.metrics()
}
pub fn iter(&self) -> <&Self as IntoIterator>::IntoIter {
self.into_iter()
}
}
impl<'a> IntoIterator for &'a Metrics {
type Item = &'a MetricEntry;
type IntoIter =
std::iter::Chain<std::slice::Iter<'a, MetricEntry>, std::slice::Iter<'a, MetricEntry>>;
fn into_iter(self) -> Self::IntoIter {
self.static_metrics()
.iter()
.chain(self.dynamic_metrics().iter())
}
}
pub struct MetricInstance<M> {
#[doc(hidden)]
pub metric: M,
name: &'static str,
description: Option<&'static str>,
}
impl<M> MetricInstance<M> {
#[doc(hidden)]
pub const fn new(metric: M, name: &'static str, description: &'static str) -> Self {
let description = if description.is_empty() {
None
} else {
Some(description)
};
Self {
metric,
name,
description,
}
}
pub const fn name(&self) -> &'static str {
self.name
}
pub const fn description(&self) -> Option<&'static str> {
self.description
}
}
impl<M> std::ops::Deref for MetricInstance<M> {
type Target = M;
#[inline]
fn deref(&self) -> &Self::Target {
&self.metric
}
}
impl<M> std::ops::DerefMut for MetricInstance<M> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.metric
}
}
impl<M> AsRef<M> for MetricInstance<M> {
#[inline]
fn as_ref(&self) -> &M {
&self.metric
}
}
impl<M> AsMut<M> for MetricInstance<M> {
#[inline]
fn as_mut(&mut self) -> &mut M {
&mut self.metric
}
}