use crate::access::get_metrics;
use crate::{Id, MetricsHandle, Tags};
#[cfg(all(feature = "span", feature = "rdtsc"))]
use quanta::Clock;
use std::ops::Deref;
#[cfg(all(feature = "span", not(feature = "rdtsc")))]
use std::time::Instant;
pub struct Histogram {
id: Id,
handle: &'static MetricsHandle,
#[cfg(all(feature = "span", feature = "rdtsc"))]
clock: Clock,
}
impl std::fmt::Debug for Histogram {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut debug = f.debug_struct("Histogram");
debug.field("id", &self.id);
#[cfg(all(feature = "span", feature = "rdtsc"))]
{
debug.field("clock", &self.clock);
}
debug.finish()
}
}
impl Histogram {
pub fn new(name: &str, tags: Tags) -> Self {
let metrics = get_metrics();
let histogram_id = metrics.new_histogram(name, tags);
Self {
id: histogram_id,
handle: metrics,
#[cfg(all(feature = "span", feature = "rdtsc"))]
clock: Clock::new(),
}
}
}
pub trait HistogramOps {
fn record(&self, value: u64);
fn span(&self) -> Span<'_>;
fn with_span<F: FnOnce() -> R, R>(&self, f: F) -> R;
}
impl HistogramOps for Histogram {
#[inline]
fn record(&self, value: u64) {
self.handle.record(self.id, value);
}
#[inline]
#[cfg(feature = "span")]
fn span(&self) -> Span<'_> {
Span {
histogram: self,
#[cfg(feature = "rdtsc")]
start_raw: self.clock.raw(),
#[cfg(not(feature = "rdtsc"))]
start_instant: Instant::now(),
}
}
#[inline]
#[cfg(not(feature = "span"))]
fn span(&self) -> Span<'_> {
Span {
_marker: std::marker::PhantomData,
}
}
#[inline]
fn with_span<F: FnOnce() -> R, R>(&self, f: F) -> R {
let _span = self.span();
f()
}
}
impl<T> HistogramOps for T
where
T: Deref<Target = Histogram>,
{
#[inline]
fn record(&self, value: u64) {
self.deref().record(value);
}
#[inline]
fn span(&self) -> Span<'_> {
self.deref().span()
}
#[inline]
fn with_span<F: FnOnce() -> R, R>(&self, f: F) -> R {
self.deref().with_span(f)
}
}
impl Drop for Histogram {
fn drop(&mut self) {
self.handle.delete_histogram(self.id);
}
}
#[cfg(feature = "span")]
pub struct Span<'a> {
histogram: &'a Histogram,
#[cfg(feature = "rdtsc")]
start_raw: u64,
#[cfg(not(feature = "rdtsc"))]
start_instant: Instant,
}
#[cfg(not(feature = "span"))]
pub struct Span<'a> {
_marker: std::marker::PhantomData<&'a ()>,
}
#[cfg(feature = "span")]
impl Drop for Span<'_> {
fn drop(&mut self) {
#[cfg(feature = "rdtsc")]
{
let end_raw = self.histogram.clock.raw();
let elapsed = self.histogram.clock.delta_as_nanos(self.start_raw, end_raw);
self.histogram.record(elapsed);
}
#[cfg(not(feature = "rdtsc"))]
{
let elapsed = self.start_instant.elapsed();
let nanos = elapsed
.as_secs()
.wrapping_mul(1_000_000_000)
.wrapping_add(u64::from(elapsed.subsec_nanos()));
self.histogram.record(nanos);
}
}
}