LCOV - code coverage report
Current view: top level - libs/metrics/src - lib.rs (source / functions) Coverage Total Hit
Test: 6df3fc19ec669bcfbbf9aba41d1338898d24eaa0.info Lines: 31.2 % 304 95
Test Date: 2025-03-12 18:28:53 Functions: 36.4 % 121 44

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

Generated by: LCOV version 2.1-beta