LCOV - code coverage report
Current view: top level - libs/metrics/src - lib.rs (source / functions) Coverage Total Hit
Test: c8f8d331b83562868d9054d9e0e68f866772aeaa.info Lines: 31.6 % 282 89
Test Date: 2025-07-26 17:20:05 Functions: 26.0 % 96 25

            Line data    Source code
       1              : //! We re-export those from prometheus crate to
       2              : //! make sure that we use the same dep version everywhere.
       3              : //! Otherwise, we might not see all metrics registered via
       4              : //! a default registry.
       5              : #![deny(clippy::undocumented_unsafe_blocks)]
       6              : 
       7              : use std::sync::RwLock;
       8              : 
       9              : use measured::label::{LabelGroupSet, LabelGroupVisitor, LabelName, NoLabels};
      10              : use measured::metric::counter::CounterState;
      11              : use measured::metric::gauge::GaugeState;
      12              : use measured::metric::group::Encoding;
      13              : use measured::metric::name::{MetricName, MetricNameEncoder};
      14              : use measured::metric::{MetricEncoding, MetricFamilyEncoding, MetricType};
      15              : use measured::{FixedCardinalityLabel, LabelGroup, MetricGroup};
      16              : use once_cell::sync::Lazy;
      17              : use prometheus::Registry;
      18              : use prometheus::core::{
      19              :     Atomic, AtomicU64, Collector, GenericCounter, GenericCounterVec, GenericGauge, GenericGaugeVec,
      20              : };
      21              : pub use prometheus::local::LocalHistogram;
      22              : pub use prometheus::{
      23              :     Counter, CounterVec, Encoder, Error, Gauge, GaugeVec, Histogram, HistogramVec, IntCounter,
      24              :     IntCounterVec, IntGauge, IntGaugeVec, TextEncoder, core, default_registry, exponential_buckets,
      25              :     linear_buckets, opts, proto, register, register_counter_vec, register_gauge,
      26              :     register_gauge_vec, register_histogram, register_histogram_vec, register_int_counter,
      27              :     register_int_counter_vec, register_int_gauge, register_int_gauge_vec,
      28              : };
      29              : 
      30              : pub mod launch_timestamp;
      31              : mod wrappers;
      32              : pub use prometheus;
      33              : pub use wrappers::{CountedReader, CountedWriter};
      34              : mod hll;
      35              : pub use hll::{HyperLogLog, HyperLogLogState, HyperLogLogVec};
      36              : #[cfg(target_os = "linux")]
      37              : pub mod more_process_metrics;
      38              : 
      39              : pub type UIntGauge = GenericGauge<AtomicU64>;
      40              : pub type UIntGaugeVec = GenericGaugeVec<AtomicU64>;
      41              : 
      42              : #[macro_export]
      43              : macro_rules! register_uint_gauge_vec {
      44              :     ($NAME:expr, $HELP:expr, $LABELS_NAMES:expr $(,)?) => {{
      45              :         let gauge_vec = UIntGaugeVec::new($crate::opts!($NAME, $HELP), $LABELS_NAMES).unwrap();
      46              :         $crate::register(Box::new(gauge_vec.clone())).map(|_| gauge_vec)
      47              :     }};
      48              : }
      49              : 
      50              : #[macro_export]
      51              : macro_rules! register_uint_gauge {
      52              :     ($NAME:expr, $HELP:expr $(,)?) => {{
      53              :         let gauge = $crate::UIntGauge::new($NAME, $HELP).unwrap();
      54              :         $crate::register(Box::new(gauge.clone())).map(|_| gauge)
      55              :     }};
      56              : }
      57              : 
      58              : /// Special internal registry, to collect metrics independently from the default registry.
      59              : /// Was introduced to fix deadlock with lazy registration of metrics in the default registry.
      60              : static INTERNAL_REGISTRY: Lazy<Registry> = Lazy::new(Registry::new);
      61              : 
      62              : /// Register a collector in the internal registry. MUST be called before the first call to `gather()`.
      63              : ///
      64              : /// Otherwise, we can have a deadlock in the `gather()` call, trying to register a new collector
      65              : /// while holding the lock.
      66            0 : pub fn register_internal(c: Box<dyn Collector>) -> prometheus::Result<()> {
      67            0 :     INTERNAL_REGISTRY.register(c)
      68            0 : }
      69              : 
      70              : /// Gathers all Prometheus metrics and records the I/O stats just before that.
      71              : ///
      72              : /// Metrics gathering is a relatively simple and standalone operation, so
      73              : /// it might be fine to do it this way to keep things simple.
      74            0 : pub fn gather() -> Vec<prometheus::proto::MetricFamily> {
      75            0 :     update_rusage_metrics();
      76            0 :     let mut mfs = prometheus::gather();
      77            0 :     let mut internal_mfs = INTERNAL_REGISTRY.gather();
      78            0 :     mfs.append(&mut internal_mfs);
      79            0 :     mfs
      80            0 : }
      81              : 
      82            0 : static DISK_IO_BYTES: Lazy<IntGaugeVec> = Lazy::new(|| {
      83            0 :     register_int_gauge_vec!(
      84              :         "libmetrics_disk_io_bytes_total",
      85              :         "Bytes written and read from disk, grouped by the operation (read|write)",
      86            0 :         &["io_operation"]
      87              :     )
      88            0 :     .expect("Failed to register disk i/o bytes int gauge vec")
      89            0 : });
      90              : 
      91            0 : static MAXRSS_KB: Lazy<IntGauge> = Lazy::new(|| {
      92            0 :     register_int_gauge!(
      93              :         "libmetrics_maxrss_kb",
      94              :         "Memory usage (Maximum Resident Set Size)"
      95              :     )
      96            0 :     .expect("Failed to register maxrss_kb int gauge")
      97            0 : });
      98              : 
      99              : /// Most common fsync latency is 50 µs - 100 µs, but it can be much higher,
     100              : /// especially during many concurrent disk operations.
     101              : pub const DISK_FSYNC_SECONDS_BUCKETS: &[f64] =
     102              :     &[0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0, 10.0, 30.0];
     103              : 
     104              : /// Constructs histogram buckets that are powers of two starting at 1 (i.e. 2^0), covering the end
     105              : /// points. For example, passing start=5,end=20 yields 4,8,16,32 as does start=4,end=32.
     106           29 : pub fn pow2_buckets(start: usize, end: usize) -> Vec<f64> {
     107           29 :     assert_ne!(start, 0);
     108           28 :     assert!(start <= end);
     109           27 :     let start = match start.checked_next_power_of_two() {
     110           27 :         Some(n) if n == start => n, // start already power of two
     111            5 :         Some(n) => n >> 1,          // power of two below start
     112            0 :         None => panic!("start too large"),
     113              :     };
     114           27 :     let end = end.checked_next_power_of_two().expect("end too large");
     115          191 :     std::iter::successors(Some(start), |n| n.checked_mul(2))
     116          191 :         .take_while(|n| n <= &end)
     117          168 :         .map(|n| n as f64)
     118           27 :         .collect()
     119           27 : }
     120              : 
     121              : pub struct InfoMetric<L: LabelGroup, M: MetricType = GaugeState> {
     122              :     label: RwLock<L>,
     123              :     metric: M,
     124              : }
     125              : 
     126              : impl<L: LabelGroup> InfoMetric<L> {
     127           51 :     pub fn new(label: L) -> Self {
     128           51 :         Self::with_metric(label, GaugeState::new(1))
     129            0 :     }
     130              : }
     131              : 
     132              : impl<L: LabelGroup + Default> Default for InfoMetric<L, GaugeState> {
     133           51 :     fn default() -> Self {
     134           51 :         InfoMetric::new(L::default())
     135            0 :     }
     136              : }
     137              : 
     138              : impl<L: LabelGroup, M: MetricType<Metadata = ()>> InfoMetric<L, M> {
     139           51 :     pub fn with_metric(label: L, metric: M) -> Self {
     140           51 :         Self {
     141           51 :             label: RwLock::new(label),
     142           51 :             metric,
     143           51 :         }
     144            0 :     }
     145              : 
     146            0 :     pub fn set_label(&self, label: L) {
     147            0 :         *self.label.write().unwrap() = label;
     148            0 :     }
     149              : }
     150              : 
     151              : impl<L, M, E> MetricFamilyEncoding<E> for InfoMetric<L, M>
     152              : where
     153              :     L: LabelGroup,
     154              :     M: MetricEncoding<E, Metadata = ()>,
     155              :     E: Encoding,
     156              : {
     157            0 :     fn collect_family_into(
     158            0 :         &self,
     159            0 :         name: impl measured::metric::name::MetricNameEncoder,
     160            0 :         enc: &mut E,
     161            0 :     ) -> Result<(), E::Err> {
     162            0 :         M::write_type(&name, enc)?;
     163            0 :         self.metric
     164            0 :             .collect_into(&(), &*self.label.read().unwrap(), name, enc)
     165            0 :     }
     166              : }
     167              : 
     168              : pub struct BuildInfo {
     169              :     pub revision: &'static str,
     170              :     pub build_tag: &'static str,
     171              : }
     172              : 
     173              : impl LabelGroup for BuildInfo {
     174            0 :     fn visit_values(&self, v: &mut impl LabelGroupVisitor) {
     175              :         const REVISION: &LabelName = LabelName::from_str("revision");
     176            0 :         v.write_value(REVISION, &self.revision);
     177              :         const BUILD_TAG: &LabelName = LabelName::from_str("build_tag");
     178            0 :         v.write_value(BUILD_TAG, &self.build_tag);
     179            0 :     }
     180              : }
     181              : 
     182              : #[derive(MetricGroup)]
     183              : #[metric(new(build_info: BuildInfo))]
     184              : pub struct NeonMetrics {
     185              :     #[cfg(target_os = "linux")]
     186              :     #[metric(namespace = "process")]
     187              :     #[metric(init = measured_process::ProcessCollector::for_self())]
     188              :     process: measured_process::ProcessCollector,
     189              : 
     190              :     #[metric(namespace = "libmetrics")]
     191              :     #[metric(init = LibMetrics::new(build_info))]
     192              :     libmetrics: LibMetrics,
     193              : }
     194              : 
     195              : #[derive(MetricGroup)]
     196              : #[metric(new(build_info: BuildInfo))]
     197              : pub struct LibMetrics {
     198              :     #[metric(init = InfoMetric::new(build_info))]
     199              :     build_info: InfoMetric<BuildInfo>,
     200              : 
     201              :     #[metric(flatten)]
     202              :     rusage: Rusage,
     203              : 
     204              :     serve_count: CollectionCounter,
     205              : }
     206              : 
     207            0 : fn write_gauge<Enc: Encoding>(
     208            0 :     x: i64,
     209            0 :     labels: impl LabelGroup,
     210            0 :     name: impl MetricNameEncoder,
     211            0 :     enc: &mut Enc,
     212            0 : ) -> Result<(), Enc::Err>
     213            0 : where
     214            0 :     GaugeState: MetricEncoding<Enc>,
     215              : {
     216            0 :     GaugeState::new(x).collect_into(&(), labels, name, enc)
     217            0 : }
     218              : 
     219              : #[derive(Default)]
     220              : struct Rusage;
     221              : 
     222              : #[derive(FixedCardinalityLabel, Clone, Copy)]
     223              : #[label(singleton = "io_operation")]
     224              : enum IoOp {
     225              :     Read,
     226              :     Write,
     227              : }
     228              : 
     229              : impl<T: Encoding> MetricGroup<T> for Rusage
     230              : where
     231              :     GaugeState: MetricEncoding<T>,
     232              : {
     233            0 :     fn collect_group_into(&self, enc: &mut T) -> Result<(), T::Err> {
     234              :         const DISK_IO: &MetricName = MetricName::from_str("disk_io_bytes_total");
     235              :         const MAXRSS: &MetricName = MetricName::from_str("maxrss_kb");
     236              : 
     237            0 :         let ru = get_rusage_stats();
     238              : 
     239            0 :         enc.write_help(
     240              :             DISK_IO,
     241            0 :             "Bytes written and read from disk, grouped by the operation (read|write)",
     242            0 :         )?;
     243            0 :         GaugeState::write_type(DISK_IO, enc)?;
     244            0 :         write_gauge(ru.ru_inblock * BYTES_IN_BLOCK, IoOp::Read, DISK_IO, enc)?;
     245            0 :         write_gauge(ru.ru_oublock * BYTES_IN_BLOCK, IoOp::Write, DISK_IO, enc)?;
     246              : 
     247            0 :         enc.write_help(MAXRSS, "Memory usage (Maximum Resident Set Size)")?;
     248            0 :         GaugeState::write_type(MAXRSS, enc)?;
     249            0 :         write_gauge(ru.ru_maxrss, IoOp::Read, MAXRSS, enc)?;
     250              : 
     251            0 :         Ok(())
     252            0 :     }
     253              : }
     254              : 
     255              : #[derive(Default)]
     256              : struct CollectionCounter(CounterState);
     257              : 
     258              : impl<T: Encoding> MetricFamilyEncoding<T> for CollectionCounter
     259              : where
     260              :     CounterState: MetricEncoding<T>,
     261              : {
     262            0 :     fn collect_family_into(
     263            0 :         &self,
     264            0 :         name: impl measured::metric::name::MetricNameEncoder,
     265            0 :         enc: &mut T,
     266            0 :     ) -> Result<(), T::Err> {
     267            0 :         self.0.inc();
     268            0 :         enc.write_help(&name, "Number of metric requests made")?;
     269            0 :         self.0.collect_into(&(), NoLabels, name, enc)
     270            0 :     }
     271              : }
     272              : 
     273            0 : pub fn set_build_info_metric(revision: &str, build_tag: &str) {
     274            0 :     let metric = register_int_gauge_vec!(
     275              :         "libmetrics_build_info",
     276              :         "Build/version information",
     277            0 :         &["revision", "build_tag"]
     278              :     )
     279            0 :     .expect("Failed to register build info metric");
     280            0 :     metric.with_label_values(&[revision, build_tag]).set(1);
     281            0 : }
     282              : const BYTES_IN_BLOCK: i64 = 512;
     283              : 
     284              : // Records I/O stats in a "cross-platform" way.
     285              : // Compiles both on macOS and Linux, but current macOS implementation always returns 0 as values for I/O stats.
     286              : // An alternative is to read procfs (`/proc/[pid]/io`) which does not work under macOS at all, hence abandoned.
     287              : //
     288              : // Uses https://www.freebsd.org/cgi/man.cgi?query=getrusage to retrieve the number of block operations
     289              : // performed by the process.
     290              : // We know the size of the block, so we can determine the I/O bytes out of it.
     291              : // The value might be not 100% exact, but should be fine for Prometheus metrics in this case.
     292            0 : fn update_rusage_metrics() {
     293            0 :     let rusage_stats = get_rusage_stats();
     294              : 
     295            0 :     DISK_IO_BYTES
     296            0 :         .with_label_values(&["read"])
     297            0 :         .set(rusage_stats.ru_inblock * BYTES_IN_BLOCK);
     298            0 :     DISK_IO_BYTES
     299            0 :         .with_label_values(&["write"])
     300            0 :         .set(rusage_stats.ru_oublock * BYTES_IN_BLOCK);
     301              : 
     302              :     // On macOS, the unit of maxrss is bytes; on Linux, it's kilobytes. https://stackoverflow.com/a/59915669
     303              :     #[cfg(target_os = "macos")]
     304              :     {
     305              :         MAXRSS_KB.set(rusage_stats.ru_maxrss / 1024);
     306              :     }
     307              :     #[cfg(not(target_os = "macos"))]
     308            0 :     {
     309            0 :         MAXRSS_KB.set(rusage_stats.ru_maxrss);
     310            0 :     }
     311            0 : }
     312              : 
     313            0 : fn get_rusage_stats() -> libc::rusage {
     314            0 :     let mut rusage = std::mem::MaybeUninit::uninit();
     315              : 
     316              :     // SAFETY: kernel will initialize the struct for us
     317              :     unsafe {
     318            0 :         let ret = libc::getrusage(libc::RUSAGE_SELF, rusage.as_mut_ptr());
     319            0 :         assert!(ret == 0, "getrusage failed: bad args");
     320            0 :         rusage.assume_init()
     321              :     }
     322            0 : }
     323              : 
     324              : /// Create an [`IntCounterPairVec`] and registers to default registry.
     325              : #[macro_export(local_inner_macros)]
     326              : macro_rules! register_int_counter_pair_vec {
     327              :     ($NAME1:expr, $HELP1:expr, $NAME2:expr, $HELP2:expr, $LABELS_NAMES:expr $(,)?) => {{
     328              :         match (
     329              :             $crate::register_int_counter_vec!($NAME1, $HELP1, $LABELS_NAMES),
     330              :             $crate::register_int_counter_vec!($NAME2, $HELP2, $LABELS_NAMES),
     331              :         ) {
     332              :             (Ok(inc), Ok(dec)) => Ok($crate::IntCounterPairVec::new(inc, dec)),
     333              :             (Err(e), _) | (_, Err(e)) => Err(e),
     334              :         }
     335              :     }};
     336              : }
     337              : 
     338              : /// Create an [`IntCounterPair`] and registers to default registry.
     339              : #[macro_export(local_inner_macros)]
     340              : macro_rules! register_int_counter_pair {
     341              :     ($NAME1:expr, $HELP1:expr, $NAME2:expr, $HELP2:expr $(,)?) => {{
     342              :         match (
     343              :             $crate::register_int_counter!($NAME1, $HELP1),
     344              :             $crate::register_int_counter!($NAME2, $HELP2),
     345              :         ) {
     346              :             (Ok(inc), Ok(dec)) => Ok($crate::IntCounterPair::new(inc, dec)),
     347              :             (Err(e), _) | (_, Err(e)) => Err(e),
     348              :         }
     349              :     }};
     350              : }
     351              : 
     352              : /// A Pair of [`GenericCounterVec`]s. Like an [`GenericGaugeVec`] but will always observe changes
     353              : pub struct GenericCounterPairVec<P: Atomic> {
     354              :     inc: GenericCounterVec<P>,
     355              :     dec: GenericCounterVec<P>,
     356              : }
     357              : 
     358              : /// A Pair of [`GenericCounter`]s. Like an [`GenericGauge`] but will always observe changes
     359              : pub struct GenericCounterPair<P: Atomic> {
     360              :     inc: GenericCounter<P>,
     361              :     dec: GenericCounter<P>,
     362              : }
     363              : 
     364              : impl<P: Atomic> GenericCounterPairVec<P> {
     365          226 :     pub fn new(inc: GenericCounterVec<P>, dec: GenericCounterVec<P>) -> Self {
     366          226 :         Self { inc, dec }
     367            0 :     }
     368              : 
     369              :     /// `get_metric_with_label_values` returns the [`GenericCounterPair<P>`] for the given slice
     370              :     /// of label values (same order as the VariableLabels in Desc). If that combination of
     371              :     /// label values is accessed for the first time, a new [`GenericCounterPair<P>`] is created.
     372              :     ///
     373              :     /// An error is returned if the number of label values is not the same as the
     374              :     /// number of VariableLabels in Desc.
     375          766 :     pub fn get_metric_with_label_values(
     376          766 :         &self,
     377          766 :         vals: &[&str],
     378          766 :     ) -> prometheus::Result<GenericCounterPair<P>> {
     379              :         Ok(GenericCounterPair {
     380          766 :             inc: self.inc.get_metric_with_label_values(vals)?,
     381          766 :             dec: self.dec.get_metric_with_label_values(vals)?,
     382              :         })
     383            0 :     }
     384              : 
     385              :     /// `with_label_values` works as `get_metric_with_label_values`, but panics if an error
     386              :     /// occurs.
     387          100 :     pub fn with_label_values(&self, vals: &[&str]) -> GenericCounterPair<P> {
     388          100 :         self.get_metric_with_label_values(vals).unwrap()
     389            0 :     }
     390              : 
     391           17 :     pub fn remove_label_values(&self, res: &mut [prometheus::Result<()>; 2], vals: &[&str]) {
     392           17 :         res[0] = self.inc.remove_label_values(vals);
     393           17 :         res[1] = self.dec.remove_label_values(vals);
     394            0 :     }
     395              : }
     396              : 
     397              : impl<P: Atomic> GenericCounterPair<P> {
     398            0 :     pub fn new(inc: GenericCounter<P>, dec: GenericCounter<P>) -> Self {
     399            0 :         Self { inc, dec }
     400            0 :     }
     401              : 
     402              :     /// Increment the gauge by 1, returning a guard that decrements by 1 on drop.
     403       114675 :     pub fn guard(&self) -> GenericCounterPairGuard<P> {
     404       114675 :         self.inc.inc();
     405       114675 :         GenericCounterPairGuard(self.dec.clone())
     406            0 :     }
     407              : 
     408              :     /// Increment the gauge by n, returning a guard that decrements by n on drop.
     409            0 :     pub fn guard_by(&self, n: P::T) -> GenericCounterPairGuardBy<P> {
     410            0 :         self.inc.inc_by(n);
     411            0 :         GenericCounterPairGuardBy(self.dec.clone(), n)
     412            0 :     }
     413              : 
     414              :     /// Increase the gauge by 1.
     415              :     #[inline]
     416         1969 :     pub fn inc(&self) {
     417         1969 :         self.inc.inc();
     418            0 :     }
     419              : 
     420              :     /// Decrease the gauge by 1.
     421              :     #[inline]
     422         1876 :     pub fn dec(&self) {
     423         1876 :         self.dec.inc();
     424            0 :     }
     425              : 
     426              :     /// Add the given value to the gauge. (The value can be
     427              :     /// negative, resulting in a decrement of the gauge.)
     428              :     #[inline]
     429            0 :     pub fn inc_by(&self, v: P::T) {
     430            0 :         self.inc.inc_by(v);
     431            0 :     }
     432              : 
     433              :     /// Subtract the given value from the gauge. (The value can be
     434              :     /// negative, resulting in an increment of the gauge.)
     435              :     #[inline]
     436            0 :     pub fn dec_by(&self, v: P::T) {
     437            0 :         self.dec.inc_by(v);
     438            0 :     }
     439              : }
     440              : 
     441              : impl<P: Atomic> Clone for GenericCounterPair<P> {
     442         3828 :     fn clone(&self) -> Self {
     443         3828 :         Self {
     444         3828 :             inc: self.inc.clone(),
     445         3828 :             dec: self.dec.clone(),
     446         3828 :         }
     447            0 :     }
     448              : }
     449              : 
     450              : /// Guard returned by [`GenericCounterPair::guard`]
     451              : pub struct GenericCounterPairGuard<P: Atomic>(GenericCounter<P>);
     452              : 
     453              : impl<P: Atomic> Drop for GenericCounterPairGuard<P> {
     454       114675 :     fn drop(&mut self) {
     455       114675 :         self.0.inc();
     456            0 :     }
     457              : }
     458              : /// Guard returned by [`GenericCounterPair::guard_by`]
     459              : pub struct GenericCounterPairGuardBy<P: Atomic>(GenericCounter<P>, P::T);
     460              : 
     461              : impl<P: Atomic> Drop for GenericCounterPairGuardBy<P> {
     462            0 :     fn drop(&mut self) {
     463            0 :         self.0.inc_by(self.1);
     464            0 :     }
     465              : }
     466              : 
     467              : /// A Pair of [`IntCounterVec`]s. Like an [`IntGaugeVec`] but will always observe changes
     468              : pub type IntCounterPairVec = GenericCounterPairVec<AtomicU64>;
     469              : 
     470              : /// A Pair of [`IntCounter`]s. Like an [`IntGauge`] but will always observe changes
     471              : pub type IntCounterPair = GenericCounterPair<AtomicU64>;
     472              : 
     473              : /// A guard for [`IntCounterPair`] that will decrement the gauge on drop
     474              : pub type IntCounterPairGuard = GenericCounterPairGuard<AtomicU64>;
     475              : 
     476              : pub trait CounterPairAssoc {
     477              :     const INC_NAME: &'static MetricName;
     478              :     const DEC_NAME: &'static MetricName;
     479              : 
     480              :     const INC_HELP: &'static str;
     481              :     const DEC_HELP: &'static str;
     482              : 
     483              :     type LabelGroupSet: LabelGroupSet;
     484              : }
     485              : 
     486              : pub struct CounterPairVec<A: CounterPairAssoc> {
     487              :     vec: measured::metric::MetricVec<MeasuredCounterPairState, A::LabelGroupSet>,
     488              : }
     489              : 
     490              : impl<A: CounterPairAssoc> Default for CounterPairVec<A>
     491              : where
     492              :     A::LabelGroupSet: Default,
     493              : {
     494          204 :     fn default() -> Self {
     495          204 :         Self {
     496          204 :             vec: Default::default(),
     497          204 :         }
     498            0 :     }
     499              : }
     500              : 
     501              : impl<A: CounterPairAssoc> CounterPairVec<A> {
     502            0 :     pub fn guard(
     503            0 :         &self,
     504            0 :         labels: <A::LabelGroupSet as LabelGroupSet>::Group<'_>,
     505            0 :     ) -> MeasuredCounterPairGuard<'_, A> {
     506            0 :         let id = self.vec.with_labels(labels);
     507            0 :         self.vec.get_metric(id).inc.inc();
     508            0 :         MeasuredCounterPairGuard { vec: &self.vec, id }
     509            0 :     }
     510            0 :     pub fn inc(&self, labels: <A::LabelGroupSet as LabelGroupSet>::Group<'_>) {
     511            0 :         let id = self.vec.with_labels(labels);
     512            0 :         self.vec.get_metric(id).inc.inc();
     513            0 :     }
     514            0 :     pub fn dec(&self, labels: <A::LabelGroupSet as LabelGroupSet>::Group<'_>) {
     515            0 :         let id = self.vec.with_labels(labels);
     516            0 :         self.vec.get_metric(id).dec.inc();
     517            0 :     }
     518            0 :     pub fn remove_metric(
     519            0 :         &self,
     520            0 :         labels: <A::LabelGroupSet as LabelGroupSet>::Group<'_>,
     521            0 :     ) -> Option<MeasuredCounterPairState> {
     522            0 :         let id = self.vec.with_labels(labels);
     523            0 :         self.vec.remove_metric(id)
     524            0 :     }
     525              : 
     526            0 :     pub fn sample(&self, labels: <A::LabelGroupSet as LabelGroupSet>::Group<'_>) -> u64 {
     527            0 :         let id = self.vec.with_labels(labels);
     528            0 :         let metric = self.vec.get_metric(id);
     529              : 
     530            0 :         let inc = metric.inc.count.load(std::sync::atomic::Ordering::Relaxed);
     531            0 :         let dec = metric.dec.count.load(std::sync::atomic::Ordering::Relaxed);
     532            0 :         inc.saturating_sub(dec)
     533            0 :     }
     534              : }
     535              : 
     536              : impl<T, A> ::measured::metric::group::MetricGroup<T> for CounterPairVec<A>
     537              : where
     538              :     T: ::measured::metric::group::Encoding,
     539              :     A: CounterPairAssoc,
     540              :     ::measured::metric::counter::CounterState: ::measured::metric::MetricEncoding<T>,
     541              : {
     542            0 :     fn collect_group_into(&self, enc: &mut T) -> Result<(), T::Err> {
     543              :         // write decrement first to avoid a race condition where inc - dec < 0
     544            0 :         T::write_help(enc, A::DEC_NAME, A::DEC_HELP)?;
     545            0 :         self.vec
     546            0 :             .collect_family_into(A::DEC_NAME, &mut Dec(&mut *enc))?;
     547              : 
     548            0 :         T::write_help(enc, A::INC_NAME, A::INC_HELP)?;
     549            0 :         self.vec
     550            0 :             .collect_family_into(A::INC_NAME, &mut Inc(&mut *enc))?;
     551              : 
     552            0 :         Ok(())
     553            0 :     }
     554              : }
     555              : 
     556              : #[derive(MetricGroup, Default)]
     557              : pub struct MeasuredCounterPairState {
     558              :     pub inc: CounterState,
     559              :     pub dec: CounterState,
     560              : }
     561              : 
     562              : impl measured::metric::MetricType for MeasuredCounterPairState {
     563              :     type Metadata = ();
     564              : }
     565              : 
     566              : pub struct MeasuredCounterPairGuard<'a, A: CounterPairAssoc> {
     567              :     vec: &'a measured::metric::MetricVec<MeasuredCounterPairState, A::LabelGroupSet>,
     568              :     id: measured::metric::LabelId<A::LabelGroupSet>,
     569              : }
     570              : 
     571              : impl<A: CounterPairAssoc> Drop for MeasuredCounterPairGuard<'_, A> {
     572            0 :     fn drop(&mut self) {
     573            0 :         self.vec.get_metric(self.id).dec.inc();
     574            0 :     }
     575              : }
     576              : 
     577              : /// [`MetricEncoding`] for [`MeasuredCounterPairState`] that only writes the inc counter to the inner encoder.
     578              : struct Inc<T>(T);
     579              : /// [`MetricEncoding`] for [`MeasuredCounterPairState`] that only writes the dec counter to the inner encoder.
     580              : struct Dec<T>(T);
     581              : 
     582              : impl<T: Encoding> Encoding for Inc<T> {
     583              :     type Err = T::Err;
     584              : 
     585            0 :     fn write_help(&mut self, name: impl MetricNameEncoder, help: &str) -> Result<(), Self::Err> {
     586            0 :         self.0.write_help(name, help)
     587            0 :     }
     588              : }
     589              : 
     590              : impl<T: Encoding> MetricEncoding<Inc<T>> for MeasuredCounterPairState
     591              : where
     592              :     CounterState: MetricEncoding<T>,
     593              : {
     594            0 :     fn write_type(name: impl MetricNameEncoder, enc: &mut Inc<T>) -> Result<(), T::Err> {
     595            0 :         CounterState::write_type(name, &mut enc.0)
     596            0 :     }
     597            0 :     fn collect_into(
     598            0 :         &self,
     599            0 :         metadata: &(),
     600            0 :         labels: impl LabelGroup,
     601            0 :         name: impl MetricNameEncoder,
     602            0 :         enc: &mut Inc<T>,
     603            0 :     ) -> Result<(), T::Err> {
     604            0 :         self.inc.collect_into(metadata, labels, name, &mut enc.0)
     605            0 :     }
     606              : }
     607              : 
     608              : impl<T: Encoding> Encoding for Dec<T> {
     609              :     type Err = T::Err;
     610              : 
     611            0 :     fn write_help(&mut self, name: impl MetricNameEncoder, help: &str) -> Result<(), Self::Err> {
     612            0 :         self.0.write_help(name, help)
     613            0 :     }
     614              : }
     615              : 
     616              : /// Write the dec counter to the encoder
     617              : impl<T: Encoding> MetricEncoding<Dec<T>> for MeasuredCounterPairState
     618              : where
     619              :     CounterState: MetricEncoding<T>,
     620              : {
     621            0 :     fn write_type(name: impl MetricNameEncoder, enc: &mut Dec<T>) -> Result<(), T::Err> {
     622            0 :         CounterState::write_type(name, &mut enc.0)
     623            0 :     }
     624            0 :     fn collect_into(
     625            0 :         &self,
     626            0 :         metadata: &(),
     627            0 :         labels: impl LabelGroup,
     628            0 :         name: impl MetricNameEncoder,
     629            0 :         enc: &mut Dec<T>,
     630            0 :     ) -> Result<(), T::Err> {
     631            0 :         self.dec.collect_into(metadata, labels, name, &mut enc.0)
     632            0 :     }
     633              : }
     634              : 
     635              : #[cfg(test)]
     636              : mod tests {
     637              :     use super::*;
     638              : 
     639              :     const POW2_BUCKETS_MAX: usize = 1 << (usize::BITS - 1);
     640              : 
     641              :     #[test]
     642            1 :     fn pow2_buckets_cases() {
     643            1 :         assert_eq!(pow2_buckets(1, 1), vec![1.0]);
     644            1 :         assert_eq!(pow2_buckets(1, 2), vec![1.0, 2.0]);
     645            1 :         assert_eq!(pow2_buckets(1, 3), vec![1.0, 2.0, 4.0]);
     646            1 :         assert_eq!(pow2_buckets(1, 4), vec![1.0, 2.0, 4.0]);
     647            1 :         assert_eq!(pow2_buckets(1, 5), vec![1.0, 2.0, 4.0, 8.0]);
     648            1 :         assert_eq!(pow2_buckets(1, 6), vec![1.0, 2.0, 4.0, 8.0]);
     649            1 :         assert_eq!(pow2_buckets(1, 7), vec![1.0, 2.0, 4.0, 8.0]);
     650            1 :         assert_eq!(pow2_buckets(1, 8), vec![1.0, 2.0, 4.0, 8.0]);
     651            1 :         assert_eq!(
     652            1 :             pow2_buckets(1, 200),
     653            1 :             vec![1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0]
     654              :         );
     655              : 
     656            1 :         assert_eq!(pow2_buckets(1, 8), vec![1.0, 2.0, 4.0, 8.0]);
     657            1 :         assert_eq!(pow2_buckets(2, 8), vec![2.0, 4.0, 8.0]);
     658            1 :         assert_eq!(pow2_buckets(3, 8), vec![2.0, 4.0, 8.0]);
     659            1 :         assert_eq!(pow2_buckets(4, 8), vec![4.0, 8.0]);
     660            1 :         assert_eq!(pow2_buckets(5, 8), vec![4.0, 8.0]);
     661            1 :         assert_eq!(pow2_buckets(6, 8), vec![4.0, 8.0]);
     662            1 :         assert_eq!(pow2_buckets(7, 8), vec![4.0, 8.0]);
     663            1 :         assert_eq!(pow2_buckets(8, 8), vec![8.0]);
     664            1 :         assert_eq!(pow2_buckets(20, 200), vec![16.0, 32.0, 64.0, 128.0, 256.0]);
     665              : 
     666              :         // Largest valid values.
     667            1 :         assert_eq!(
     668            1 :             pow2_buckets(1, POW2_BUCKETS_MAX).len(),
     669              :             usize::BITS as usize
     670              :         );
     671            1 :         assert_eq!(pow2_buckets(POW2_BUCKETS_MAX, POW2_BUCKETS_MAX).len(), 1);
     672            1 :     }
     673              : 
     674              :     #[test]
     675              :     #[should_panic]
     676            1 :     fn pow2_buckets_zero_start() {
     677            1 :         pow2_buckets(0, 1);
     678            1 :     }
     679              : 
     680              :     #[test]
     681              :     #[should_panic]
     682            1 :     fn pow2_buckets_end_lt_start() {
     683            1 :         pow2_buckets(2, 1);
     684            1 :     }
     685              : 
     686              :     #[test]
     687              :     #[should_panic]
     688            1 :     fn pow2_buckets_end_overflow_min() {
     689            1 :         pow2_buckets(1, POW2_BUCKETS_MAX + 1);
     690            1 :     }
     691              : 
     692              :     #[test]
     693              :     #[should_panic]
     694            1 :     fn pow2_buckets_end_overflow_max() {
     695            1 :         pow2_buckets(1, usize::MAX);
     696            1 :     }
     697              : }
        

Generated by: LCOV version 2.1-beta