LCOV - code coverage report
Current view: top level - libs/metrics/src - lib.rs (source / functions) Coverage Total Hit
Test: 12c2fc96834f59604b8ade5b9add28f1dce41ec6.info Lines: 17.1 % 257 44
Test Date: 2024-07-03 15:33:13 Functions: 26.5 % 98 26

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

Generated by: LCOV version 2.1-beta