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