metrics_exporter_statsd/lib.rs
1//! A [`metrics`] exporter that supports reporting metrics to Statsd. This exporter is basically
2//! a thin wrapper on top of the [`cadence`] crate which supports Statsd/Datadog style metrics.
3//!
4//! # Warnings
5//!
6//! * **Versions of this crate are tightly coupled to metrics crate versions.**
7//!
8//! * [`metrics::Counter::absolute`], [`metrics::Gauge::increment`], and
9//! [`metrics::Gauge::decrement`] are not supported. Statsd doesn't have these concepts.
10//! Unfortunately this means that if the application is using these methods, the metrics will
11//! silently be missing or wrong.
12//!
13//! # Usage
14//!
15//! ```
16//! use metrics_exporter_statsd::StatsdBuilder;
17//!
18//! let recorder = StatsdBuilder::from("127.0.0.1", 8125)
19//! .with_queue_size(5000)
20//! .with_buffer_size(1024)
21//! .build(Some("prefix"))
22//! .expect("Could not create StatsdRecorder");
23//!
24//! metrics::set_global_recorder(recorder);
25//! ```
26//!
27//! You can then continue to use [`metrics`] as usual:
28//!
29//! ```
30//! metrics::counter!("counter.name").increment(1);
31//! ```
32//!
33//! Labels are translated to datadog style tags:
34//!
35//!```
36//! metrics::gauge!("gauge.name", "tag" => "value").set(100.0);
37//!```
38//! will translate to `gauge.name:50.25|g|#tag:value` and should render appropriately in systems
39//! like Datadog.
40//!
41//! # Queue Size and Buffer Size
42//!
43//! The supplied queue size and buffer size are used to construct the two different
44//! [`MetricSink`](cadence::MetricSink)s provided by [`cadence`]:
45//!
46//! 1. [`BufferedUdpMetricSink`](cadence::BufferedUdpMetricSink) controls how much data
47//! should be buffered before actually flushing it over the network/socket. By default this value
48//! is conservatively set to `256` and should be tuned based on the client needs/experience.
49//!
50//! 2. [`QueuingMetricSink`](cadence::QueuingMetricSink) controls how many elements should be
51//! allowed to queue when the demand on `StatsdClient` is high, this value is currently
52//! defauled to `5000`. This value should also be tuned according to the client
53//! needs/experience. It's important to note that once the queue is full the `StatsdClient`
54//! will error out and overflow metrics will not be reported to statsd.
55//!
56//! As documented in `cadence`'s documentation, this is the preferred way to configure `cadence`
57//! in production. This interface doesn't allow you to configure an unbounded queue, you must provide
58//! a queue size or one is chosen for you.
59//!
60//! # Histograms
61//! The default behavior if you do not specify a global preference, or an explict tag is to send
62//! `histogram!` metrics as Histograms. If you do set an alternative global preference but would
63//! like to override it for a given metric you can still do so:
64//!
65//! ```
66//! metrics::histogram!("metric.name", "histogram"=> "histogram","tag"=>"value").record(100.0)
67//! ```
68//! This will emit the usual histogram metric this `metric.name:100|h|#tag:value`.
69//!
70//!# Distributions
71//! Some implementations of StatsD like Dogstatsd support the concept of distributions, that
72//! aggregate the measurments on the server instead of the agent. This allows for more accurate
73//! calculation percentiles by systems like Datadog.
74//!
75//! Unfortunately the metrics library doesn't have the direct interface for reporting distributions
76//! e.g. `metrics::distribution!("...")` (which is understandable as it may not be broadly applicable).
77//!
78//! This library works around that by morphing `metrics::histogram!` into distribution if you provide
79//! provide an appropriate hint label.
80//!
81//! **Reporting distributions:**
82//! ```
83//! metrics::histogram!("metric.name", "histogram"=>"distribution", "tag"=>"value").record(100.0)
84//! ```
85//! This will emit a metric like this: `metric.name:100|d|#tag:value`, note the metric type has
86//! changed from `h` to `d`.
87//!
88//! # Timers
89//! StatsD specification does have the concept of timers that more or less behave like histograms e.g.
90//! they are aggregated at the agent, support for timer metrics is similar to distribution.
91//!
92//! **Reporting timers:**
93//! ```
94//! metrics::histogram!("metric.name", "histogram"=>"timer", "tag"=>"value").record(100.0)
95//! ```
96//! This will emit a metric like this: `metric.name:100|ms|#tag:value`, note the metric type has
97//! changed from `h` to `ms`.
98//!
99//! # Chaging the default type of histogram
100//!
101//! If your application mostly is interested in distribution or timers, you can indicate that to
102//! `StatsdBuilder` in the following way:
103//!
104//! ```
105//! use metrics_exporter_statsd::StatsdBuilder;
106//!
107//! let recorder = StatsdBuilder::from("127.0.0.1", 8125)
108//! .with_queue_size(5000)
109//! .with_buffer_size(1024)
110//! .histogram_is_distribution()
111//! .build(Some("prefix"))
112//! .expect("Could not create StatsdRecorder");
113//!
114//! metrics::set_global_recorder(recorder);
115//!```
116//!
117//! Once the exporter is marked this way then all the histograms will be reported as distributions
118//! by default unless labeled differently. For example following statement:
119//!
120//! ```
121//! metrics::histogram!("metric.name", "tag"=>"value").record(100.0)
122//! ```
123//! This will emit a metric like this: `metric.name:100|d|#tag:value`, note the metric type has
124//! emitted here is `d` and not `h`.
125//!
126//! **Note:** Most of the other metrics-rs builders provide a convenience method for installing a global recorder. E.g
127//! for Prometheus or TCP metrics exporters you could do something along the lines of `PrometheusBuilder::new().install()`.
128//!
129//! This library does not have an `.install()` method. Instead, use `.build()` and call
130//! `metrics::set_boxed_recorder`, as in the example code. This ensures that if you ever have a version mismatch
131//! between `metrics-recorder-statsd` and `metrics`, you'll get a build-time error (rather than Cargo silently
132//! linking in two versions of `metrics`, which would result in `metrics` silently dropping all your data).
133mod recorder;
134
135pub use self::recorder::*;
136
137mod builder;
138mod types;
139
140pub use self::builder::*;