LCOV - code coverage report
Current view: top level - libs/metrics/src - lib.rs (source / functions) Coverage Total Hit
Test: 1e20c4f2b28aa592527961bb32170ebbd2c9172f.info Lines: 29.6 % 270 80
Test Date: 2025-07-16 12:29:03 Functions: 25.3 % 87 22

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

Generated by: LCOV version 2.1-beta