[go: up one dir, main page]

cadence/
lib.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
11//! An extensible Statsd client for Rust!
12//!
13//! Cadence is a fast and flexible way to emit Statsd metrics from your application.
14//!
15//! ## Features
16//!
17//! * [Support](https://docs.rs/cadence/) for emitting counters, timers, histograms, distributions,
18//!   gauges, meters, and sets to Statsd over UDP (or optionally Unix sockets).
19//! * Support for alternate backends via the `MetricSink` trait.
20//! * Support for [Datadog](https://docs.datadoghq.com/developers/dogstatsd/) style metrics tags.
21//! * [Macros](https://docs.rs/cadence-macros/) to simplify common calls to emit metrics
22//! * A simple yet flexible API for sending metrics.
23//!
24//! ## Install
25//!
26//! To make use of `cadence` in your project, add it as a dependency in your `Cargo.toml` file.
27//!
28//! ```toml
29//! [dependencies]
30//! cadence = "x.y.z"
31//! ```
32//!
33//! That's all you need!
34//!
35//! ## Usage
36//!
37//! Some examples of how to use Cadence are shown below. The examples start
38//! simple and work up to how you should be using Cadence in a production
39//! application.
40//!
41//! ### Simple Use
42//!
43//! Simple usage of Cadence is shown below. In this example, we just import
44//! the client, create an instance that will write to some imaginary metrics
45//! server, and send a few metrics.
46//!
47//! ```rust,no_run
48//! use std::net::UdpSocket;
49//! use cadence::prelude::*;
50//! use cadence::{StatsdClient, UdpMetricSink, DEFAULT_PORT};
51//!
52//! // Create client that will write to the given host over UDP.
53//! //
54//! // Note that you'll probably want to actually handle any errors creating
55//! // the client when you use it for real in your application. We're just
56//! // using .unwrap() here since this is an example!
57//! let host = ("metrics.example.com", DEFAULT_PORT);
58//! let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
59//! let sink = UdpMetricSink::from(host, socket).unwrap();
60//! let client = StatsdClient::from_sink("my.metrics", sink);
61//!
62//! // Emit metrics!
63//! client.count("some.counter", 1);
64//! client.time("some.methodCall", 42);
65//! client.gauge("some.thing", 7);
66//! client.meter("some.value", 5);
67//! ```
68//!
69//! ### Buffered UDP Sink
70//!
71//! While sending a metric over UDP is very fast, the overhead of frequent
72//! network calls can start to add up. This is especially true if you are
73//! writing a high performance application that emits a lot of metrics.
74//!
75//! To make sure that metrics aren't interfering with the performance of
76//! your application, you may want to use a `MetricSink` implementation that
77//! buffers multiple metrics before sending them in a single network
78//! operation. For this, there's `BufferedUdpMetricSink`. An example of
79//! using this sink is given below.
80//!
81//! ```rust,no_run
82//! use std::net::UdpSocket;
83//! use cadence::prelude::*;
84//! use cadence::{StatsdClient, BufferedUdpMetricSink, DEFAULT_PORT};
85//!
86//! let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
87//! socket.set_nonblocking(true).unwrap();
88//!
89//! let host = ("metrics.example.com", DEFAULT_PORT);
90//! let sink = BufferedUdpMetricSink::from(host, socket).unwrap();
91//! let client = StatsdClient::from_sink("my.prefix", sink);
92//!
93//! client.count("my.counter.thing", 29);
94//! client.time("my.service.call", 214);
95//! ```
96//!
97//! As you can see, using this buffered UDP sink is no more complicated
98//! than using the regular, non-buffered, UDP sink.
99//!
100//! The only downside to this sink is that metrics aren't written to the
101//! Statsd server until the buffer is full. If you have a busy application
102//! that is constantly emitting metrics, this shouldn't be a problem.
103//! However, if your application only occasionally emits metrics, this sink
104//! might result in the metrics being delayed for a little while until the
105//! buffer fills. In this case, it may make sense to use the `UdpMetricSink`
106//! since it does not do any buffering.
107//!
108//! ### Queuing Asynchronous Metric Sink
109//!
110//! To make sure emitting metrics doesn't interfere with the performance
111//! of your application (even though emitting metrics is generally quite
112//! fast), it's probably a good idea to make sure metrics are emitted in
113//! in a different thread than your application thread.
114//!
115//! To allow you to do this, there is `QueuingMetricSink`. This sink allows
116//! you to wrap any other metric sink and send metrics to it via a queue,
117//! as it emits metrics in another thread, asynchronously from the flow of
118//! your application.
119//!
120//! The requirements for the wrapped metric sink are that it is thread
121//! safe, meaning that it implements the `Send` and `Sync` traits. If
122//! you're using the `QueuingMetricSink` with another sink from Cadence,
123//! you don't need to worry: they are all thread safe.
124//!
125//! An example of using the `QueuingMetricSink` to wrap a buffered UDP
126//! metric sink is given below. This is the preferred way to use Cadence
127//! in production.
128//!
129//! ```rust,no_run
130//! use std::net::UdpSocket;
131//! use cadence::prelude::*;
132//! use cadence::{StatsdClient, QueuingMetricSink, BufferedUdpMetricSink, DEFAULT_PORT};
133//!
134//! let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
135//! socket.set_nonblocking(true).unwrap();
136//!
137//! let host = ("metrics.example.com", DEFAULT_PORT);
138//! let udp_sink = BufferedUdpMetricSink::from(host, socket).unwrap();
139//! let queuing_sink = QueuingMetricSink::from(udp_sink);
140//! let client = StatsdClient::from_sink("my.prefix", queuing_sink);
141//!
142//! client.count("my.counter.thing", 29);
143//! client.time("my.service.call", 214);
144//! ```
145//!
146//! In the example above, we use the default constructor for the queuing
147//! sink which creates an **unbounded** queue, with no maximum size, to connect
148//! the main thread where the client sends metrics to the background thread
149//! in which the wrapped sink is running. If instead, you want to create a
150//! **bounded** queue with a maximum size, you can use the `with_capacity`
151//! constructor. An example of this is given below.
152//!
153//! ```rust,no_run
154//! use std::net::UdpSocket;
155//! use cadence::prelude::*;
156//! use cadence::{StatsdClient, QueuingMetricSink, BufferedUdpMetricSink,
157//!               DEFAULT_PORT};
158//!
159//! // Queue with a maximum capacity of 128K elements
160//! const QUEUE_SIZE: usize = 128 * 1024;
161//!
162//! let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
163//! socket.set_nonblocking(true).unwrap();
164//!
165//! let host = ("metrics.example.com", DEFAULT_PORT);
166//! let udp_sink = BufferedUdpMetricSink::from(host, socket).unwrap();
167//! let queuing_sink = QueuingMetricSink::with_capacity(udp_sink, QUEUE_SIZE);
168//! let client = StatsdClient::from_sink("my.prefix", queuing_sink);
169//!
170//! client.count("my.counter.thing", 29);
171//! client.time("my.service.call", 214);
172//! ```
173//!
174//! Using a `QueuingMetricSink` with a capacity set means that when the queue
175//! is full, attempts to emit metrics via the `StatsdClient` will fail. While
176//! this is bad, the alternative (if you instead used an unbounded queue) is
177//! for unsent metrics to slowly use up more and more memory until your
178//! application exhausts all memory.
179//!
180//! Using an **unbounded** queue means that the sending of metrics can absorb
181//! slowdowns of sending metrics until your application runs out of memory.
182//! Using a **bounded** queue puts a cap on the amount of memory that sending
183//! metrics will use in your application. This is a tradeoff that users of
184//! Cadence must decide for themselves.
185//!
186
187//! It is also possible to supply an error handler for a `QueuingMetricSink` to
188//! be called whenever the wrapped sink cannot send metrics for whatever reason.
189
190//! ```rust,no_run
191//! use std::net::UdpSocket;
192//! use cadence::prelude::*;
193//! use cadence::{StatsdClient, QueuingMetricSink, BufferedUdpMetricSink,
194//!               DEFAULT_PORT};
195//!
196//! // Queue with a maximum capacity of 128K elements
197//! const QUEUE_SIZE: usize = 128 * 1024;
198//!
199//! let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
200//! socket.set_nonblocking(true).unwrap();
201//!
202//! let host = ("metrics.example.com", DEFAULT_PORT);
203//! let udp_sink = BufferedUdpMetricSink::from(host, socket).unwrap();
204//! let queuing_sink = QueuingMetricSink::builder()
205//!     .with_capacity(QUEUE_SIZE)
206//!     .with_error_handler(|e| {
207//!         eprintln!("Error while sending metrics: {:?}", e);
208//!     })
209//!     .build(udp_sink);
210//! let client = StatsdClient::from_sink("my.prefix", queuing_sink);
211//!
212//! client.count("my.counter.thing", 29);
213//! client.time("my.service.call", 214);
214//! ```
215//!
216//! ### Use With Tags
217//!
218//! Adding tags to metrics is accomplished via the use of each of the `_with_tags`
219//! methods that are part of the Cadence `StatsdClient` struct. An example of using
220//! these methods is given below. Note that tags are an extension to the Statsd
221//! protocol and so may not be supported by all servers.
222//!
223//! See the [Datadog docs](https://docs.datadoghq.com/developers/dogstatsd/) for
224//! more information.
225//!
226//! ```rust,no_run
227//! use cadence::prelude::*;
228//! use cadence::{Metric, StatsdClient, NopMetricSink};
229//!
230//! let client = StatsdClient::from_sink("my.prefix", NopMetricSink);
231//!
232//! let res = client.count_with_tags("my.counter", 29)
233//!     .with_tag("host", "web03.example.com")
234//!     .with_tag_value("beta-test")
235//!     .try_send();
236//!
237//! assert_eq!(
238//!     concat!(
239//!         "my.prefix.my.counter:29|c|#",
240//!         "host:web03.example.com,",
241//!         "beta-test"
242//!     ),
243//!     res.unwrap().as_metric_str()
244//! );
245//! ```
246//!
247//! ### Default Tags
248//!
249//! Default tags can be added to a `StatsdClient` when constructed using the builder.
250//! Default tags are added to every metric emitted by the `StatsdClient` without any
251//! extra work after building the client. Note that tags are an extension to the Statsd
252//! protocol and so may not be supported by all servers.
253//!
254//! See the [Datadog docs](https://docs.datadoghq.com/developers/dogstatsd/) for
255//! more information.
256//!
257//! ```rust,no_run
258//! use cadence::prelude::*;
259//! use cadence::{Metric, StatsdClient, NopMetricSink};
260//!
261//! let client = StatsdClient::builder("my.prefix", NopMetricSink)
262//!     .with_tag("env", "prod")
263//!     .with_tag("app", "auth")
264//!     .build();
265//!
266//! let res = client.count_with_tags("my.counter", 29)
267//!     .with_tag("host", "web03.example.com")
268//!     .with_tag_value("beta-test")
269//!     .try_send();
270//!
271//! assert_eq!(
272//!     concat!(
273//!         "my.prefix.my.counter:29|c|#",
274//!         "env:prod,",
275//!         "app:auth,",
276//!         "host:web03.example.com,",
277//!         "beta-test"
278//!     ),
279//!     res.unwrap().as_metric_str()
280//! );
281//! ```
282//!
283//! ### Value Packing
284//!
285//! Value packing allows multiple values to be sent as a single metric for histograms,
286//! distributions, and timer types. The Cadence client accepts `Vec<T>` for histogram,
287//! distribution, and timer methods and will format multiple values as described below.
288//! Note that this feature is a Datadog extension and so may not be supported by your
289//! server. It is supported by versions `>=v6.25.0 && <v7.0.0` or `>=v7.25.0` of the
290//! Datadog agent.
291//!
292//! Packed metrics have the following format:
293//! ```text
294//! <METRIC_NAME>:<VALUE1>:<VALUE2>:<VALUE3>|<TYPE>|#<TAG_KEY_1>:<TAG_VALUE_1>,<TAG_2>`
295//! ```
296//!
297//! See the [Datadog Docs](https://docs.datadoghq.com/developers/dogstatsd/datagram_shell/?tab=metrics#dogstatsd-protocol-v11)
298//! for more information.
299//!
300//! ```rust,no_run
301//! use cadence::prelude::*;
302//! use cadence::{Metric, StatsdClient, NopMetricSink};
303//!
304//! let client = StatsdClient::from_sink("my.prefix", NopMetricSink);
305//!
306//! let res = client.distribution_with_tags("my.distribution", vec![29, 30, 31, 32])
307//!     .with_tag("host", "web03.example.com")
308//!     .with_tag_value("beta-test")
309//!     .try_send();
310//!
311//! assert_eq!(
312//!     concat!(
313//!         "my.prefix.my.distribution:29:30:31:32|d|#",
314//!         "host:web03.example.com,",
315//!         "beta-test"
316//!     ),
317//!     res.unwrap().as_metric_str()
318//! );
319//! ```
320//!
321//! ### Implemented Traits
322//!
323//! Each of the methods that the Cadence `StatsdClient` struct uses to send
324//! metrics are implemented as a trait. There is also a trait that combines
325//! all of these other traits. If we want, we can just use one of the trait
326//! types to refer to the client instance. This might be useful to you if
327//! you'd like to swap out the actual Cadence client with a dummy version
328//! when you are unit testing your code or want to abstract away all the
329//! implementation details of the client being used behind a trait and
330//! pointer.
331//!
332//! Each of these traits are exported in the prelude module. They are also
333//! available in the main module but aren't typically used like that.
334//!
335//! ```rust,no_run
336//! use std::net::UdpSocket;
337//! use cadence::prelude::*;
338//! use cadence::{StatsdClient, UdpMetricSink, DEFAULT_PORT};
339//!
340//! pub struct User {
341//!     id: u64,
342//!     username: String,
343//!     email: String
344//! }
345//!
346//!
347//! // Here's a simple DAO (Data Access Object) that doesn't do anything but
348//! // uses a metric client to keep track of the number of times the
349//! // 'getUserById' method gets called.
350//! pub struct MyUserDao {
351//!     metrics: Box<dyn MetricClient>
352//! }
353//!
354//!
355//! impl MyUserDao {
356//!     // Create a new instance that will use the StatsdClient
357//!     pub fn new<T: MetricClient + 'static>(metrics: T) -> MyUserDao {
358//!         MyUserDao { metrics: Box::new(metrics) }
359//!     }
360//!
361//!     /// Get a new user by their ID
362//!     pub fn get_user_by_id(&self, id: u64) -> Option<User> {
363//!         self.metrics.count("getUserById", 1);
364//!         None
365//!     }
366//! }
367//!
368//!
369//! // Create a new Statsd client that writes to "metrics.example.com"
370//! let host = ("metrics.example.com", DEFAULT_PORT);
371//! let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
372//! let sink = UdpMetricSink::from(host, socket).unwrap();
373//! let metrics = StatsdClient::from_sink("counter.example", sink);
374//!
375//! // Create a new instance of the DAO that will use the client
376//! let dao = MyUserDao::new(metrics);
377//!
378//! // Try to lookup a user by ID!
379//! match dao.get_user_by_id(123) {
380//!     Some(u) => println!("Found a user!"),
381//!     None => println!("No user!")
382//! };
383//! ```
384//!
385//! ### Quiet Metric Sending and Error Handling
386//!
387//! When sending metrics sometimes you don't really care about the `Result` of
388//! trying to send it or maybe you just don't want to deal with it inline with
389//! the rest of your code. In order to handle this, Cadence allows you to set a
390//! default error handler. This handler is invoked when there are errors sending
391//! metrics so that the calling code doesn't have to deal with them.
392//!
393//! An example of configuring an error handler and an example of when it might
394//! be invoked is given below.
395//!
396//! ```rust,no_run
397//! use cadence::prelude::*;
398//! use cadence::{MetricError, StatsdClient, NopMetricSink};
399//!
400//! fn my_error_handler(err: MetricError) {
401//!     println!("Metric error! {}", err);
402//! }
403//!
404//! let client = StatsdClient::builder("prefix", NopMetricSink)
405//!     .with_error_handler(my_error_handler)
406//!     .build();
407//!
408//! // When sending metrics via the `MetricBuilder` used for assembling tags,
409//! // callers may opt into sending metrics quietly via the `.send()` method
410//! // as opposed to the `.try_send()` method
411//! client.count_with_tags("some.counter", 42)
412//!     .with_tag("region", "us-east-2")
413//!     .send();
414//! ```
415//!
416//! ### Custom Metric Sinks
417//!
418//! The Cadence `StatsdClient` uses implementations of the `MetricSink`
419//! trait to send metrics to a metric server. Most users of the Cadence
420//! library probably want to use the `QueuingMetricSink` wrapping an instance
421//! of the `BufferedMetricSink`.
422//!
423//! However, maybe you want to do something not covered by an existing sink.
424//! An example of creating a custom sink is below.
425//!
426//! ```rust,no_run
427//! use std::io;
428//! use cadence::prelude::*;
429//! use cadence::{StatsdClient, MetricSink, DEFAULT_PORT};
430//!
431//! pub struct MyMetricSink;
432//!
433//!
434//! impl MetricSink for MyMetricSink {
435//!     fn emit(&self, metric: &str) -> io::Result<usize> {
436//!         // Your custom metric sink implementation goes here!
437//!         Ok(0)
438//!     }
439//! }
440//!
441//!
442//! let sink = MyMetricSink;
443//! let client = StatsdClient::from_sink("my.prefix", sink);
444//!
445//! client.count("my.counter.thing", 42);
446//! client.time("my.method.time", 25);
447//! client.count("some.other.counter", 1);
448//! ```
449//!
450//! ### Custom UDP Socket
451//!
452//! Most users of the Cadence `StatsdClient` will be using it to send metrics
453//! over a UDP socket. If you need to customize the socket, for example you
454//! want to use the socket in blocking mode but set a write timeout, you can
455//! do that as demonstrated below.
456//!
457//! ```rust,no_run
458//! use std::net::UdpSocket;
459//! use std::time::Duration;
460//! use cadence::prelude::*;
461//! use cadence::{StatsdClient, UdpMetricSink, DEFAULT_PORT};
462//!
463//! let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
464//! socket.set_write_timeout(Some(Duration::from_millis(1))).unwrap();
465//!
466//! let host = ("metrics.example.com", DEFAULT_PORT);
467//! let sink = UdpMetricSink::from(host, socket).unwrap();
468//! let client = StatsdClient::from_sink("my.prefix", sink);
469//!
470//! client.count("my.counter.thing", 29);
471//! client.time("my.service.call", 214);
472//! client.count("some.event", 33);
473//! client.set("users.uniques", 42);
474//! ```
475//!
476//! ### Unix Sockets
477//!
478//! Cadence also supports using Unix datagram sockets with the `UnixMetricSink`  or
479//! `BufferedUnixMetricSink`. Unix sockets can be used for sending metrics to a server
480//! or agent running on the same machine (physical machine, VM, containers in a pod)
481//! as your application. Unix sockets are somewhat similar to UDP sockets with a few
482//! important differences:
483//!
484//! * Sending metrics on a socket that doesn't exist or is not being listened to will
485//!   result in an error.
486//! * Metrics sent on a connected socket are guaranteed to be delievered (i.e. they are
487//!   reliable as opposed to UDP sockets). However, it's still possible that the metrics
488//!   won't be read by the server due to a variety of environment and server specific
489//!   reasons.
490//!
491//! An example of using the sinks is given below.
492//!
493//! ```rust,no_run
494//! use std::os::unix::net::UnixDatagram;
495//! use cadence::prelude::*;
496//! use cadence::{StatsdClient, BufferedUnixMetricSink};
497//!
498//! let socket = UnixDatagram::unbound().unwrap();
499//! socket.set_nonblocking(true).unwrap();
500//! let sink = BufferedUnixMetricSink::from("/run/statsd.sock", socket);
501//! let client = StatsdClient::from_sink("my.prefix", sink);
502//!
503//! client.count("my.counter.thing", 29);
504//! client.time("my.service.call", 214);
505//! client.count("some.event", 33);
506//! client.set("users.uniques", 42);
507//! ```
508//!
509//! NOTE: This feature is only available on Unix platforms (Linux, BSD, MacOS).
510//!
511
512#![forbid(unsafe_code)]
513#![allow(clippy::uninlined_format_args)]
514
515pub const DEFAULT_PORT: u16 = 8125;
516
517pub use self::builder::MetricBuilder;
518
519pub use self::client::{
520    Counted, CountedExt, Distributed, Gauged, Histogrammed, Metered, MetricClient, Setted, StatsdClient,
521    StatsdClientBuilder, Timed,
522};
523
524pub use self::sinks::{
525    BufferedSpyMetricSink, BufferedUdpMetricSink, MetricSink, NopMetricSink, QueuingMetricSink,
526    QueuingMetricSinkBuilder, SinkStats, SpyMetricSink, UdpMetricSink,
527};
528
529pub use self::types::{
530    Counter, Distribution, ErrorKind, Gauge, Histogram, Meter, Metric, MetricError, MetricResult, Set, Timer,
531};
532
533mod builder;
534mod client;
535pub mod ext;
536mod io;
537pub mod prelude;
538mod sinks;
539mod types;
540
541// Utilities for running integration tests with Unix datagram sockets.
542#[cfg(unix)]
543#[doc(hidden)]
544pub mod test;
545
546// Sinks for sending metrics over Unix datagram sockets
547#[cfg(unix)]
548pub use crate::sinks::{BufferedUnixMetricSink, UnixMetricSink};
549
550mod sealed {
551    pub trait Sealed {}
552}