use std::fmt;
use self::cell::RecorderOnceCell;
use crate::{Counter, Gauge, Histogram, Key, KeyName, SharedString, Unit};
mod cell {
use super::{Recorder, SetRecorderError};
use std::{
cell::UnsafeCell,
sync::atomic::{AtomicUsize, Ordering},
};
const UNINITIALIZED: usize = 0;
const INITIALIZING: usize = 1;
const INITIALIZED: usize = 2;
pub struct RecorderOnceCell {
recorder: UnsafeCell<Option<&'static dyn Recorder>>,
state: AtomicUsize,
}
impl RecorderOnceCell {
pub const fn new() -> Self {
Self { recorder: UnsafeCell::new(None), state: AtomicUsize::new(UNINITIALIZED) }
}
pub fn set(&self, recorder: &'static dyn Recorder) -> Result<(), SetRecorderError> {
match self.state.compare_exchange(
UNINITIALIZED,
INITIALIZING,
Ordering::Acquire,
Ordering::Relaxed,
) {
Ok(UNINITIALIZED) => {
unsafe {
self.recorder.get().write(Some(recorder));
}
self.state.store(INITIALIZED, Ordering::Release);
Ok(())
}
_ => Err(SetRecorderError(())),
}
}
pub unsafe fn clear(&self) {
self.state.store(UNINITIALIZED, Ordering::Relaxed);
}
pub fn try_load(&self) -> Option<&'static dyn Recorder> {
if self.state.load(Ordering::Acquire) != INITIALIZED {
None
} else {
unsafe { self.recorder.get().read() }
}
}
}
unsafe impl Send for RecorderOnceCell {}
unsafe impl Sync for RecorderOnceCell {}
}
static RECORDER: RecorderOnceCell = RecorderOnceCell::new();
static SET_RECORDER_ERROR: &str =
"attempted to set a recorder after the metrics system was already initialized";
pub trait Recorder {
fn describe_counter(&self, key: KeyName, unit: Option<Unit>, description: SharedString);
fn describe_gauge(&self, key: KeyName, unit: Option<Unit>, description: SharedString);
fn describe_histogram(&self, key: KeyName, unit: Option<Unit>, description: SharedString);
fn register_counter(&self, key: &Key) -> Counter;
fn register_gauge(&self, key: &Key) -> Gauge;
fn register_histogram(&self, key: &Key) -> Histogram;
}
pub struct NoopRecorder;
impl Recorder for NoopRecorder {
fn describe_counter(&self, _key: KeyName, _unit: Option<Unit>, _description: SharedString) {}
fn describe_gauge(&self, _key: KeyName, _unit: Option<Unit>, _description: SharedString) {}
fn describe_histogram(&self, _key: KeyName, _unit: Option<Unit>, _description: SharedString) {}
fn register_counter(&self, _key: &Key) -> Counter {
Counter::noop()
}
fn register_gauge(&self, _key: &Key) -> Gauge {
Gauge::noop()
}
fn register_histogram(&self, _key: &Key) -> Histogram {
Histogram::noop()
}
}
pub fn set_recorder(recorder: &'static dyn Recorder) -> Result<(), SetRecorderError> {
RECORDER.set(recorder)
}
pub fn set_boxed_recorder(recorder: Box<dyn Recorder>) -> Result<(), SetRecorderError> {
RECORDER.set(Box::leak(recorder))
}
#[doc(hidden)]
pub unsafe fn clear_recorder() {
RECORDER.clear();
}
#[derive(Debug)]
pub struct SetRecorderError(());
impl fmt::Display for SetRecorderError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.write_str(SET_RECORDER_ERROR)
}
}
impl std::error::Error for SetRecorderError {
fn description(&self) -> &str {
SET_RECORDER_ERROR
}
}
pub fn recorder() -> &'static dyn Recorder {
static NOOP: NoopRecorder = NoopRecorder;
try_recorder().unwrap_or(&NOOP)
}
pub fn try_recorder() -> Option<&'static dyn Recorder> {
RECORDER.try_load()
}