#![deny(missing_docs)]
#![doc = include_str!("../README.md")]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![allow(clippy::collapsible_if)]
pub mod emf;
pub mod flex;
pub mod instrument;
#[cfg(feature = "json")]
pub mod json;
mod keep_alive;
#[cfg(feature = "local-format")]
pub mod local;
pub mod timers;
pub mod slot;
pub mod _guide {
#[doc = include_str!("../docs/cookbook.md")]
pub mod cookbook {}
#[doc = include_str!("../docs/concurrency.md")]
pub mod concurrency {}
#[doc = include_str!("../docs/sinks.md")]
pub mod sinks {}
#[doc = include_str!("../docs/sampling.md")]
pub mod sampling {}
#[doc = include_str!("../docs/testing.md")]
pub mod testing {}
#[doc = include_str!("../docs/extending.md")]
pub mod extending {}
}
use metrique_core::CloseEntry;
use metrique_writer_core::Entry;
use metrique_writer_core::EntryWriter;
use metrique_writer_core::entry::SampleGroupElement;
pub use slot::{FlushGuard, ForceFlushGuard, LazySlot, OnParentDrop, Slot, SlotGuard};
pub use flex::Flex;
use core::ops::Deref;
use core::ops::DerefMut;
use keep_alive::DropAll;
use keep_alive::Guard;
use keep_alive::Parent;
use metrique_writer_core::EntrySink;
use std::fmt::Debug;
use std::sync::Arc;
#[cfg(all(shuttle, feature = "_shuttle"))]
use shuttle::sync::Mutex;
#[cfg(not(all(shuttle, feature = "_shuttle")))]
use std::sync::Mutex;
pub use metrique_core::{
CloseValue, CloseValueRef, Counter, CounterGuard, InflectableEntry, NameStyle,
OwnedCounterGuard,
};
#[doc(hidden)]
pub use metrique_core::{Identity, KebabCase, PascalCase, SnakeCase, Styles};
pub mod unit {
pub use metrique_writer_core::unit::{
Bit, BitPerSecond, Byte, BytePerSecond, Count, Gigabit, GigabitPerSecond, Gigabyte,
GigabytePerSecond, Kilobit, KilobitPerSecond, Kilobyte, KilobytePerSecond, Megabit,
MegabitPerSecond, Megabyte, MegabytePerSecond, Microsecond, Millisecond, None, Percent,
Second, Terabit, TerabitPerSecond, Terabyte, TerabytePerSecond,
};
use metrique_writer_core::{MetricValue, unit::WithUnit};
#[doc(hidden)]
pub trait AttachUnit: Sized {
type Output<U>;
fn make<U>(self) -> Self::Output<U>;
}
impl<V: MetricValue> AttachUnit for V {
type Output<U> = WithUnit<V, U>;
fn make<U>(self) -> Self::Output<U> {
WithUnit::from(self)
}
}
}
#[doc(hidden)]
pub mod format {
pub use metrique_writer_core::value::FormattedValue;
}
#[cfg(feature = "test-util")]
pub mod test_util {
pub use crate::writer::test_util::{
Inspector, Metric, TestEntry, TestEntrySink, test_entry_sink, test_metric, to_test_entry,
};
}
pub mod unit_of_work {
pub use metrique_macro::metrics;
}
pub type DefaultSink = metrique_writer_core::sink::BoxEntrySink;
pub struct AppendAndCloseOnDrop<E: CloseEntry, S: EntrySink<RootMetric<E>>> {
inner: Option<(E, S)>,
promoted: LazyPromotionSlot<Parent<PendingEmit<E, S>>>,
}
impl<E: CloseEntry, S: EntrySink<RootMetric<E>>> Unpin for AppendAndCloseOnDrop<E, S> {}
impl<E: CloseEntry + Debug, S: EntrySink<RootMetric<E>> + Debug> Debug
for AppendAndCloseOnDrop<E, S>
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let (entry, sink) = self.inner.as_ref().unwrap();
f.debug_struct("AppendAndCloseOnDrop")
.field("value", entry)
.field("sink", sink)
.finish()
}
}
impl<E: CloseEntry, S: EntrySink<RootMetric<E>>> AppendAndCloseOnDrop<E, S> {
pub fn discard(mut self) {
self.inner = None;
}
}
impl<E: CloseEntry + Send + Sync + 'static, S: EntrySink<RootMetric<E>> + Send + Sync + 'static>
AppendAndCloseOnDrop<E, S>
{
pub fn flush_guard(&self) -> FlushGuard {
self.promoted.with_init(
|| Parent::new(PendingEmit { pending: None }),
|parent| FlushGuard {
_drop_guard: parent.new_guard(),
},
)
}
pub fn force_flush_guard(&self) -> ForceFlushGuard {
self.promoted.with_init(
|| Parent::new(PendingEmit { pending: None }),
|parent| ForceFlushGuard::new(parent.force_drop_guard()),
)
}
pub fn handle(self) -> AppendAndCloseOnDropHandle<E, S> {
AppendAndCloseOnDropHandle {
inner: std::sync::Arc::new(self),
}
}
}
impl<E: CloseEntry, S: EntrySink<RootMetric<E>>> Deref for AppendAndCloseOnDrop<E, S> {
type Target = E;
fn deref(&self) -> &Self::Target {
&self.inner.as_ref().unwrap().0
}
}
impl<E: CloseEntry, S: EntrySink<RootMetric<E>>> DerefMut for AppendAndCloseOnDrop<E, S> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner.as_mut().unwrap().0
}
}
impl<E: CloseEntry, S: EntrySink<RootMetric<E>>> Drop for AppendAndCloseOnDrop<E, S> {
fn drop(&mut self) {
if let Some(inner) = self.inner.take() {
let promoted = self.promoted.get_mut();
match promoted {
Some(parent) => {
parent.pending = Some(inner);
}
None => {
let (entry, sink) = inner;
sink.append(RootEntry::new(entry.close()));
}
}
}
}
}
pub struct AppendAndCloseOnDropHandle<E: CloseEntry, S: EntrySink<RootMetric<E>>> {
inner: Arc<AppendAndCloseOnDrop<E, S>>,
}
impl<E: CloseEntry, S: EntrySink<RootMetric<E>>> Clone for AppendAndCloseOnDropHandle<E, S> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<E: CloseEntry, S: EntrySink<RootMetric<E>>> std::ops::Deref
for AppendAndCloseOnDropHandle<E, S>
{
type Target = E;
fn deref(&self) -> &Self::Target {
self.inner.deref()
}
}
pub fn append_and_close<C: CloseEntry, S: EntrySink<RootMetric<C>>>(
base: C,
sink: S,
) -> AppendAndCloseOnDrop<C, S> {
AppendAndCloseOnDrop {
inner: Some((base, sink)),
promoted: LazyPromotionSlot::new(),
}
}
struct LazyPromotionSlot<T> {
inner: Mutex<Option<T>>,
}
impl<T> LazyPromotionSlot<T> {
fn new() -> Self {
Self {
inner: Mutex::new(None),
}
}
fn get_mut(&mut self) -> &mut Option<T> {
self.inner.get_mut().unwrap_or_else(|e| e.into_inner())
}
}
impl<T: Send> LazyPromotionSlot<T> {
fn with_init<R>(&self, init: impl FnOnce() -> T, f: impl FnOnce(&T) -> R) -> R {
let mut guard = self.inner.lock().unwrap_or_else(|e| e.into_inner());
let value = guard.get_or_insert_with(init);
f(value)
}
}
unsafe impl<T> Sync for LazyPromotionSlot<T> {}
struct PendingEmit<E: CloseEntry, S: EntrySink<RootMetric<E>>> {
pending: Option<(E, S)>,
}
impl<E: CloseEntry, S: EntrySink<RootMetric<E>>> Drop for PendingEmit<E, S> {
fn drop(&mut self) {
if let Some((entry, sink)) = self.pending.take() {
sink.append(RootEntry::new(entry.close()));
}
}
}
pub struct SharedChild<T>(Arc<T>);
impl<T> SharedChild<T> {
pub fn new(value: T) -> Self {
Self(Arc::from(value))
}
}
impl<T> Clone for SharedChild<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T: Default> Default for SharedChild<T> {
fn default() -> Self {
Self::new(T::default())
}
}
impl<T> Deref for SharedChild<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[diagnostic::do_not_recommend]
impl<T: CloseValue> CloseValue for SharedChild<T> {
type Closed = Option<T::Closed>;
fn close(self) -> Self::Closed {
Arc::into_inner(self.0).map(|t| t.close())
}
}
pub type RootMetric<E> = RootEntry<<E as CloseValue>::Closed>;
pub struct RootEntry<M: InflectableEntry> {
metric: M,
}
impl<M: InflectableEntry> RootEntry<M> {
pub fn new(metric: M) -> Self {
Self { metric }
}
}
impl<M: InflectableEntry> Entry for RootEntry<M> {
fn write<'a>(&'a self, w: &mut impl EntryWriter<'a>) {
self.metric.write(w);
}
fn sample_group(&self) -> impl Iterator<Item = SampleGroupElement> {
self.metric.sample_group()
}
fn descriptors(&self) -> metrique_writer_core::Descriptors<'_> {
self.metric.descriptors()
}
}
#[cfg(feature = "service-metrics")]
pub use metrique_service_metrics::ServiceMetrics;
#[cfg(feature = "metrics-rs-bridge")]
pub use metrique_metricsrs as metrics_rs;
pub use metrique_core::concat;
pub mod writer {
pub use metrique_writer::GlobalEntrySink;
pub use metrique_writer::{AnyEntrySink, BoxEntrySink, EntrySink};
pub use metrique_writer::{BoxEntry, EntryConfig, EntryWriter, core::Entry};
pub use metrique_writer::{Convert, Unit};
pub use metrique_writer::{EntryIoStream, IoStreamError};
pub use metrique_writer::{MetricFlags, MetricValue, Observation, Value, ValueWriter};
pub use metrique_writer::{ValidationError, ValidationErrorBuilder};
pub use metrique_writer_macro::MetriqueEntry as Entry;
pub use metrique_writer::AttachGlobalEntrySinkExt;
pub use metrique_writer::{AttachGlobalEntrySink, EntryIoStreamExt, FormatExt, ShutdownFn};
pub use metrique_writer::{entry, format, quantize, sample, sink, stream, value};
#[cfg(feature = "test-util")]
#[doc(hidden)] pub use metrique_writer::test_util;
#[doc(hidden)] pub use metrique_writer::unit;
#[doc(hidden)]
pub use metrique_writer::core;
}
#[cfg(all(test, shuttle, feature = "_shuttle"))]
mod shuttle_promotion_tests {
use core::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use metrique_writer_core::shuttle_test;
use super::*;
#[derive(Default)]
struct MinimalEntry;
impl CloseValue for MinimalEntry {
type Closed = MinimalClosed;
fn close(self) -> Self::Closed {
MinimalClosed
}
}
struct MinimalClosed;
impl InflectableEntry for MinimalClosed {
fn write<'a>(&'a self, _w: &mut impl metrique_writer_core::EntryWriter<'a>) {}
}
#[derive(Clone)]
struct CountingSink(Arc<AtomicUsize>);
impl EntrySink<RootEntry<MinimalClosed>> for CountingSink {
fn append(&self, _entry: RootEntry<MinimalClosed>) {
self.0.fetch_add(1, Ordering::SeqCst);
}
fn flush_async(&self) -> metrique_writer_core::sink::FlushWait {
metrique_writer_core::sink::FlushWait::ready()
}
}
shuttle_test! {
num_iters = 2_000, depth = 4;
fn shuttle_concurrent_flush_guard_emits_exactly_once() {
let emit_count = Arc::new(AtomicUsize::new(0));
let sink = CountingSink(emit_count.clone());
let guard = Arc::new(append_and_close(MinimalEntry, sink));
let mut handles = vec![];
for _ in 0..4 {
let g = guard.clone();
handles.push(shuttle::thread::spawn(move || {
let _fg = g.flush_guard();
}));
}
for h in handles {
h.join().unwrap();
}
drop(guard);
assert_eq!(
emit_count.load(Ordering::SeqCst),
1,
"entry must be emitted exactly once regardless of promotion race"
);
}
}
}