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