prometheus/
counter.rs

1// Copyright 2014 The Prometheus Authors
2// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.
3
4use std::cell::RefCell;
5use std::collections::HashMap;
6use std::marker::PhantomData;
7use std::sync::Arc;
8
9use crate::atomic64::{Atomic, AtomicF64, AtomicU64, Number};
10use crate::desc::Desc;
11use crate::errors::Result;
12use crate::metrics::{Collector, LocalMetric, Metric, Opts};
13use crate::proto;
14use crate::value::{Value, ValueType};
15use crate::vec::{MetricVec, MetricVecBuilder};
16
17/// The underlying implementation for [`Counter`] and [`IntCounter`].
18#[derive(Debug)]
19pub struct GenericCounter<P: Atomic> {
20    v: Arc<Value<P>>,
21}
22
23/// A [`Metric`] represents a single numerical value that only ever goes up.
24pub type Counter = GenericCounter<AtomicF64>;
25
26/// The integer version of [`Counter`]. Provides better performance if metric values
27/// are all positive integers (natural numbers).
28pub type IntCounter = GenericCounter<AtomicU64>;
29
30impl<P: Atomic> Clone for GenericCounter<P> {
31    fn clone(&self) -> Self {
32        Self {
33            v: Arc::clone(&self.v),
34        }
35    }
36}
37
38impl<P: Atomic> GenericCounter<P> {
39    /// Create a [`GenericCounter`] with the `name` and `help` arguments.
40    pub fn new<S1: Into<String>, S2: Into<String>>(name: S1, help: S2) -> Result<Self> {
41        let opts = Opts::new(name, help);
42        Self::with_opts(opts)
43    }
44
45    /// Create a [`GenericCounter`] with the `opts` options.
46    pub fn with_opts(opts: Opts) -> Result<Self> {
47        Self::with_opts_and_label_values::<&str>(&opts, &[])
48    }
49
50    fn with_opts_and_label_values<V: AsRef<str>>(opts: &Opts, label_values: &[V]) -> Result<Self> {
51        let v = Value::new(opts, ValueType::Counter, P::T::from_i64(0), label_values)?;
52        Ok(Self { v: Arc::new(v) })
53    }
54
55    /// Increase the given value to the counter.
56    ///
57    /// # Panics
58    ///
59    /// Panics in debug build if the value is < 0.
60    #[inline]
61    pub fn inc_by(&self, v: P::T) {
62        debug_assert!(v >= P::T::from_i64(0));
63        self.v.inc_by(v);
64    }
65
66    /// Increase the counter by 1.
67    #[inline]
68    pub fn inc(&self) {
69        self.v.inc();
70    }
71
72    /// Return the counter value.
73    #[inline]
74    pub fn get(&self) -> P::T {
75        self.v.get()
76    }
77
78    /// Restart the counter, resetting its value back to 0.
79    #[inline]
80    pub fn reset(&self) {
81        self.v.set(P::T::from_i64(0))
82    }
83
84    /// Return a [`GenericLocalCounter`] for single thread usage.
85    pub fn local(&self) -> GenericLocalCounter<P> {
86        GenericLocalCounter::new(self.clone())
87    }
88}
89
90impl<P: Atomic> Collector for GenericCounter<P> {
91    fn desc(&self) -> Vec<&Desc> {
92        vec![&self.v.desc]
93    }
94
95    fn collect(&self) -> Vec<proto::MetricFamily> {
96        vec![self.v.collect()]
97    }
98}
99
100impl<P: Atomic> Metric for GenericCounter<P> {
101    fn metric(&self) -> proto::Metric {
102        self.v.metric()
103    }
104}
105
106#[derive(Debug)]
107pub struct CounterVecBuilder<P: Atomic> {
108    _phantom: PhantomData<P>,
109}
110
111impl<P: Atomic> CounterVecBuilder<P> {
112    pub fn new() -> Self {
113        Self {
114            _phantom: PhantomData,
115        }
116    }
117}
118
119impl<P: Atomic> Clone for CounterVecBuilder<P> {
120    fn clone(&self) -> Self {
121        Self::new()
122    }
123}
124
125impl<P: Atomic> MetricVecBuilder for CounterVecBuilder<P> {
126    type M = GenericCounter<P>;
127    type P = Opts;
128
129    fn build<V: AsRef<str>>(&self, opts: &Opts, vals: &[V]) -> Result<Self::M> {
130        Self::M::with_opts_and_label_values(opts, vals)
131    }
132}
133
134/// The underlying implementation for [`CounterVec`] and [`IntCounterVec`].
135pub type GenericCounterVec<P> = MetricVec<CounterVecBuilder<P>>;
136
137/// A [`Collector`] that bundles a set of [`Counter`]s that all share
138/// the same [`Desc`], but have different values for their variable labels. This is
139/// used if you want to count the same thing partitioned by various dimensions
140/// (e.g. number of HTTP requests, partitioned by response code and method).
141pub type CounterVec = GenericCounterVec<AtomicF64>;
142
143/// The integer version of [`CounterVec`]. Provides better performance if metric
144/// are all positive integers (natural numbers).
145pub type IntCounterVec = GenericCounterVec<AtomicU64>;
146
147impl<P: Atomic> GenericCounterVec<P> {
148    /// Create a new [`GenericCounterVec`] based on the provided
149    /// [`Opts`] and partitioned by the given label names. At least one label name must be
150    /// provided.
151    pub fn new(opts: Opts, label_names: &[&str]) -> Result<Self> {
152        let variable_names = label_names.iter().map(|s| (*s).to_owned()).collect();
153        let opts = opts.variable_labels(variable_names);
154        let metric_vec =
155            MetricVec::create(proto::MetricType::COUNTER, CounterVecBuilder::new(), opts)?;
156
157        Ok(metric_vec as Self)
158    }
159
160    /// Return a [`GenericLocalCounterVec`] for single thread usage.
161    pub fn local(&self) -> GenericLocalCounterVec<P> {
162        GenericLocalCounterVec::new(self.clone())
163    }
164}
165
166/// The underlying implementation for [`LocalCounter`]
167/// and [`LocalIntCounter`].
168#[derive(Debug)]
169pub struct GenericLocalCounter<P: Atomic> {
170    counter: GenericCounter<P>,
171    val: RefCell<P::T>,
172}
173
174/// For auto_flush::AFLocalCounter to use to make type inference possible
175pub trait CounterWithValueType {
176    ///the exact type which implements Atomic
177    type ValueType: Atomic;
178}
179
180impl<P: Atomic> CounterWithValueType for GenericLocalCounter<P> {
181    type ValueType = P;
182}
183
184/// An unsync [`Counter`].
185pub type LocalCounter = GenericLocalCounter<AtomicF64>;
186
187/// The integer version of [`LocalCounter`]. Provides better performance
188/// are all positive integers (natural numbers).
189pub type LocalIntCounter = GenericLocalCounter<AtomicU64>;
190
191impl<P: Atomic> GenericLocalCounter<P> {
192    fn new(counter: GenericCounter<P>) -> Self {
193        Self {
194            counter,
195            val: RefCell::new(P::T::from_i64(0)),
196        }
197    }
198
199    /// Increase the given value to the local counter.
200    ///
201    /// # Panics
202    ///
203    /// Panics in debug build if the value is < 0.
204    #[inline]
205    pub fn inc_by(&self, v: P::T) {
206        debug_assert!(v >= P::T::from_i64(0));
207        *self.val.borrow_mut() += v;
208    }
209
210    /// Increase the local counter by 1.
211    #[inline]
212    pub fn inc(&self) {
213        *self.val.borrow_mut() += P::T::from_i64(1);
214    }
215
216    /// Return the local counter value.
217    #[inline]
218    pub fn get(&self) -> P::T {
219        *self.val.borrow()
220    }
221
222    /// Restart the counter, resetting its value back to 0.
223    #[inline]
224    pub fn reset(&self) {
225        *self.val.borrow_mut() = P::T::from_i64(0);
226    }
227
228    /// Flush the local metrics to the [`Counter`].
229    #[inline]
230    pub fn flush(&self) {
231        if *self.val.borrow() == P::T::from_i64(0) {
232            return;
233        }
234        self.counter.inc_by(*self.val.borrow());
235        *self.val.borrow_mut() = P::T::from_i64(0);
236    }
237}
238
239impl<P: Atomic> LocalMetric for GenericLocalCounter<P> {
240    /// Flush the local metrics to the [`Counter`].
241    #[inline]
242    fn flush(&self) {
243        GenericLocalCounter::flush(self);
244    }
245}
246
247impl<P: Atomic> Clone for GenericLocalCounter<P> {
248    fn clone(&self) -> Self {
249        Self::new(self.counter.clone())
250    }
251}
252
253/// The underlying implementation for [`LocalCounterVec`]
254/// and [`LocalIntCounterVec`].
255pub struct GenericLocalCounterVec<P: Atomic> {
256    vec: GenericCounterVec<P>,
257    local: HashMap<u64, GenericLocalCounter<P>>,
258}
259
260impl<P: Atomic> std::fmt::Debug for GenericLocalCounterVec<P> {
261    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
262        write!(
263            f,
264            "GenericLocalCounterVec ({} locals)",
265            self.local.keys().len()
266        )
267    }
268}
269
270/// An unsync [`CounterVec`].
271pub type LocalCounterVec = GenericLocalCounterVec<AtomicF64>;
272
273/// The integer version of [`LocalCounterVec`].
274/// Provides better performance if metric values are all positive
275/// integers (natural numbers).
276pub type LocalIntCounterVec = GenericLocalCounterVec<AtomicU64>;
277
278impl<P: Atomic> GenericLocalCounterVec<P> {
279    fn new(vec: GenericCounterVec<P>) -> Self {
280        let local = HashMap::with_capacity(vec.v.children.read().len());
281        Self { vec, local }
282    }
283
284    /// Get a [`GenericLocalCounter`] by label values.
285    /// See more [MetricVec::with_label_values].
286    pub fn with_label_values<'a>(&'a mut self, vals: &[&str]) -> &'a mut GenericLocalCounter<P> {
287        let hash = self.vec.v.hash_label_values(vals).unwrap();
288        let vec = &self.vec;
289        self.local
290            .entry(hash)
291            .or_insert_with(|| vec.with_label_values(vals).local())
292    }
293
294    /// Remove a [`GenericLocalCounter`] by label values.
295    /// See more [MetricVec::remove_label_values].
296    pub fn remove_label_values(&mut self, vals: &[&str]) -> Result<()> {
297        let hash = self.vec.v.hash_label_values(vals)?;
298        self.local.remove(&hash);
299        self.vec.v.delete_label_values(vals)
300    }
301
302    /// Flush the local metrics to the [`CounterVec`] metric.
303    pub fn flush(&self) {
304        for h in self.local.values() {
305            h.flush();
306        }
307    }
308}
309
310impl<P: Atomic> LocalMetric for GenericLocalCounterVec<P> {
311    /// Flush the local metrics to the [`CounterVec`] metric.
312    fn flush(&self) {
313        GenericLocalCounterVec::flush(self);
314    }
315}
316
317impl<P: Atomic> Clone for GenericLocalCounterVec<P> {
318    fn clone(&self) -> Self {
319        Self::new(self.vec.clone())
320    }
321}
322
323#[cfg(test)]
324mod tests {
325    use std::collections::HashMap;
326    use std::f64;
327
328    use super::*;
329    use crate::metrics::{Collector, Opts};
330    #[cfg(feature = "protobuf")]
331    use crate::proto_ext::MessageFieldExt;
332
333    #[test]
334    fn test_counter() {
335        let opts = Opts::new("test", "test help")
336            .const_label("a", "1")
337            .const_label("b", "2");
338        let counter = Counter::with_opts(opts).unwrap();
339        counter.inc();
340        assert_eq!(counter.get() as u64, 1);
341        counter.inc_by(42.0);
342        assert_eq!(counter.get() as u64, 43);
343
344        let mut mfs = counter.collect();
345        assert_eq!(mfs.len(), 1);
346
347        let mf = mfs.pop().unwrap();
348        let m = mf.get_metric().first().unwrap();
349        assert_eq!(m.get_label().len(), 2);
350        assert_eq!(m.get_counter().get_value() as u64, 43);
351
352        counter.reset();
353        assert_eq!(counter.get() as u64, 0);
354    }
355
356    #[test]
357    fn test_int_counter() {
358        let counter = IntCounter::new("foo", "bar").unwrap();
359        counter.inc();
360        assert_eq!(counter.get(), 1);
361        counter.inc_by(11);
362        assert_eq!(counter.get(), 12);
363
364        let mut mfs = counter.collect();
365        assert_eq!(mfs.len(), 1);
366
367        let mf = mfs.pop().unwrap();
368        let m = mf.get_metric().first().unwrap();
369        assert_eq!(m.get_label().len(), 0);
370        assert_eq!(m.get_counter().get_value() as u64, 12);
371
372        counter.reset();
373        assert_eq!(counter.get(), 0);
374    }
375
376    #[test]
377    fn test_local_counter() {
378        let counter = Counter::new("counter", "counter helper").unwrap();
379        let local_counter1 = counter.local();
380        let local_counter2 = counter.local();
381
382        local_counter1.inc();
383        local_counter2.inc();
384        assert_eq!(local_counter1.get() as u64, 1);
385        assert_eq!(local_counter2.get() as u64, 1);
386        assert_eq!(counter.get() as u64, 0);
387        local_counter1.flush();
388        assert_eq!(local_counter1.get() as u64, 0);
389        assert_eq!(counter.get() as u64, 1);
390        local_counter2.flush();
391        assert_eq!(counter.get() as u64, 2);
392
393        local_counter1.reset();
394        local_counter2.reset();
395        counter.reset();
396        assert_eq!(counter.get() as u64, 0);
397        local_counter1.flush();
398        assert_eq!(counter.get() as u64, 0);
399        local_counter2.flush();
400        assert_eq!(counter.get() as u64, 0);
401    }
402
403    #[test]
404    fn test_int_local_counter() {
405        let counter = IntCounter::new("foo", "bar").unwrap();
406        let local_counter = counter.local();
407
408        local_counter.inc();
409        assert_eq!(local_counter.get(), 1);
410        assert_eq!(counter.get(), 0);
411
412        local_counter.inc_by(5);
413        local_counter.flush();
414        assert_eq!(local_counter.get(), 0);
415        assert_eq!(counter.get(), 6);
416
417        local_counter.reset();
418        counter.reset();
419        assert_eq!(counter.get(), 0);
420        local_counter.flush();
421        assert_eq!(counter.get(), 0);
422    }
423
424    #[test]
425    fn test_counter_vec_with_labels() {
426        let vec = CounterVec::new(
427            Opts::new("test_couter_vec", "test counter vec help"),
428            &["l1", "l2"],
429        )
430        .unwrap();
431
432        let mut labels = HashMap::new();
433        labels.insert("l1", "v1");
434        labels.insert("l2", "v2");
435        assert!(vec.remove(&labels).is_err());
436
437        vec.with(&labels).inc();
438        assert!(vec.remove(&labels).is_ok());
439        assert!(vec.remove(&labels).is_err());
440
441        let mut labels2 = HashMap::new();
442        labels2.insert("l1", "v2");
443        labels2.insert("l2", "v1");
444
445        vec.with(&labels).inc();
446        assert!(vec.remove(&labels2).is_err());
447
448        vec.with(&labels).inc();
449
450        let mut labels3 = HashMap::new();
451        labels3.insert("l1", "v1");
452        assert!(vec.remove(&labels3).is_err());
453    }
454
455    #[test]
456    fn test_counter_vec_with_owned_labels() {
457        let vec = CounterVec::new(
458            Opts::new("test_couter_vec", "test counter vec help"),
459            &["l1", "l2"],
460        )
461        .unwrap();
462
463        let v1 = "v1".to_string();
464        let v2 = "v2".to_string();
465
466        let mut labels = HashMap::new();
467        labels.insert("l1", v1.clone());
468        labels.insert("l2", v2.clone());
469        assert!(vec.remove(&labels).is_err());
470
471        vec.with(&labels).inc();
472        assert!(vec.remove(&labels).is_ok());
473        assert!(vec.remove(&labels).is_err());
474
475        let mut labels2 = HashMap::new();
476        labels2.insert("l1", v2.clone());
477        labels2.insert("l2", v1.clone());
478
479        vec.with(&labels).inc();
480        assert!(vec.remove(&labels2).is_err());
481
482        vec.with(&labels).inc();
483
484        let mut labels3 = HashMap::new();
485        labels3.insert("l1", v1.clone());
486        assert!(vec.remove(&labels3).is_err());
487    }
488
489    #[test]
490    fn test_int_counter_vec() {
491        let vec = IntCounterVec::new(Opts::new("foo", "bar"), &["l1", "l2"]).unwrap();
492
493        vec.with_label_values(&["v1", "v3"]).inc();
494        assert_eq!(vec.with_label_values(&["v1", "v3"]).get(), 1);
495
496        vec.with_label_values(&["v1", "v2"]).inc_by(12);
497        assert_eq!(vec.with_label_values(&["v1", "v3"]).get(), 1);
498        assert_eq!(vec.with_label_values(&["v1", "v2"]).get(), 12);
499
500        vec.with_label_values(&["v4", "v2"]).inc_by(2);
501        assert_eq!(vec.with_label_values(&["v1", "v3"]).get(), 1);
502        assert_eq!(vec.with_label_values(&["v1", "v2"]).get(), 12);
503        assert_eq!(vec.with_label_values(&["v4", "v2"]).get(), 2);
504
505        vec.with_label_values(&["v1", "v3"]).inc_by(5);
506        assert_eq!(vec.with_label_values(&["v1", "v3"]).get(), 6);
507        assert_eq!(vec.with_label_values(&["v1", "v2"]).get(), 12);
508        assert_eq!(vec.with_label_values(&["v4", "v2"]).get(), 2);
509    }
510
511    #[test]
512    fn test_counter_vec_with_label_values() {
513        let vec = CounterVec::new(
514            Opts::new("test_vec", "test counter vec help"),
515            &["l1", "l2"],
516        )
517        .unwrap();
518
519        assert!(vec.remove_label_values(&["v1", "v2"]).is_err());
520        vec.with_label_values(&["v1", "v2"]).inc();
521        assert!(vec.remove_label_values(&["v1", "v2"]).is_ok());
522
523        vec.with_label_values(&["v1", "v2"]).inc();
524        assert!(vec.remove_label_values(&["v1"]).is_err());
525        assert!(vec.remove_label_values(&["v1", "v3"]).is_err());
526    }
527
528    #[test]
529    fn test_counter_vec_with_owned_label_values() {
530        let vec = CounterVec::new(
531            Opts::new("test_vec", "test counter vec help"),
532            &["l1", "l2"],
533        )
534        .unwrap();
535
536        let v1 = "v1".to_string();
537        let v2 = "v2".to_string();
538        let v3 = "v3".to_string();
539
540        assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_err());
541        vec.with_label_values(&[v1.clone(), v2.clone()]).inc();
542        assert!(vec.remove_label_values(&[v1.clone(), v2.clone()]).is_ok());
543
544        vec.with_label_values(&[v1.clone(), v2.clone()]).inc();
545        assert!(vec.remove_label_values(&[v1.clone()]).is_err());
546        assert!(vec.remove_label_values(&[v1.clone(), v3.clone()]).is_err());
547    }
548
549    #[test]
550    fn test_counter_vec_local() {
551        let vec = CounterVec::new(
552            Opts::new("test_vec_local", "test counter vec help"),
553            &["l1", "l2"],
554        )
555        .unwrap();
556        let mut local_vec_1 = vec.local();
557        let mut local_vec_2 = local_vec_1.clone();
558
559        assert!(local_vec_1.remove_label_values(&["v1", "v2"]).is_err());
560
561        local_vec_1.with_label_values(&["v1", "v2"]).inc_by(23.0);
562        assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 23.0) <= f64::EPSILON);
563        assert!((vec.with_label_values(&["v1", "v2"]).get() - 0.0) <= f64::EPSILON);
564
565        local_vec_1.flush();
566        assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 0.0) <= f64::EPSILON);
567        assert!((vec.with_label_values(&["v1", "v2"]).get() - 23.0) <= f64::EPSILON);
568
569        local_vec_1.flush();
570        assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 0.0) <= f64::EPSILON);
571        assert!((vec.with_label_values(&["v1", "v2"]).get() - 23.0) <= f64::EPSILON);
572
573        local_vec_1.with_label_values(&["v1", "v2"]).inc_by(11.0);
574        assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 11.0) <= f64::EPSILON);
575        assert!((vec.with_label_values(&["v1", "v2"]).get() - 23.0) <= f64::EPSILON);
576
577        local_vec_1.flush();
578        assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 0.0) <= f64::EPSILON);
579        assert!((vec.with_label_values(&["v1", "v2"]).get() - 34.0) <= f64::EPSILON);
580
581        // When calling `remove_label_values`, it is "flushed" immediately.
582        assert!(local_vec_1.remove_label_values(&["v1", "v2"]).is_ok());
583        assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 0.0) <= f64::EPSILON);
584        assert!((vec.with_label_values(&["v1", "v2"]).get() - 0.0) <= f64::EPSILON);
585
586        local_vec_1.with_label_values(&["v1", "v2"]).inc();
587        assert!(local_vec_1.remove_label_values(&["v1"]).is_err());
588        assert!(local_vec_1.remove_label_values(&["v1", "v3"]).is_err());
589
590        local_vec_1.with_label_values(&["v1", "v2"]).inc_by(13.0);
591        assert!((local_vec_1.with_label_values(&["v1", "v2"]).get() - 14.0) <= f64::EPSILON);
592        assert!((vec.with_label_values(&["v1", "v2"]).get() - 0.0) <= f64::EPSILON);
593
594        local_vec_2.with_label_values(&["v1", "v2"]).inc_by(7.0);
595        assert!((local_vec_2.with_label_values(&["v1", "v2"]).get() - 7.0) <= f64::EPSILON);
596
597        local_vec_1.flush();
598        local_vec_2.flush();
599        assert!((vec.with_label_values(&["v1", "v2"]).get() - 21.0) <= f64::EPSILON);
600
601        local_vec_1.flush();
602        local_vec_2.flush();
603        assert!((vec.with_label_values(&["v1", "v2"]).get() - 21.0) <= f64::EPSILON);
604    }
605
606    #[test]
607    fn test_int_counter_vec_local() {
608        let vec = IntCounterVec::new(Opts::new("foo", "bar"), &["l1", "l2"]).unwrap();
609        let mut local_vec_1 = vec.local();
610        assert!(local_vec_1.remove_label_values(&["v1", "v2"]).is_err());
611
612        local_vec_1.with_label_values(&["v1", "v2"]).inc_by(23);
613        assert_eq!(local_vec_1.with_label_values(&["v1", "v2"]).get(), 23);
614        assert_eq!(vec.with_label_values(&["v1", "v2"]).get(), 0);
615
616        local_vec_1.flush();
617        assert_eq!(local_vec_1.with_label_values(&["v1", "v2"]).get(), 0);
618        assert_eq!(vec.with_label_values(&["v1", "v2"]).get(), 23);
619
620        local_vec_1.flush();
621        assert_eq!(local_vec_1.with_label_values(&["v1", "v2"]).get(), 0);
622        assert_eq!(vec.with_label_values(&["v1", "v2"]).get(), 23);
623
624        local_vec_1.with_label_values(&["v1", "v2"]).inc_by(11);
625        assert_eq!(local_vec_1.with_label_values(&["v1", "v2"]).get(), 11);
626        assert_eq!(vec.with_label_values(&["v1", "v2"]).get(), 23);
627
628        local_vec_1.flush();
629        assert_eq!(local_vec_1.with_label_values(&["v1", "v2"]).get(), 0);
630        assert_eq!(vec.with_label_values(&["v1", "v2"]).get(), 34);
631    }
632
633    #[cfg(debug_assertions)]
634    #[test]
635    #[should_panic(expected = "assertion failed")]
636    fn test_counter_negative_inc() {
637        let counter = Counter::new("foo", "bar").unwrap();
638        counter.inc_by(-42.0);
639    }
640
641    #[cfg(debug_assertions)]
642    #[test]
643    #[should_panic(expected = "assertion failed")]
644    fn test_local_counter_negative_inc() {
645        let counter = Counter::new("foo", "bar").unwrap();
646        let local = counter.local();
647        local.inc_by(-42.0);
648    }
649}