LCOV - code coverage report
Current view: top level - libs/metrics/src - lib.rs (source / functions) Coverage Total Hit
Test: 8ff8efadb0253cf618c612650348666c0c564111.info Lines: 31.5 % 305 96
Test Date: 2024-11-20 17:53:50 Functions: 35.4 % 113 40

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

Generated by: LCOV version 2.1-beta