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 0 : pub fn new(label: L) -> Self {
128 0 : Self::with_metric(label, GaugeState::new(1))
129 0 : }
130 : }
131 :
132 : impl<L: LabelGroup, M: MetricType<Metadata = ()>> InfoMetric<L, M> {
133 0 : pub fn with_metric(label: L, metric: M) -> Self {
134 0 : Self {
135 0 : label: RwLock::new(label),
136 0 : metric,
137 0 : }
138 0 : }
139 :
140 0 : pub fn set_label(&self, label: L) {
141 0 : *self.label.write().unwrap() = label;
142 0 : }
143 : }
144 :
145 : impl<L, M, E> MetricFamilyEncoding<E> for InfoMetric<L, M>
146 : where
147 : L: LabelGroup,
148 : M: MetricEncoding<E, Metadata = ()>,
149 : E: Encoding,
150 : {
151 0 : fn collect_family_into(
152 0 : &self,
153 0 : name: impl measured::metric::name::MetricNameEncoder,
154 0 : enc: &mut E,
155 0 : ) -> Result<(), E::Err> {
156 0 : M::write_type(&name, enc)?;
157 0 : self.metric
158 0 : .collect_into(&(), &*self.label.read().unwrap(), name, enc)
159 0 : }
160 : }
161 :
162 : pub struct BuildInfo {
163 : pub revision: &'static str,
164 : pub build_tag: &'static str,
165 : }
166 :
167 : impl LabelGroup for BuildInfo {
168 0 : fn visit_values(&self, v: &mut impl LabelGroupVisitor) {
169 : const REVISION: &LabelName = LabelName::from_str("revision");
170 0 : v.write_value(REVISION, &self.revision);
171 : const BUILD_TAG: &LabelName = LabelName::from_str("build_tag");
172 0 : v.write_value(BUILD_TAG, &self.build_tag);
173 0 : }
174 : }
175 :
176 : #[derive(MetricGroup)]
177 : #[metric(new(build_info: BuildInfo))]
178 : pub struct NeonMetrics {
179 : #[cfg(target_os = "linux")]
180 : #[metric(namespace = "process")]
181 : #[metric(init = measured_process::ProcessCollector::for_self())]
182 : process: measured_process::ProcessCollector,
183 :
184 : #[metric(namespace = "libmetrics")]
185 : #[metric(init = LibMetrics::new(build_info))]
186 : libmetrics: LibMetrics,
187 : }
188 :
189 : #[derive(MetricGroup)]
190 : #[metric(new(build_info: BuildInfo))]
191 : pub struct LibMetrics {
192 : #[metric(init = InfoMetric::new(build_info))]
193 : build_info: InfoMetric<BuildInfo>,
194 :
195 : #[metric(flatten)]
196 : rusage: Rusage,
197 :
198 : serve_count: CollectionCounter,
199 : }
200 :
201 0 : fn write_gauge<Enc: Encoding>(
202 0 : x: i64,
203 0 : labels: impl LabelGroup,
204 0 : name: impl MetricNameEncoder,
205 0 : enc: &mut Enc,
206 0 : ) -> Result<(), Enc::Err>
207 0 : where
208 0 : GaugeState: MetricEncoding<Enc>,
209 : {
210 0 : GaugeState::new(x).collect_into(&(), labels, name, enc)
211 0 : }
212 :
213 : #[derive(Default)]
214 : struct Rusage;
215 :
216 : #[derive(FixedCardinalityLabel, Clone, Copy)]
217 : #[label(singleton = "io_operation")]
218 : enum IoOp {
219 : Read,
220 : Write,
221 : }
222 :
223 : impl<T: Encoding> MetricGroup<T> for Rusage
224 : where
225 : GaugeState: MetricEncoding<T>,
226 : {
227 0 : fn collect_group_into(&self, enc: &mut T) -> Result<(), T::Err> {
228 : const DISK_IO: &MetricName = MetricName::from_str("disk_io_bytes_total");
229 : const MAXRSS: &MetricName = MetricName::from_str("maxrss_kb");
230 :
231 0 : let ru = get_rusage_stats();
232 :
233 0 : enc.write_help(
234 : DISK_IO,
235 0 : "Bytes written and read from disk, grouped by the operation (read|write)",
236 0 : )?;
237 0 : GaugeState::write_type(DISK_IO, enc)?;
238 0 : write_gauge(ru.ru_inblock * BYTES_IN_BLOCK, IoOp::Read, DISK_IO, enc)?;
239 0 : write_gauge(ru.ru_oublock * BYTES_IN_BLOCK, IoOp::Write, DISK_IO, enc)?;
240 :
241 0 : enc.write_help(MAXRSS, "Memory usage (Maximum Resident Set Size)")?;
242 0 : GaugeState::write_type(MAXRSS, enc)?;
243 0 : write_gauge(ru.ru_maxrss, IoOp::Read, MAXRSS, enc)?;
244 :
245 0 : Ok(())
246 0 : }
247 : }
248 :
249 : #[derive(Default)]
250 : struct CollectionCounter(CounterState);
251 :
252 : impl<T: Encoding> MetricFamilyEncoding<T> for CollectionCounter
253 : where
254 : CounterState: MetricEncoding<T>,
255 : {
256 0 : fn collect_family_into(
257 0 : &self,
258 0 : name: impl measured::metric::name::MetricNameEncoder,
259 0 : enc: &mut T,
260 0 : ) -> Result<(), T::Err> {
261 0 : self.0.inc();
262 0 : enc.write_help(&name, "Number of metric requests made")?;
263 0 : self.0.collect_into(&(), NoLabels, name, enc)
264 0 : }
265 : }
266 :
267 0 : pub fn set_build_info_metric(revision: &str, build_tag: &str) {
268 0 : let metric = register_int_gauge_vec!(
269 : "libmetrics_build_info",
270 : "Build/version information",
271 0 : &["revision", "build_tag"]
272 : )
273 0 : .expect("Failed to register build info metric");
274 0 : metric.with_label_values(&[revision, build_tag]).set(1);
275 0 : }
276 : const BYTES_IN_BLOCK: i64 = 512;
277 :
278 : // Records I/O stats in a "cross-platform" way.
279 : // Compiles both on macOS and Linux, but current macOS implementation always returns 0 as values for I/O stats.
280 : // An alternative is to read procfs (`/proc/[pid]/io`) which does not work under macOS at all, hence abandoned.
281 : //
282 : // Uses https://www.freebsd.org/cgi/man.cgi?query=getrusage to retrieve the number of block operations
283 : // performed by the process.
284 : // We know the size of the block, so we can determine the I/O bytes out of it.
285 : // The value might be not 100% exact, but should be fine for Prometheus metrics in this case.
286 0 : fn update_rusage_metrics() {
287 0 : let rusage_stats = get_rusage_stats();
288 :
289 0 : DISK_IO_BYTES
290 0 : .with_label_values(&["read"])
291 0 : .set(rusage_stats.ru_inblock * BYTES_IN_BLOCK);
292 0 : DISK_IO_BYTES
293 0 : .with_label_values(&["write"])
294 0 : .set(rusage_stats.ru_oublock * BYTES_IN_BLOCK);
295 :
296 : // On macOS, the unit of maxrss is bytes; on Linux, it's kilobytes. https://stackoverflow.com/a/59915669
297 : #[cfg(target_os = "macos")]
298 : {
299 : MAXRSS_KB.set(rusage_stats.ru_maxrss / 1024);
300 : }
301 : #[cfg(not(target_os = "macos"))]
302 0 : {
303 0 : MAXRSS_KB.set(rusage_stats.ru_maxrss);
304 0 : }
305 0 : }
306 :
307 0 : fn get_rusage_stats() -> libc::rusage {
308 0 : let mut rusage = std::mem::MaybeUninit::uninit();
309 :
310 : // SAFETY: kernel will initialize the struct for us
311 : unsafe {
312 0 : let ret = libc::getrusage(libc::RUSAGE_SELF, rusage.as_mut_ptr());
313 0 : assert!(ret == 0, "getrusage failed: bad args");
314 0 : rusage.assume_init()
315 : }
316 0 : }
317 :
318 : /// Create an [`IntCounterPairVec`] and registers to default registry.
319 : #[macro_export(local_inner_macros)]
320 : macro_rules! register_int_counter_pair_vec {
321 : ($NAME1:expr, $HELP1:expr, $NAME2:expr, $HELP2:expr, $LABELS_NAMES:expr $(,)?) => {{
322 : match (
323 : $crate::register_int_counter_vec!($NAME1, $HELP1, $LABELS_NAMES),
324 : $crate::register_int_counter_vec!($NAME2, $HELP2, $LABELS_NAMES),
325 : ) {
326 : (Ok(inc), Ok(dec)) => Ok($crate::IntCounterPairVec::new(inc, dec)),
327 : (Err(e), _) | (_, Err(e)) => Err(e),
328 : }
329 : }};
330 : }
331 :
332 : /// Create an [`IntCounterPair`] and registers to default registry.
333 : #[macro_export(local_inner_macros)]
334 : macro_rules! register_int_counter_pair {
335 : ($NAME1:expr, $HELP1:expr, $NAME2:expr, $HELP2:expr $(,)?) => {{
336 : match (
337 : $crate::register_int_counter!($NAME1, $HELP1),
338 : $crate::register_int_counter!($NAME2, $HELP2),
339 : ) {
340 : (Ok(inc), Ok(dec)) => Ok($crate::IntCounterPair::new(inc, dec)),
341 : (Err(e), _) | (_, Err(e)) => Err(e),
342 : }
343 : }};
344 : }
345 :
346 : /// A Pair of [`GenericCounterVec`]s. Like an [`GenericGaugeVec`] but will always observe changes
347 : pub struct GenericCounterPairVec<P: Atomic> {
348 : inc: GenericCounterVec<P>,
349 : dec: GenericCounterVec<P>,
350 : }
351 :
352 : /// A Pair of [`GenericCounter`]s. Like an [`GenericGauge`] but will always observe changes
353 : pub struct GenericCounterPair<P: Atomic> {
354 : inc: GenericCounter<P>,
355 : dec: GenericCounter<P>,
356 : }
357 :
358 : impl<P: Atomic> GenericCounterPairVec<P> {
359 226 : pub fn new(inc: GenericCounterVec<P>, dec: GenericCounterVec<P>) -> Self {
360 226 : Self { inc, dec }
361 0 : }
362 :
363 : /// `get_metric_with_label_values` returns the [`GenericCounterPair<P>`] for the given slice
364 : /// of label values (same order as the VariableLabels in Desc). If that combination of
365 : /// label values is accessed for the first time, a new [`GenericCounterPair<P>`] is created.
366 : ///
367 : /// An error is returned if the number of label values is not the same as the
368 : /// number of VariableLabels in Desc.
369 766 : pub fn get_metric_with_label_values(
370 766 : &self,
371 766 : vals: &[&str],
372 766 : ) -> prometheus::Result<GenericCounterPair<P>> {
373 : Ok(GenericCounterPair {
374 766 : inc: self.inc.get_metric_with_label_values(vals)?,
375 766 : dec: self.dec.get_metric_with_label_values(vals)?,
376 : })
377 0 : }
378 :
379 : /// `with_label_values` works as `get_metric_with_label_values`, but panics if an error
380 : /// occurs.
381 100 : pub fn with_label_values(&self, vals: &[&str]) -> GenericCounterPair<P> {
382 100 : self.get_metric_with_label_values(vals).unwrap()
383 0 : }
384 :
385 17 : pub fn remove_label_values(&self, res: &mut [prometheus::Result<()>; 2], vals: &[&str]) {
386 17 : res[0] = self.inc.remove_label_values(vals);
387 17 : res[1] = self.dec.remove_label_values(vals);
388 0 : }
389 : }
390 :
391 : impl<P: Atomic> GenericCounterPair<P> {
392 0 : pub fn new(inc: GenericCounter<P>, dec: GenericCounter<P>) -> Self {
393 0 : Self { inc, dec }
394 0 : }
395 :
396 : /// Increment the gauge by 1, returning a guard that decrements by 1 on drop.
397 113968 : pub fn guard(&self) -> GenericCounterPairGuard<P> {
398 113968 : self.inc.inc();
399 113968 : GenericCounterPairGuard(self.dec.clone())
400 0 : }
401 :
402 : /// Increment the gauge by n, returning a guard that decrements by n on drop.
403 0 : pub fn guard_by(&self, n: P::T) -> GenericCounterPairGuardBy<P> {
404 0 : self.inc.inc_by(n);
405 0 : GenericCounterPairGuardBy(self.dec.clone(), n)
406 0 : }
407 :
408 : /// Increase the gauge by 1.
409 : #[inline]
410 1970 : pub fn inc(&self) {
411 1970 : self.inc.inc();
412 0 : }
413 :
414 : /// Decrease the gauge by 1.
415 : #[inline]
416 1900 : pub fn dec(&self) {
417 1900 : self.dec.inc();
418 0 : }
419 :
420 : /// Add the given value to the gauge. (The value can be
421 : /// negative, resulting in a decrement of the gauge.)
422 : #[inline]
423 0 : pub fn inc_by(&self, v: P::T) {
424 0 : self.inc.inc_by(v);
425 0 : }
426 :
427 : /// Subtract the given value from the gauge. (The value can be
428 : /// negative, resulting in an increment of the gauge.)
429 : #[inline]
430 0 : pub fn dec_by(&self, v: P::T) {
431 0 : self.dec.inc_by(v);
432 0 : }
433 : }
434 :
435 : impl<P: Atomic> Clone for GenericCounterPair<P> {
436 3853 : fn clone(&self) -> Self {
437 3853 : Self {
438 3853 : inc: self.inc.clone(),
439 3853 : dec: self.dec.clone(),
440 3853 : }
441 0 : }
442 : }
443 :
444 : /// Guard returned by [`GenericCounterPair::guard`]
445 : pub struct GenericCounterPairGuard<P: Atomic>(GenericCounter<P>);
446 :
447 : impl<P: Atomic> Drop for GenericCounterPairGuard<P> {
448 113968 : fn drop(&mut self) {
449 113968 : self.0.inc();
450 0 : }
451 : }
452 : /// Guard returned by [`GenericCounterPair::guard_by`]
453 : pub struct GenericCounterPairGuardBy<P: Atomic>(GenericCounter<P>, P::T);
454 :
455 : impl<P: Atomic> Drop for GenericCounterPairGuardBy<P> {
456 0 : fn drop(&mut self) {
457 0 : self.0.inc_by(self.1);
458 0 : }
459 : }
460 :
461 : /// A Pair of [`IntCounterVec`]s. Like an [`IntGaugeVec`] but will always observe changes
462 : pub type IntCounterPairVec = GenericCounterPairVec<AtomicU64>;
463 :
464 : /// A Pair of [`IntCounter`]s. Like an [`IntGauge`] but will always observe changes
465 : pub type IntCounterPair = GenericCounterPair<AtomicU64>;
466 :
467 : /// A guard for [`IntCounterPair`] that will decrement the gauge on drop
468 : pub type IntCounterPairGuard = GenericCounterPairGuard<AtomicU64>;
469 :
470 : pub trait CounterPairAssoc {
471 : const INC_NAME: &'static MetricName;
472 : const DEC_NAME: &'static MetricName;
473 :
474 : const INC_HELP: &'static str;
475 : const DEC_HELP: &'static str;
476 :
477 : type LabelGroupSet: LabelGroupSet;
478 : }
479 :
480 : pub struct CounterPairVec<A: CounterPairAssoc> {
481 : vec: measured::metric::MetricVec<MeasuredCounterPairState, A::LabelGroupSet>,
482 : }
483 :
484 : impl<A: CounterPairAssoc> Default for CounterPairVec<A>
485 : where
486 : A::LabelGroupSet: Default,
487 : {
488 204 : fn default() -> Self {
489 204 : Self {
490 204 : vec: Default::default(),
491 204 : }
492 0 : }
493 : }
494 :
495 : impl<A: CounterPairAssoc> CounterPairVec<A> {
496 0 : pub fn guard(
497 0 : &self,
498 0 : labels: <A::LabelGroupSet as LabelGroupSet>::Group<'_>,
499 0 : ) -> MeasuredCounterPairGuard<'_, A> {
500 0 : let id = self.vec.with_labels(labels);
501 0 : self.vec.get_metric(id).inc.inc();
502 0 : MeasuredCounterPairGuard { vec: &self.vec, id }
503 0 : }
504 0 : pub fn inc(&self, labels: <A::LabelGroupSet as LabelGroupSet>::Group<'_>) {
505 0 : let id = self.vec.with_labels(labels);
506 0 : self.vec.get_metric(id).inc.inc();
507 0 : }
508 0 : pub fn dec(&self, labels: <A::LabelGroupSet as LabelGroupSet>::Group<'_>) {
509 0 : let id = self.vec.with_labels(labels);
510 0 : self.vec.get_metric(id).dec.inc();
511 0 : }
512 0 : pub fn remove_metric(
513 0 : &self,
514 0 : labels: <A::LabelGroupSet as LabelGroupSet>::Group<'_>,
515 0 : ) -> Option<MeasuredCounterPairState> {
516 0 : let id = self.vec.with_labels(labels);
517 0 : self.vec.remove_metric(id)
518 0 : }
519 :
520 0 : pub fn sample(&self, labels: <A::LabelGroupSet as LabelGroupSet>::Group<'_>) -> u64 {
521 0 : let id = self.vec.with_labels(labels);
522 0 : let metric = self.vec.get_metric(id);
523 :
524 0 : let inc = metric.inc.count.load(std::sync::atomic::Ordering::Relaxed);
525 0 : let dec = metric.dec.count.load(std::sync::atomic::Ordering::Relaxed);
526 0 : inc.saturating_sub(dec)
527 0 : }
528 : }
529 :
530 : impl<T, A> ::measured::metric::group::MetricGroup<T> for CounterPairVec<A>
531 : where
532 : T: ::measured::metric::group::Encoding,
533 : A: CounterPairAssoc,
534 : ::measured::metric::counter::CounterState: ::measured::metric::MetricEncoding<T>,
535 : {
536 0 : fn collect_group_into(&self, enc: &mut T) -> Result<(), T::Err> {
537 : // write decrement first to avoid a race condition where inc - dec < 0
538 0 : T::write_help(enc, A::DEC_NAME, A::DEC_HELP)?;
539 0 : self.vec
540 0 : .collect_family_into(A::DEC_NAME, &mut Dec(&mut *enc))?;
541 :
542 0 : T::write_help(enc, A::INC_NAME, A::INC_HELP)?;
543 0 : self.vec
544 0 : .collect_family_into(A::INC_NAME, &mut Inc(&mut *enc))?;
545 :
546 0 : Ok(())
547 0 : }
548 : }
549 :
550 : #[derive(MetricGroup, Default)]
551 : pub struct MeasuredCounterPairState {
552 : pub inc: CounterState,
553 : pub dec: CounterState,
554 : }
555 :
556 : impl measured::metric::MetricType for MeasuredCounterPairState {
557 : type Metadata = ();
558 : }
559 :
560 : pub struct MeasuredCounterPairGuard<'a, A: CounterPairAssoc> {
561 : vec: &'a measured::metric::MetricVec<MeasuredCounterPairState, A::LabelGroupSet>,
562 : id: measured::metric::LabelId<A::LabelGroupSet>,
563 : }
564 :
565 : impl<A: CounterPairAssoc> Drop for MeasuredCounterPairGuard<'_, A> {
566 0 : fn drop(&mut self) {
567 0 : self.vec.get_metric(self.id).dec.inc();
568 0 : }
569 : }
570 :
571 : /// [`MetricEncoding`] for [`MeasuredCounterPairState`] that only writes the inc counter to the inner encoder.
572 : struct Inc<T>(T);
573 : /// [`MetricEncoding`] for [`MeasuredCounterPairState`] that only writes the dec counter to the inner encoder.
574 : struct Dec<T>(T);
575 :
576 : impl<T: Encoding> Encoding for Inc<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 :
584 : impl<T: Encoding> MetricEncoding<Inc<T>> for MeasuredCounterPairState
585 : where
586 : CounterState: MetricEncoding<T>,
587 : {
588 0 : fn write_type(name: impl MetricNameEncoder, enc: &mut Inc<T>) -> Result<(), T::Err> {
589 0 : CounterState::write_type(name, &mut enc.0)
590 0 : }
591 0 : fn collect_into(
592 0 : &self,
593 0 : metadata: &(),
594 0 : labels: impl LabelGroup,
595 0 : name: impl MetricNameEncoder,
596 0 : enc: &mut Inc<T>,
597 0 : ) -> Result<(), T::Err> {
598 0 : self.inc.collect_into(metadata, labels, name, &mut enc.0)
599 0 : }
600 : }
601 :
602 : impl<T: Encoding> Encoding for Dec<T> {
603 : type Err = T::Err;
604 :
605 0 : fn write_help(&mut self, name: impl MetricNameEncoder, help: &str) -> Result<(), Self::Err> {
606 0 : self.0.write_help(name, help)
607 0 : }
608 : }
609 :
610 : /// Write the dec counter to the encoder
611 : impl<T: Encoding> MetricEncoding<Dec<T>> for MeasuredCounterPairState
612 : where
613 : CounterState: MetricEncoding<T>,
614 : {
615 0 : fn write_type(name: impl MetricNameEncoder, enc: &mut Dec<T>) -> Result<(), T::Err> {
616 0 : CounterState::write_type(name, &mut enc.0)
617 0 : }
618 0 : fn collect_into(
619 0 : &self,
620 0 : metadata: &(),
621 0 : labels: impl LabelGroup,
622 0 : name: impl MetricNameEncoder,
623 0 : enc: &mut Dec<T>,
624 0 : ) -> Result<(), T::Err> {
625 0 : self.dec.collect_into(metadata, labels, name, &mut enc.0)
626 0 : }
627 : }
628 :
629 : #[cfg(test)]
630 : mod tests {
631 : use super::*;
632 :
633 : const POW2_BUCKETS_MAX: usize = 1 << (usize::BITS - 1);
634 :
635 : #[test]
636 1 : fn pow2_buckets_cases() {
637 1 : assert_eq!(pow2_buckets(1, 1), vec![1.0]);
638 1 : assert_eq!(pow2_buckets(1, 2), vec![1.0, 2.0]);
639 1 : assert_eq!(pow2_buckets(1, 3), vec![1.0, 2.0, 4.0]);
640 1 : assert_eq!(pow2_buckets(1, 4), vec![1.0, 2.0, 4.0]);
641 1 : assert_eq!(pow2_buckets(1, 5), vec![1.0, 2.0, 4.0, 8.0]);
642 1 : assert_eq!(pow2_buckets(1, 6), vec![1.0, 2.0, 4.0, 8.0]);
643 1 : assert_eq!(pow2_buckets(1, 7), vec![1.0, 2.0, 4.0, 8.0]);
644 1 : assert_eq!(pow2_buckets(1, 8), vec![1.0, 2.0, 4.0, 8.0]);
645 1 : assert_eq!(
646 1 : pow2_buckets(1, 200),
647 1 : vec![1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0]
648 : );
649 :
650 1 : assert_eq!(pow2_buckets(1, 8), vec![1.0, 2.0, 4.0, 8.0]);
651 1 : assert_eq!(pow2_buckets(2, 8), vec![2.0, 4.0, 8.0]);
652 1 : assert_eq!(pow2_buckets(3, 8), vec![2.0, 4.0, 8.0]);
653 1 : assert_eq!(pow2_buckets(4, 8), vec![4.0, 8.0]);
654 1 : assert_eq!(pow2_buckets(5, 8), vec![4.0, 8.0]);
655 1 : assert_eq!(pow2_buckets(6, 8), vec![4.0, 8.0]);
656 1 : assert_eq!(pow2_buckets(7, 8), vec![4.0, 8.0]);
657 1 : assert_eq!(pow2_buckets(8, 8), vec![8.0]);
658 1 : assert_eq!(pow2_buckets(20, 200), vec![16.0, 32.0, 64.0, 128.0, 256.0]);
659 :
660 : // Largest valid values.
661 1 : assert_eq!(
662 1 : pow2_buckets(1, POW2_BUCKETS_MAX).len(),
663 : usize::BITS as usize
664 : );
665 1 : assert_eq!(pow2_buckets(POW2_BUCKETS_MAX, POW2_BUCKETS_MAX).len(), 1);
666 1 : }
667 :
668 : #[test]
669 : #[should_panic]
670 1 : fn pow2_buckets_zero_start() {
671 1 : pow2_buckets(0, 1);
672 1 : }
673 :
674 : #[test]
675 : #[should_panic]
676 1 : fn pow2_buckets_end_lt_start() {
677 1 : pow2_buckets(2, 1);
678 1 : }
679 :
680 : #[test]
681 : #[should_panic]
682 1 : fn pow2_buckets_end_overflow_min() {
683 1 : pow2_buckets(1, POW2_BUCKETS_MAX + 1);
684 1 : }
685 :
686 : #[test]
687 : #[should_panic]
688 1 : fn pow2_buckets_end_overflow_max() {
689 1 : pow2_buckets(1, usize::MAX);
690 1 : }
691 : }
|