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}