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