cadence_with_flush/
types.rs

1// Cadence - An extensible Statsd client for Rust!
2//
3// Copyright 2015-2021 Nick Pillitteri
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
11use crate::builder::{MetricFormatter, MetricValue};
12use std::error;
13use std::fmt;
14use std::io;
15
16/// Trait for metrics to expose Statsd metric string slice representation.
17///
18/// Implementing metrics know how to turn themselves into one of the supported
19/// types of metrics as defined in the [Statsd spec](https://github.com/b/statsd_spec).
20pub trait Metric {
21    fn as_metric_str(&self) -> &str;
22}
23
24/// Counters are simple values incremented or decremented by a client.
25///
26/// See the `Counted` trait for more information.
27#[derive(PartialEq, Eq, Debug, Hash, Clone)]
28pub struct Counter {
29    repr: String,
30}
31
32impl Counter {
33    pub fn new(prefix: &str, key: &str, count: i64) -> Self {
34        Self::from(MetricFormatter::counter(prefix, key, MetricValue::Signed(count)).format())
35    }
36}
37
38impl From<String> for Counter {
39    fn from(s: String) -> Self {
40        Counter { repr: s }
41    }
42}
43
44impl Metric for Counter {
45    fn as_metric_str(&self) -> &str {
46        &self.repr
47    }
48}
49
50/// Timers are a positive number of milliseconds between a start and end point.
51///
52/// Statistical distribution of timer values is often computed by the server.
53///
54/// See the `Timed` trait for more information.
55#[derive(PartialEq, Eq, Debug, Hash, Clone)]
56pub struct Timer {
57    repr: String,
58}
59
60impl Timer {
61    pub fn new(prefix: &str, key: &str, time: u64) -> Self {
62        Self::from(MetricFormatter::timer(prefix, key, MetricValue::Unsigned(time)).format())
63    }
64}
65
66impl From<String> for Timer {
67    fn from(s: String) -> Self {
68        Timer { repr: s }
69    }
70}
71
72impl Metric for Timer {
73    fn as_metric_str(&self) -> &str {
74        &self.repr
75    }
76}
77
78/// Gauges are an instantaneous value determined by the client.
79///
80/// See the `Gauged` trait for more information.
81#[derive(PartialEq, Eq, Debug, Hash, Clone)]
82pub struct Gauge {
83    repr: String,
84}
85
86impl Gauge {
87    pub fn new(prefix: &str, key: &str, value: u64) -> Self {
88        Self::from(MetricFormatter::gauge(prefix, key, MetricValue::Unsigned(value)).format())
89    }
90
91    pub fn new_f64(prefix: &str, key: &str, value: f64) -> Self {
92        Self::from(MetricFormatter::gauge(prefix, key, MetricValue::Float(value)).format())
93    }
94}
95
96impl From<String> for Gauge {
97    fn from(s: String) -> Self {
98        Gauge { repr: s }
99    }
100}
101
102impl Metric for Gauge {
103    fn as_metric_str(&self) -> &str {
104        &self.repr
105    }
106}
107
108/// Meters measure the rate at which events occur as determined by the server.
109///
110/// See the `Metered` trait for more information.
111#[derive(PartialEq, Eq, Debug, Hash, Clone)]
112pub struct Meter {
113    repr: String,
114}
115
116impl Meter {
117    pub fn new(prefix: &str, key: &str, value: u64) -> Self {
118        Self::from(MetricFormatter::meter(prefix, key, MetricValue::Unsigned(value)).format())
119    }
120}
121
122impl From<String> for Meter {
123    fn from(s: String) -> Self {
124        Meter { repr: s }
125    }
126}
127
128impl Metric for Meter {
129    fn as_metric_str(&self) -> &str {
130        &self.repr
131    }
132}
133
134/// Histograms are values whose distribution is calculated by the server.
135///
136/// The distribution calculated for histograms is often similar to that of
137/// timers. Histograms can be thought of as a more general (not limited to
138/// timing things) form of timers.
139///
140/// See the `Histogrammed` trait for more information.
141#[derive(PartialEq, Eq, Debug, Hash, Clone)]
142pub struct Histogram {
143    repr: String,
144}
145
146impl Histogram {
147    pub fn new(prefix: &str, key: &str, value: u64) -> Self {
148        Self::from(MetricFormatter::histogram(prefix, key, MetricValue::Unsigned(value)).format())
149    }
150
151    pub fn new_f64(prefix: &str, key: &str, value: f64) -> Self {
152        Self::from(MetricFormatter::histogram(prefix, key, MetricValue::Float(value)).format())
153    }
154}
155
156impl From<String> for Histogram {
157    fn from(s: String) -> Self {
158        Histogram { repr: s }
159    }
160}
161
162impl Metric for Histogram {
163    fn as_metric_str(&self) -> &str {
164        &self.repr
165    }
166}
167
168/// Distributions represent a global statistical distribution of a set of values.
169///
170/// See the `Distributed` trait for more information.
171#[derive(PartialEq, Eq, Debug, Hash, Clone)]
172pub struct Distribution {
173    repr: String,
174}
175
176impl Distribution {
177    pub fn new(prefix: &str, key: &str, value: u64) -> Self {
178        Self::from(MetricFormatter::distribution(prefix, key, MetricValue::Unsigned(value)).format())
179    }
180
181    pub fn new_f64(prefix: &str, key: &str, value: f64) -> Self {
182        Self::from(MetricFormatter::distribution(prefix, key, MetricValue::Float(value)).format())
183    }
184}
185
186impl From<String> for Distribution {
187    fn from(s: String) -> Self {
188        Distribution { repr: s }
189    }
190}
191
192impl Metric for Distribution {
193    fn as_metric_str(&self) -> &str {
194        &self.repr
195    }
196}
197
198/// Sets count the number of unique elements in a group.
199///
200/// See the `Setted` trait for more information.
201#[derive(PartialEq, Eq, Debug, Hash, Clone)]
202pub struct Set {
203    repr: String,
204}
205
206impl Set {
207    pub fn new(prefix: &str, key: &str, value: i64) -> Self {
208        Self::from(MetricFormatter::set(prefix, key, MetricValue::Signed(value)).format())
209    }
210}
211
212impl From<String> for Set {
213    fn from(s: String) -> Self {
214        Set { repr: s }
215    }
216}
217
218impl Metric for Set {
219    fn as_metric_str(&self) -> &str {
220        &self.repr
221    }
222}
223
224/// Potential categories an error from this library falls into.
225#[derive(PartialEq, Eq, Debug, Hash, Clone, Copy)]
226pub enum ErrorKind {
227    InvalidInput,
228    IoError,
229}
230
231/// Error generated by this library potentially wrapping another
232/// type of error (exposed via the `Error` trait).
233#[derive(Debug)]
234pub struct MetricError {
235    repr: ErrorRepr,
236}
237
238#[derive(Debug)]
239enum ErrorRepr {
240    WithDescription(ErrorKind, &'static str),
241    IoError(io::Error),
242}
243
244impl MetricError {
245    /// Return the kind of the error
246    pub fn kind(&self) -> ErrorKind {
247        match self.repr {
248            ErrorRepr::IoError(_) => ErrorKind::IoError,
249            ErrorRepr::WithDescription(kind, _) => kind,
250        }
251    }
252}
253
254impl fmt::Display for MetricError {
255    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
256        match self.repr {
257            ErrorRepr::IoError(ref err) => err.fmt(f),
258            ErrorRepr::WithDescription(_, desc) => desc.fmt(f),
259        }
260    }
261}
262
263impl error::Error for MetricError {
264    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
265        match self.repr {
266            ErrorRepr::IoError(ref err) => Some(err),
267            _ => None,
268        }
269    }
270
271    // Deprecated in 1.42 but we'd like to support it and older versions of
272    // Rust where this method wasn't deprecated. There's no easy way to return
273    // a `&str` based on the result of `Display` or similar so we're stuck with
274    // this API for a while.
275    #[allow(deprecated, deprecated_in_future)]
276    fn description(&self) -> &str {
277        match self.repr {
278            ErrorRepr::IoError(ref err) => err.description(),
279            ErrorRepr::WithDescription(_, desc) => desc,
280        }
281    }
282
283    fn cause(&self) -> Option<&dyn error::Error> {
284        self.source()
285    }
286}
287
288impl From<io::Error> for MetricError {
289    fn from(err: io::Error) -> MetricError {
290        MetricError {
291            repr: ErrorRepr::IoError(err),
292        }
293    }
294}
295
296impl From<(ErrorKind, &'static str)> for MetricError {
297    fn from((kind, desc): (ErrorKind, &'static str)) -> MetricError {
298        MetricError {
299            repr: ErrorRepr::WithDescription(kind, desc),
300        }
301    }
302}
303
304pub type MetricResult<T> = Result<T, MetricError>;
305
306#[cfg(test)]
307mod tests {
308    #![allow(deprecated, deprecated_in_future)]
309
310    use super::{Counter, ErrorKind, Gauge, Histogram, Meter, Metric, MetricError, Set, Timer};
311    use std::error::Error;
312    use std::io;
313
314    #[test]
315    fn test_counter_to_metric_string() {
316        let counter = Counter::new("my.app.", "test.counter", 4);
317        assert_eq!("my.app.test.counter:4|c", counter.as_metric_str());
318    }
319
320    #[test]
321    fn test_counter_no_prefix_to_metric_string() {
322        let counter = Counter::new("", "test.counter", 4);
323        assert_eq!("test.counter:4|c", counter.as_metric_str());
324    }
325
326    #[test]
327    fn test_timer_to_metric_string() {
328        let timer = Timer::new("my.app.", "test.timer", 34);
329        assert_eq!("my.app.test.timer:34|ms", timer.as_metric_str());
330    }
331
332    #[test]
333    fn test_timer_no_prefix_to_metric_string() {
334        let timer = Timer::new("", "test.timer", 34);
335        assert_eq!("test.timer:34|ms", timer.as_metric_str());
336    }
337
338    #[test]
339    fn test_gauge_to_metric_string() {
340        let gauge = Gauge::new("my.app.", "test.gauge", 2);
341        assert_eq!("my.app.test.gauge:2|g", gauge.as_metric_str());
342    }
343
344    #[test]
345    fn test_gauge_no_prefix_to_metric_string() {
346        let gauge = Gauge::new("", "test.gauge", 2);
347        assert_eq!("test.gauge:2|g", gauge.as_metric_str());
348    }
349
350    #[test]
351    fn test_meter_to_metric_string() {
352        let meter = Meter::new("my.app.", "test.meter", 5);
353        assert_eq!("my.app.test.meter:5|m", meter.as_metric_str());
354    }
355
356    #[test]
357    fn test_meter_no_prefix_to_metric_string() {
358        let meter = Meter::new("", "test.meter", 5);
359        assert_eq!("test.meter:5|m", meter.as_metric_str());
360    }
361
362    #[test]
363    fn test_histogram_to_metric_string() {
364        let histogram = Histogram::new("my.app.", "test.histogram", 45);
365        assert_eq!("my.app.test.histogram:45|h", histogram.as_metric_str());
366    }
367
368    #[test]
369    fn test_histogram_no_prefix_to_metric_string() {
370        let histogram = Histogram::new("", "test.histogram", 45);
371        assert_eq!("test.histogram:45|h", histogram.as_metric_str());
372    }
373
374    #[test]
375    fn test_set_to_metric_string() {
376        let set = Set::new("my.app.", "test.set", 4);
377        assert_eq!("my.app.test.set:4|s", set.as_metric_str());
378    }
379
380    #[test]
381    fn test_set_no_prefix_to_metric_string() {
382        let set = Set::new("", "test.set", 4);
383        assert_eq!("test.set:4|s", set.as_metric_str());
384    }
385
386    #[test]
387    fn test_metric_error_kind_io_error() {
388        let io_err = io::Error::new(io::ErrorKind::BrokenPipe, "Broken pipe");
389        let our_err = MetricError::from(io_err);
390        assert_eq!(ErrorKind::IoError, our_err.kind());
391    }
392
393    #[test]
394    fn test_metric_error_kind_invalid_input() {
395        let our_err = MetricError::from((ErrorKind::InvalidInput, "Nope"));
396        assert_eq!(ErrorKind::InvalidInput, our_err.kind());
397    }
398
399    #[test]
400    fn test_metric_error_description_io_error() {
401        let io_err = io::Error::new(io::ErrorKind::PermissionDenied, "Permission!");
402        let our_err = MetricError::from(io_err);
403        assert_eq!("Permission!", our_err.description());
404    }
405
406    #[test]
407    fn test_metric_error_description_other() {
408        let our_err = MetricError::from((ErrorKind::InvalidInput, "Something!"));
409        assert_eq!("Something!", our_err.description());
410    }
411
412    #[test]
413    fn test_metric_error_cause_io_error() {
414        let io_err = io::Error::new(io::ErrorKind::TimedOut, "Timeout!");
415        let our_err = MetricError::from(io_err);
416        assert_eq!("Timeout!", our_err.source().unwrap().description());
417    }
418
419    #[test]
420    fn test_metric_error_cause_other() {
421        let our_err = MetricError::from((ErrorKind::InvalidInput, "Nope!"));
422        assert!(our_err.source().is_none());
423    }
424}