1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
//! Utilities for working with Prometheus metrics in Rust
//!
//! This crate builds on the Promtheus crate to provide API with additional safety guardrails:
//!
//! * Use [`InstrumentedFuture`] to easily instrument futures with metric updates.
//! * Use [`GuardedGauge`] to work with gauges using an RAII-style guard that decrements
//!   the gauge upon drop.
//! * Use [`IntCounterWithLabels`] and [`IntGaugeWithLabels`] to produce labeled Prometheus
//!   metrics with a type-safe API.

// When building the project in release mode:
//   (1): Promote warnings into errors.
//   (2): Warn about public items that are missing documentation.
//   (3): Deny broken documentation links.
//   (4): Deny invalid codeblock attributes in documentation.
//   (5): Promote warnings in examples into errors, except for unused variables.
#![cfg_attr(not(debug_assertions), deny(warnings))]
#![cfg_attr(not(debug_assertions), warn(missing_docs))]
#![cfg_attr(not(debug_assertions), deny(broken_intra_doc_links))]
#![cfg_attr(not(debug_assertions), deny(invalid_codeblock_attributes))]
#![cfg_attr(not(debug_assertions), doc(test(attr(deny(warnings)))))]
#![cfg_attr(not(debug_assertions), doc(test(attr(allow(dead_code)))))]
#![cfg_attr(not(debug_assertions), doc(test(attr(allow(unused_variables)))))]

mod guards;
mod instrumented_future;
mod labels;
mod percentile;

pub use guards::{
    DeferredAdd, DeferredAddWithLabels, DeferredCounter, GaugeGuard, GenericGaugeGuard,
    GuardedGauge, IntGaugeGuard,
};
pub use instrumented_future::{InstrumentedFuture, IntoInstrumentedFuture};
pub use labels::{IntCounterWithLabels, IntGaugeWithLabels, LabelValues, Labels};
pub use percentile::{Observations, Sample, TimingBucket, Windowing};

#[allow(missing_docs)]
pub mod paste_crate {
    pub use paste::*;
}

// See `label_enum!` below; the factoring into two macros is to accomodate parsing
// multiple kinds of visibility annotations.
#[macro_export]
#[doc(hidden)]
macro_rules! __label_enum_internal {
    ($(#[$attr:meta])* ($($vis:tt)*) enum $N:ident { $($(#[$var_attr:meta])* $V:ident),* }) => {
        $(#[$attr])* $($vis)* enum $N { $($(#[$var_attr])* $V),* }

            $crate::paste_crate::paste! {
                impl $N {
                    /// The name of this enum variant, as a string slice.
                    pub fn as_str(&self) -> &'static str {
                        match self {
                            $($N::$V => stringify!([<$V:snake>])),*
                        }
                    }

                    /// A vector containing one instance of each of the enum's variants.
                    pub fn all_variants() -> Vec<Self> {
                        vec![$($N::$V),*]
                    }
                }
            }
    };
}

/// Declare an enum intended to be used as a Prometheus label.
///
/// This helper macro can only be used to define enums consisting of tags without values.
/// Each tag corresponds to a possible Prometheus label value. The macro then generates
/// two functions, `as_str` and `all_variants`, as described in the example below. Those
/// funtions are intended to assist in implementing the [`Labels`] trait for a label struct
/// that contains the enum, ensuring a consistent conversion to strings for label values,
/// and that all possible variants are included when implementing `possible_label_values`.
///
/// [`Labels`]: trait.Labels.html
///
/// # Example
///
/// When using the macro, define exactly one `enum` inside, as you would normally:
///
/// ```ignore
/// label_enum! {
///     pub(crate) enum MyErrorLabel {
///         IoError,
///         TimeoutError,
///         MemoryError,
///     }
/// }
/// ```
///
/// The macro will declare the enum exactly as provided. But in addition, it will generate
/// two functions:
///
/// ```ignore
/// impl MyErrorLabel {
///     /// The name of this enum variant, as a string slice.
///     pub fn as_str(&self) -> &'static str { ... }
///
///     /// A vector containing one instance of each of the enum's variants.
///     pub fn all_variants() -> Vec<Self> { ... }
/// }
/// ```
#[macro_export(local_inner_macros)]
macro_rules! label_enum {
    ($(#[$attr:meta])* enum $N:ident { $($(#[$var_attr:meta])* $V:ident),* $(,)* }) => {
        __label_enum_internal!($(#[$attr])* () enum $N { $($(#[$var_attr])* $V),* });
    };
    ($(#[$attr:meta])* pub enum $N:ident { $($(#[$var_attr:meta])* $V:ident),* $(,)* }) => {
        __label_enum_internal!($(#[$attr])* (pub) enum $N { $($(#[$var_attr])* $V),* });
    };
    ($(#[$attr:meta])* pub ($($vis:tt)+) enum $N:ident { $($(#[$var_attr:meta])* $V:ident),* $(,)* }) => {
        __label_enum_internal!($(#[$attr])* (pub ($($vis)+)) enum $N { $($(#[$var_attr])* $V),* });
    };
}