metrics_runtime/
sink.rs

1use crate::{
2    common::{Delta, Identifier, Kind, Measurement, Scope, ScopeHandle, ValueHandle},
3    data::{Counter, Gauge, Histogram},
4    registry::{MetricRegistry, ScopeRegistry},
5};
6use metrics_core::{IntoLabels, Key, Label, ScopedString};
7use quanta::Clock;
8use std::{collections::HashMap, error::Error, fmt, sync::Arc};
9
10/// Errors during sink creation.
11#[derive(Debug, Clone)]
12pub enum SinkError {
13    /// The scope value given was invalid i.e. empty or illegal characters.
14    InvalidScope,
15}
16
17impl Error for SinkError {}
18
19impl fmt::Display for SinkError {
20    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
21        match self {
22            SinkError::InvalidScope => write!(f, "given scope is invalid"),
23        }
24    }
25}
26
27/// A value that can be used as a metric scope.
28///
29/// This helper trait allows us to accept either a single string or a slice of strings to use as a
30/// scope, to avoid needing to allocate in the case where we want to be able to specify multiple
31/// scope levels in a single go.
32pub trait AsScoped<'a> {
33    /// Creates a new [`Scope`] by adding `self` to the `base` scope.
34    fn as_scoped(&'a self, base: Scope) -> Scope;
35}
36
37/// Handle for sending metric samples.
38#[derive(Debug)]
39pub struct Sink {
40    metric_registry: Arc<MetricRegistry>,
41    metric_cache: HashMap<Identifier, ValueHandle>,
42    scope_registry: Arc<ScopeRegistry>,
43    scope: Scope,
44    scope_handle: ScopeHandle,
45    clock: Clock,
46    default_labels: Vec<Label>,
47}
48
49impl Sink {
50    pub(crate) fn new(
51        metric_registry: Arc<MetricRegistry>,
52        scope_registry: Arc<ScopeRegistry>,
53        scope: Scope,
54        clock: Clock,
55    ) -> Sink {
56        let scope_handle = scope_registry.register(scope.clone());
57
58        Sink {
59            metric_registry,
60            metric_cache: HashMap::default(),
61            scope_registry,
62            scope,
63            scope_handle,
64            clock,
65            default_labels: Vec::new(),
66        }
67    }
68
69    /// Adds default labels for this sink and any derived sinks.
70    ///
71    /// Default labels are added to all metrics.  If a metric is updated and requested and it has
72    /// its own labels specified, the default labels will be appended to the existing labels.
73    ///
74    /// Labels are passed on, with scope, to any scoped children or cloned sinks.
75    pub fn add_default_labels<L>(&mut self, labels: L)
76    where
77        L: IntoLabels,
78    {
79        let labels = labels.into_labels();
80        self.default_labels.extend(labels);
81    }
82
83    /// Creates a scoped clone of this [`Sink`].
84    ///
85    /// Scoping controls the resulting metric name for any metrics sent by this [`Sink`].  For
86    /// example, you might have a metric called `messages_sent`.
87    ///
88    /// With scoping, you could have independent versions of the same metric.  This is useful for
89    /// having the same "base" metric name but with broken down values.
90    ///
91    /// Going further with the above example, if you had a server, and listened on multiple
92    /// addresses, maybe you would have a scoped [`Sink`] per listener, and could end up with
93    /// metrics that look like this:
94    /// - `listener.a.messages_sent`
95    /// - `listener.b.messages_sent`
96    /// - `listener.c.messages_sent`
97    /// - etc
98    ///
99    /// Scopes are also inherited.  If you create a scoped [`Sink`] from another [`Sink`] which is
100    /// already scoped, the scopes will be merged together using a `.` as the string separator.
101    /// This makes it easy to nest scopes.  Cloning a scoped [`Sink`], though, will inherit the
102    /// same scope as the original.
103    pub fn scoped<'a, S: AsScoped<'a> + ?Sized>(&self, scope: &'a S) -> Sink {
104        let new_scope = scope.as_scoped(self.scope.clone());
105
106        let mut sink = Sink::new(
107            self.metric_registry.clone(),
108            self.scope_registry.clone(),
109            new_scope,
110            self.clock.clone(),
111        );
112        if !self.default_labels.is_empty() {
113            sink.add_default_labels(self.default_labels.clone());
114        }
115
116        sink
117    }
118
119    /// Gets the current time, in nanoseconds, from the internal high-speed clock.
120    pub fn now(&self) -> u64 {
121        self.clock.now()
122    }
123
124    /// Increment a value for a counter identified by the given name.
125    ///
126    /// # Examples
127    ///
128    /// ```rust
129    /// # extern crate metrics_runtime;
130    /// # use metrics_runtime::Receiver;
131    /// # fn main() {
132    /// let receiver = Receiver::builder().build().expect("failed to create receiver");
133    /// let mut sink = receiver.sink();
134    /// sink.increment_counter("messages_processed", 1);
135    /// # }
136    /// ```
137    pub fn increment_counter<N>(&mut self, name: N, value: u64)
138    where
139        N: Into<Key>,
140    {
141        let key = self.construct_key(name);
142        let id = Identifier::new(key, self.scope_handle, Kind::Counter);
143        let value_handle = self.get_cached_value_handle(id);
144        value_handle.update_counter(value);
145    }
146
147    /// Increment a value for a counter identified by the given name and labels.
148    ///
149    /// # Examples
150    ///
151    /// ```rust
152    /// # extern crate metrics_runtime;
153    /// # use metrics_runtime::Receiver;
154    /// # fn main() {
155    /// let receiver = Receiver::builder().build().expect("failed to create receiver");
156    /// let mut sink = receiver.sink();
157    /// sink.increment_counter_with_labels("messages_processed", 1, &[("message_type", "mgmt")]);
158    /// # }
159    /// ```
160    pub fn increment_counter_with_labels<N, L>(&mut self, name: N, value: u64, labels: L)
161    where
162        N: Into<ScopedString>,
163        L: IntoLabels,
164    {
165        let key = self.construct_key((name, labels));
166        let id = Identifier::new(key, self.scope_handle, Kind::Counter);
167        let value_handle = self.get_cached_value_handle(id);
168        value_handle.update_counter(value);
169    }
170
171    /// Update a value for a gauge identified by the given name.
172    ///
173    /// # Examples
174    ///
175    /// ```rust
176    /// # extern crate metrics_runtime;
177    /// # use metrics_runtime::Receiver;
178    /// # fn main() {
179    /// let receiver = Receiver::builder().build().expect("failed to create receiver");
180    /// let mut sink = receiver.sink();
181    /// sink.update_gauge("current_offset", -131);
182    /// # }
183    /// ```
184    pub fn update_gauge<N>(&mut self, name: N, value: i64)
185    where
186        N: Into<Key>,
187    {
188        let key = self.construct_key(name);
189        let id = Identifier::new(key, self.scope_handle, Kind::Gauge);
190        let value_handle = self.get_cached_value_handle(id);
191        value_handle.update_gauge(value);
192    }
193
194    /// Update a value for a gauge identified by the given name and labels.
195    ///
196    /// # Examples
197    ///
198    /// ```rust
199    /// # extern crate metrics_runtime;
200    /// # use metrics_runtime::Receiver;
201    /// # fn main() {
202    /// let receiver = Receiver::builder().build().expect("failed to create receiver");
203    /// let mut sink = receiver.sink();
204    /// sink.update_gauge_with_labels("current_offset", -131, &[("source", "stratum-1")]);
205    /// # }
206    /// ```
207    pub fn update_gauge_with_labels<N, L>(&mut self, name: N, value: i64, labels: L)
208    where
209        N: Into<ScopedString>,
210        L: IntoLabels,
211    {
212        let key = self.construct_key((name, labels));
213        let id = Identifier::new(key, self.scope_handle, Kind::Gauge);
214        let value_handle = self.get_cached_value_handle(id);
215        value_handle.update_gauge(value);
216    }
217
218    /// Records the value for a timing histogram identified by the given name.
219    ///
220    /// Both the start and end times must be supplied, but any values that implement [`Delta`] can
221    /// be used which allows for raw values from [`quanta::Clock`] to be used, or measurements from
222    /// [`Instant::now`](std::time::Instant::now).
223    ///
224    /// # Examples
225    ///
226    /// ```rust
227    /// # extern crate metrics_runtime;
228    /// # use metrics_runtime::Receiver;
229    /// # use std::thread;
230    /// # use std::time::Duration;
231    /// # fn main() {
232    /// let receiver = Receiver::builder().build().expect("failed to create receiver");
233    /// let mut sink = receiver.sink();
234    /// let start = sink.now();
235    /// thread::sleep(Duration::from_millis(10));
236    /// let end = sink.now();
237    /// sink.record_timing("sleep_time", start, end);
238    /// # }
239    /// ```
240    pub fn record_timing<N, V>(&mut self, name: N, start: V, end: V)
241    where
242        N: Into<Key>,
243        V: Delta,
244    {
245        let delta = end.delta(start);
246        self.record_value(name, delta);
247    }
248
249    /// Records the value for a timing histogram identified by the given name and labels.
250    ///
251    /// Both the start and end times must be supplied, but any values that implement [`Delta`] can
252    /// be used which allows for raw values from [`quanta::Clock`] to be used, or measurements from
253    /// [`Instant::now`](std::time::Instant::now).
254    ///
255    /// # Examples
256    ///
257    /// ```rust
258    /// # extern crate metrics_runtime;
259    /// # use metrics_runtime::Receiver;
260    /// # use std::thread;
261    /// # use std::time::Duration;
262    /// # fn main() {
263    /// let receiver = Receiver::builder().build().expect("failed to create receiver");
264    /// let mut sink = receiver.sink();
265    /// let start = sink.now();
266    /// thread::sleep(Duration::from_millis(10));
267    /// let end = sink.now();
268    /// sink.record_timing_with_labels("sleep_time", start, end, &[("mode", "low_priority")]);
269    /// # }
270    /// ```
271    pub fn record_timing_with_labels<N, L, V>(&mut self, name: N, start: V, end: V, labels: L)
272    where
273        N: Into<ScopedString>,
274        L: IntoLabels,
275        V: Delta,
276    {
277        let delta = end.delta(start);
278        self.record_value_with_labels(name, delta, labels);
279    }
280
281    /// Records the value for a value histogram identified by the given name.
282    ///
283    /// # Examples
284    ///
285    /// ```rust
286    /// # extern crate metrics_runtime;
287    /// # use metrics_runtime::Receiver;
288    /// # use std::thread;
289    /// # use std::time::Duration;
290    /// # fn main() {
291    /// let receiver = Receiver::builder().build().expect("failed to create receiver");
292    /// let mut sink = receiver.sink();
293    /// sink.record_value("rows_returned", 42);
294    /// # }
295    /// ```
296    pub fn record_value<N>(&mut self, name: N, value: u64)
297    where
298        N: Into<Key>,
299    {
300        let key = self.construct_key(name);
301        let id = Identifier::new(key, self.scope_handle, Kind::Histogram);
302        let value_handle = self.get_cached_value_handle(id);
303        value_handle.update_histogram(value);
304    }
305
306    /// Records the value for a value histogram identified by the given name and labels.
307    ///
308    /// # Examples
309    ///
310    /// ```rust
311    /// # extern crate metrics_runtime;
312    /// # use metrics_runtime::Receiver;
313    /// # use std::thread;
314    /// # use std::time::Duration;
315    /// # fn main() {
316    /// let receiver = Receiver::builder().build().expect("failed to create receiver");
317    /// let mut sink = receiver.sink();
318    /// sink.record_value_with_labels("rows_returned", 42, &[("table", "posts")]);
319    /// # }
320    /// ```
321    pub fn record_value_with_labels<N, L>(&mut self, name: N, value: u64, labels: L)
322    where
323        N: Into<ScopedString>,
324        L: IntoLabels,
325    {
326        let key = self.construct_key((name, labels));
327        let id = Identifier::new(key, self.scope_handle, Kind::Histogram);
328        let value_handle = self.get_cached_value_handle(id);
329        value_handle.update_histogram(value);
330    }
331
332    /// Creates a handle to the given counter.
333    ///
334    /// This handle can be embedded into an existing type and used to directly update the
335    /// underlying counter without requiring a [`Sink`].  This method can be called multiple times
336    /// with the same `name` and the handle will point to the single underlying instance.
337    ///
338    /// [`Counter`] is clonable.
339    ///`
340    /// # Examples
341    ///
342    /// ```rust
343    /// # extern crate metrics_runtime;
344    /// # use metrics_runtime::Receiver;
345    /// # fn main() {
346    /// let receiver = Receiver::builder().build().expect("failed to create receiver");
347    /// let mut sink = receiver.sink();
348    /// let counter = sink.counter("messages_processed");
349    /// counter.record(1);
350    ///
351    /// // Alternate, simpler usage:
352    /// counter.increment();
353    /// # }
354    /// ```
355    pub fn counter<N>(&mut self, name: N) -> Counter
356    where
357        N: Into<Key>,
358    {
359        let key = self.construct_key(name);
360        self.get_owned_value_handle(key, Kind::Counter).into()
361    }
362
363    /// Creates a handle to the given counter, with labels attached.
364    ///
365    /// This handle can be embedded into an existing type and used to directly update the
366    /// underlying counter without requiring a [`Sink`].  This method can be called multiple times
367    /// with the same `name`/`labels` and the handle will point to the single underlying instance.
368    ///
369    /// [`Counter`] is clonable.
370    ///
371    /// # Examples
372    ///
373    /// ```rust
374    /// # extern crate metrics_runtime;
375    /// # use metrics_runtime::Receiver;
376    /// # fn main() {
377    /// let receiver = Receiver::builder().build().expect("failed to create receiver");
378    /// let mut sink = receiver.sink();
379    /// let counter = sink.counter_with_labels("messages_processed", &[("service", "secure")]);
380    /// counter.record(1);
381    ///
382    /// // Alternate, simpler usage:
383    /// counter.increment();
384    /// # }
385    /// ```
386    pub fn counter_with_labels<N, L>(&mut self, name: N, labels: L) -> Counter
387    where
388        N: Into<ScopedString>,
389        L: IntoLabels,
390    {
391        self.counter((name, labels))
392    }
393
394    /// Creates a handle to the given gauge.
395    ///
396    /// This handle can be embedded into an existing type and used to directly update the
397    /// underlying gauge without requiring a [`Sink`].  This method can be called multiple times
398    /// with the same `name` and the handle will point to the single underlying instance.
399    ///
400    /// [`Gauge`] is clonable.
401    ///
402    /// # Examples
403    ///
404    /// ```rust
405    /// # extern crate metrics_runtime;
406    /// # use metrics_runtime::Receiver;
407    /// # fn main() {
408    /// let receiver = Receiver::builder().build().expect("failed to create receiver");
409    /// let mut sink = receiver.sink();
410    /// let gauge = sink.gauge("current_offset");
411    /// gauge.record(-131);
412    /// # }
413    /// ```
414    pub fn gauge<N>(&mut self, name: N) -> Gauge
415    where
416        N: Into<Key>,
417    {
418        let key = self.construct_key(name);
419        self.get_owned_value_handle(key, Kind::Gauge).into()
420    }
421
422    /// Creates a handle to the given gauge, with labels attached.
423    ///
424    /// This handle can be embedded into an existing type and used to directly update the
425    /// underlying gauge without requiring a [`Sink`].  This method can be called multiple times
426    /// with the same `name`/`labels` and the handle will point to the single underlying instance.
427    ///
428    /// [`Gauge`] is clonable.
429    ///
430    /// # Examples
431    ///
432    /// ```rust
433    /// # extern crate metrics_runtime;
434    /// # use metrics_runtime::Receiver;
435    /// # fn main() {
436    /// let receiver = Receiver::builder().build().expect("failed to create receiver");
437    /// let mut sink = receiver.sink();
438    /// let gauge = sink.gauge_with_labels("current_offset", &[("source", "stratum-1")]);
439    /// gauge.record(-131);
440    /// # }
441    /// ```
442    pub fn gauge_with_labels<N, L>(&mut self, name: N, labels: L) -> Gauge
443    where
444        N: Into<ScopedString>,
445        L: IntoLabels,
446    {
447        self.gauge((name, labels))
448    }
449
450    /// Creates a handle to the given histogram.
451    ///
452    /// This handle can be embedded into an existing type and used to directly update the
453    /// underlying histogram without requiring a [`Sink`].  This method can be called multiple
454    /// times with the same `name` and the handle will point to the single underlying instance.
455    ///
456    /// [`Histogram`] is clonable.
457    ///
458    /// # Examples
459    ///
460    /// ```rust
461    /// # extern crate metrics_runtime;
462    /// # use metrics_runtime::Receiver;
463    /// # use std::thread;
464    /// # use std::time::Duration;
465    /// # fn main() {
466    /// let receiver = Receiver::builder().build().expect("failed to create receiver");
467    /// let mut sink = receiver.sink();
468    /// let histogram = sink.histogram("request_duration");
469    ///
470    /// let start = sink.now();
471    /// thread::sleep(Duration::from_millis(10));
472    /// let end = sink.now();
473    /// histogram.record_timing(start, end);
474    ///
475    /// // Alternatively, you can just push the raw value into a histogram:
476    /// let delta = end - start;
477    /// histogram.record_value(delta);
478    /// # }
479    /// ```
480    pub fn histogram<N>(&mut self, name: N) -> Histogram
481    where
482        N: Into<Key>,
483    {
484        let key = self.construct_key(name);
485        self.get_owned_value_handle(key, Kind::Histogram).into()
486    }
487
488    /// Creates a handle to the given histogram, with labels attached.
489    ///
490    /// This handle can be embedded into an existing type and used to directly update the
491    /// underlying histogram without requiring a [`Sink`].  This method can be called multiple
492    /// times with the same `name` and the handle will point to the single underlying instance.
493    ///
494    /// [`Histogram`] is clonable.
495    ///
496    /// # Examples
497    ///
498    /// ```rust
499    /// # extern crate metrics_runtime;
500    /// # use metrics_runtime::Receiver;
501    /// # use std::thread;
502    /// # use std::time::Duration;
503    /// # fn main() {
504    /// let receiver = Receiver::builder().build().expect("failed to create receiver");
505    /// let mut sink = receiver.sink();
506    /// let histogram = sink.histogram_with_labels("request_duration", &[("service", "secure")]);
507    ///
508    /// let start = sink.now();
509    /// thread::sleep(Duration::from_millis(10));
510    /// let end = sink.now();
511    /// histogram.record_timing(start, end);
512    ///
513    /// // Alternatively, you can just push the raw value into a histogram:
514    /// let delta = end - start;
515    /// histogram.record_value(delta);
516    /// # }
517    /// ```
518    pub fn histogram_with_labels<N, L>(&mut self, name: N, labels: L) -> Histogram
519    where
520        N: Into<ScopedString>,
521        L: IntoLabels,
522    {
523        self.histogram((name, labels))
524    }
525
526    /// Creates a proxy metric.
527    ///
528    /// Proxy metrics allow you to register a closure that, when a snapshot of the metric state is
529    /// requested, will be called and have a chance to return multiple metrics that are added to
530    /// the overall metric of actual metrics.
531    ///
532    /// This can be useful for metrics which are expensive to constantly recalculate/poll, allowing
533    /// you to avoid needing to calculate/push them them yourself, with all the boilerplate that
534    /// comes with doing so periodically.
535    ///
536    /// Individual metrics must provide their own key (name), which will be appended to the name
537    /// given when registering the proxy.  A proxy can be reregistered at any time by calling this
538    /// function again with the same name.
539    ///
540    /// # Examples
541    ///
542    /// ```rust
543    /// # extern crate metrics_runtime;
544    /// # extern crate metrics_core;
545    /// # use metrics_runtime::{Receiver, Measurement};
546    /// # use metrics_core::Key;
547    /// # use std::thread;
548    /// # use std::time::Duration;
549    /// # fn main() {
550    /// let receiver = Receiver::builder().build().expect("failed to create receiver");
551    /// let mut sink = receiver.sink();
552    ///
553    /// // A proxy is now registered under the name "load_stats", which is prepended to all the
554    /// // metrics generated by the closure i.e. "load_stats.avg_1min".  These metrics are also
555    /// // still scoped normally based on the [`Sink`].
556    /// sink.proxy("load_stats", || {
557    ///     let mut values = Vec::new();
558    ///     values.push((Key::from_name("avg_1min"), Measurement::Gauge(19)));
559    ///     values.push((Key::from_name("avg_5min"), Measurement::Gauge(12)));
560    ///     values.push((Key::from_name("avg_10min"), Measurement::Gauge(10)));
561    ///     values
562    /// });
563    /// # }
564    /// ```
565    pub fn proxy<N, F>(&mut self, name: N, f: F)
566    where
567        N: Into<Key>,
568        F: Fn() -> Vec<(Key, Measurement)> + Send + Sync + 'static,
569    {
570        let id = Identifier::new(name.into(), self.scope_handle, Kind::Proxy);
571        let handle = self.get_cached_value_handle(id);
572        handle.update_proxy(f);
573    }
574
575    /// Creates a proxy metric, with labels attached.
576    ///
577    /// Proxy metrics allow you to register a closure that, when a snapshot of the metric state is
578    /// requested, will be called and have a chance to return multiple metrics that are added to
579    /// the overall metric of actual metrics.
580    ///
581    /// This can be useful for metrics which are expensive to constantly recalculate/poll, allowing
582    /// you to avoid needing to calculate/push them them yourself, with all the boilerplate that
583    /// comes with doing so periodically.
584    ///
585    /// Individual metrics must provide their own key (name), which will be appended to the name
586    /// given when registering the proxy.  A proxy can be reregistered at any time by calling this
587    /// function again with the same name.
588    ///
589    /// # Examples
590    ///
591    /// ```rust
592    /// # extern crate metrics_runtime;
593    /// # extern crate metrics_core;
594    /// # use metrics_runtime::{Receiver, Measurement};
595    /// # use metrics_core::Key;
596    /// # use std::thread;
597    /// # use std::time::Duration;
598    /// # fn main() {
599    /// let receiver = Receiver::builder().build().expect("failed to create receiver");
600    /// let mut sink = receiver.sink();
601    ///
602    /// let system_name = "web03".to_string();
603    ///
604    /// // A proxy is now registered under the name "load_stats", which is prepended to all the
605    /// // metrics generated by the closure i.e. "load_stats.avg_1min".  These metrics are also
606    /// // still scoped normally based on the [`Sink`].
607    /// sink.proxy_with_labels("load_stats", &[("system", system_name)], || {
608    ///     let mut values = Vec::new();
609    ///     values.push((Key::from_name("avg_1min"), Measurement::Gauge(19)));
610    ///     values.push((Key::from_name("avg_5min"), Measurement::Gauge(12)));
611    ///     values.push((Key::from_name("avg_10min"), Measurement::Gauge(10)));
612    ///     values
613    /// });
614    /// # }
615    /// ```
616    pub fn proxy_with_labels<N, L, F>(&mut self, name: N, labels: L, f: F)
617    where
618        N: Into<ScopedString>,
619        L: IntoLabels,
620        F: Fn() -> Vec<(Key, Measurement)> + Send + Sync + 'static,
621    {
622        self.proxy((name, labels), f)
623    }
624
625    pub(crate) fn construct_key<K>(&self, key: K) -> Key
626    where
627        K: Into<Key>,
628    {
629        let mut key = key.into();
630        if !self.default_labels.is_empty() {
631            key.add_labels(self.default_labels.clone());
632        }
633        key
634    }
635
636    fn get_owned_value_handle<K>(&mut self, key: K, kind: Kind) -> ValueHandle
637    where
638        K: Into<Key>,
639    {
640        let id = Identifier::new(key.into(), self.scope_handle, kind);
641        self.get_cached_value_handle(id).clone()
642    }
643
644    fn get_cached_value_handle(&mut self, identifier: Identifier) -> &ValueHandle {
645        // This gross hack gets around lifetime rules until full NLL is stable.  Without it, the
646        // borrow checker doesn't understand the flow control and thinks the reference lives all
647        // the way until the of the function, which breaks when we try to take a mutable reference
648        // for inserting into the handle cache.
649        if let Some(handle) = self.metric_cache.get(&identifier) {
650            return unsafe { &*(handle as *const ValueHandle) };
651        }
652
653        let handle = self.metric_registry.get_or_register(identifier.clone());
654        self.metric_cache.insert(identifier.clone(), handle);
655        self.metric_cache.get(&identifier).unwrap()
656    }
657}
658
659impl Clone for Sink {
660    fn clone(&self) -> Sink {
661        Sink {
662            metric_registry: self.metric_registry.clone(),
663            metric_cache: self.metric_cache.clone(),
664            scope_registry: self.scope_registry.clone(),
665            scope: self.scope.clone(),
666            scope_handle: self.scope_handle,
667            clock: self.clock.clone(),
668            default_labels: self.default_labels.clone(),
669        }
670    }
671}
672
673impl<'a> AsScoped<'a> for str {
674    fn as_scoped(&'a self, base: Scope) -> Scope {
675        base.add_part(self.to_string())
676    }
677}
678
679impl<'a, 'b, T> AsScoped<'a> for T
680where
681    &'a T: AsRef<[&'b str]>,
682    T: 'a,
683{
684    fn as_scoped(&'a self, base: Scope) -> Scope {
685        self.as_ref()
686            .iter()
687            .fold(base, |s, ss| s.add_part(ss.to_string()))
688    }
689}
690
691#[cfg(test)]
692mod tests {
693    use super::{Clock, MetricRegistry, Scope, ScopeRegistry, Sink};
694    use crate::config::Configuration;
695    use std::sync::Arc;
696
697    #[test]
698    fn test_construct_key() {
699        // TODO(tobz): this is a lot of boilerplate to get a `Sink` for testing, wonder if there's
700        // anything better we could be doing?
701        let sregistry = Arc::new(ScopeRegistry::new());
702        let config = Configuration::mock();
703        let (clock, _) = Clock::mock();
704        let mregistry = Arc::new(MetricRegistry::new(
705            sregistry.clone(),
706            config,
707            clock.clone(),
708        ));
709        let mut sink = Sink::new(mregistry, sregistry, Scope::Root, clock);
710
711        let no_labels = sink.construct_key("foo");
712        assert_eq!(no_labels.name(), "foo");
713        assert_eq!(no_labels.labels().count(), 0);
714
715        let labels_given = sink.construct_key(("baz", &[("type", "test")]));
716        assert_eq!(labels_given.name(), "baz");
717        let label_str = labels_given
718            .labels()
719            .map(|l| format!("{}={}", l.key(), l.value()))
720            .collect::<Vec<_>>()
721            .join(",");
722        assert_eq!(label_str, "type=test");
723
724        sink.add_default_labels(&[("service", "foo")]);
725
726        let no_labels = sink.construct_key("bar");
727        assert_eq!(no_labels.name(), "bar");
728        let label_str = no_labels
729            .labels()
730            .map(|l| format!("{}={}", l.key(), l.value()))
731            .collect::<Vec<_>>()
732            .join(",");
733        assert_eq!(label_str, "service=foo");
734
735        let labels_given = sink.construct_key(("quux", &[("type", "test")]));
736        assert_eq!(labels_given.name(), "quux");
737        let label_str = labels_given
738            .labels()
739            .map(|l| format!("{}={}", l.key(), l.value()))
740            .collect::<Vec<_>>()
741            .join(",");
742        assert_eq!(label_str, "type=test,service=foo");
743    }
744}