massive docs tweaks

This commit is contained in:
Toby Lawrence 2020-10-25 16:59:44 -04:00
parent d7efc64ff7
commit 9a3c293633
7 changed files with 413 additions and 193 deletions

View File

@ -28,6 +28,7 @@ name = "macros"
harness = false
[dependencies]
beef = "0.4"
metrics-macros = { version = "0.1.0-alpha.1", path = "../metrics-macros" }
proc-macro-hack = "0.5"
once_cell = "1"

View File

@ -1,4 +1,14 @@
use metrics::{counter, gauge, histogram, increment, Key, Recorder, Unit};
//! This example is part unit test and part demonstration.
//!
//! We show all of the registration macros, as well as all of the "emission" macros, the ones you
//! would actually call to update a metric.
//!
//! We demonstrate the various permutations of values that can be passed in the macro calls, all of
//! which are documented in detail for the respective macro.
use metrics::{
counter, gauge, histogram, increment, register_counter, register_gauge, register_histogram,
Key, Recorder, Unit,
};
#[allow(dead_code)]
static RECORDER: PrintRecorder = PrintRecorder;
@ -56,21 +66,58 @@ fn main() {
let server_name = "web03".to_string();
init_print_logger();
for _ in 0..3 {
increment!("requests_processed");
increment!("requests_processed", "request_type" => "admin");
}
let common_labels = &[("listener", "frontend")];
// Go through registration:
register_counter!("requests_processed", "number of requests processed");
register_counter!("bytes_sent", Unit::Bytes);
register_gauge!("connection_count", common_labels);
register_histogram!(
"svc.execution_time",
Unit::Milliseconds,
"execution time of request handler"
);
register_gauge!("unused_gauge", "service" => "backend");
register_histogram!("unused_histogram", Unit::Seconds, "unused histo", "service" => "middleware");
// All the supported permutations of `increment!`:
increment!("requests_processed");
increment!("requests_processed", "request_type" => "admin");
increment!("requests_processed", "request_type" => "admin", "server" => server_name.clone());
counter!("requests_processed", 1);
counter!("requests_processed", 1, "request_type" => "admin");
counter!("requests_processed", 1, "request_type" => "admin", "server" => server_name.clone());
increment!("requests_processed", common_labels);
increment!(<"requests_processed">);
increment!(<"requests_processed">, "request_type" => "admin");
increment!(<"requests_processed">, "request_type" => "admin", "server" => server_name.clone());
increment!(<"requests_processed">, common_labels);
// All the supported permutations of `counter!`:
counter!("bytes_sent", 64);
counter!("bytes_sent", 64, "listener" => "frontend");
counter!("bytes_sent", 64, "listener" => "frontend", "server" => server_name.clone());
counter!("bytes_sent", 64, common_labels);
counter!(<"bytes_sent">, 64);
counter!(<"bytes_sent">, 64, "listener" => "frontend");
counter!(<"bytes_sent">, 64, "listener" => "frontend", "server" => server_name.clone());
counter!(<"bytes_sent">, 64, common_labels);
// All the supported permutations of `gauge!`:
gauge!("connection_count", 300.0);
gauge!("connection_count", 300.0, "listener" => "frontend");
gauge!("connection_count", 300.0, "listener" => "frontend", "server" => server_name.clone());
histogram!("service.execution_time", 70);
histogram!("service.execution_time", 70, "type" => "users");
histogram!("service.execution_time", 70, "type" => "users", "server" => server_name.clone());
histogram!(<"service.execution_time">, 70);
histogram!(<"service.execution_time">, 70, "type" => "users");
histogram!(<"service.execution_time">, 70, "type" => "users", "server" => server_name.clone());
gauge!("connection_count", 300.0, common_labels);
gauge!(<"connection_count">, 300.0);
gauge!(<"connection_count">, 300.0, "listener" => "frontend");
gauge!(<"connection_count">, 300.0, "listener" => "frontend", "server" => server_name.clone());
gauge!(<"connection_count">, 300.0, common_labels);
// All the supported permutations of `histogram!`:
histogram!("svc.execution_time", 70);
histogram!("svc.execution_time", 70, "type" => "users");
histogram!("svc.execution_time", 70, "type" => "users", "server" => server_name.clone());
histogram!("svc.execution_time", 70, common_labels);
histogram!(<"svc.execution_time">, 70);
histogram!(<"svc.execution_time">, 70, "type" => "users");
histogram!(<"svc.execution_time">, 70, "type" => "users", "server" => server_name.clone());
histogram!(<"svc.execution_time">, 70, common_labels);
}

View File

@ -1,4 +1,7 @@
use std::borrow::Cow;
#[cfg(target_pointer_width = "64")]
use beef::lean::Cow;
#[cfg(not(target_pointer_width = "64"))]
use beef::Cow;
/// An allocation-optimized string.
///
@ -111,6 +114,9 @@ impl Unit {
/// Gets the canonical string label for the given unit.
///
/// For example, the canonical label for Seconds` would be `s`, while for `Nanoseconds, it would
/// be `ns`.
///
/// Not all units have a meaningful display label and so may be empty.
pub fn as_canonical_label(&self) -> &str {
match self {
@ -145,6 +151,8 @@ impl Unit {
}
/// Converts the string representation of a unit back into `Unit` if possible.
///
/// The value passed here should match the output of [`Unit::as_str`].
pub fn from_str(s: &str) -> Option<Unit> {
match s {
"count" => Some(Unit::Count),
@ -189,14 +197,26 @@ impl Unit {
/// Whether or not this unit relates to the measurement of data.
pub fn is_data_based(&self) -> bool {
match self {
Unit::Terabytes | Unit::Gigabytes | Unit::Megabytes | Unit::Kilobytes | Unit::Bytes => {
true
}
Unit::Terabits | Unit::Gigabits | Unit::Megabits | Unit::Kilobits | Unit::Bits => true,
Unit::TerabytesPerSecond | Unit::GigabytesPerSecond | Unit::MegabytesPerSecond => true,
Unit::KilobytesPerSecond | Unit::BytesPerSecond | Unit::TerabitsPerSecond => true,
Unit::GigabitsPerSecond | Unit::MegabitsPerSecond | Unit::KilobitsPerSecond => true,
Unit::BitsPerSecond => true,
Unit::Terabytes
| Unit::Gigabytes
| Unit::Megabytes
| Unit::Kilobytes
| Unit::Bytes
| Unit::Terabits
| Unit::Gigabits
| Unit::Megabits
| Unit::Kilobits
| Unit::Bits
| Unit::TerabytesPerSecond
| Unit::GigabytesPerSecond
| Unit::MegabytesPerSecond
| Unit::KilobytesPerSecond
| Unit::BytesPerSecond
| Unit::TerabitsPerSecond
| Unit::GigabitsPerSecond
| Unit::MegabitsPerSecond
| Unit::KilobitsPerSecond
| Unit::BitsPerSecond => true,
_ => false,
}
}
@ -204,12 +224,16 @@ impl Unit {
/// Whether or not this unit relates to the measurement of data rates.
pub fn is_data_rate_based(&self) -> bool {
match self {
Unit::TerabytesPerSecond | Unit::GigabytesPerSecond | Unit::MegabytesPerSecond => true,
Unit::KilobytesPerSecond
Unit::TerabytesPerSecond
| Unit::GigabytesPerSecond
| Unit::MegabytesPerSecond
| Unit::KilobytesPerSecond
| Unit::BytesPerSecond
| Unit::TerabitsPerSecond
| Unit::Gigabits => true,
Unit::MegabitsPerSecond | Unit::KilobitsPerSecond | Unit::BitsPerSecond => true,
| Unit::Gigabits
| Unit::MegabitsPerSecond
| Unit::KilobitsPerSecond
| Unit::BitsPerSecond => true,
_ => false,
}
}

View File

@ -1,14 +1,14 @@
use crate::{IntoLabels, Label, ScopedString};
use std::{
use core::{
fmt,
hash::{Hash, Hasher},
slice::Iter,
};
/// A metric key data.
/// Inner representation of [`Key`].
///
/// A key data always includes a name, but can optionally include multiple
/// labels used to further describe the metric.
/// While [`Key`] is the type that users will interact with via [`crate::Recorder`], [`KeyData`] is
/// responsible for the actual storage of the name and label data.
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub struct KeyData {
name: ScopedString,
@ -16,7 +16,7 @@ pub struct KeyData {
}
impl KeyData {
/// Creates a `KeyData` from a name.
/// Creates a [`KeyData`] from a name.
pub fn from_name<N>(name: N) -> Self
where
N: Into<ScopedString>,
@ -24,7 +24,7 @@ impl KeyData {
Self::from_name_and_labels(name, Vec::new())
}
/// Creates a `KeyData` from a name and vector of `Label`s.
/// Creates a [`KeyData`] from a name and vector of [`Label`]s.
pub fn from_name_and_labels<N, L>(name: N, labels: L) -> Self
where
N: Into<ScopedString>,
@ -36,12 +36,12 @@ impl KeyData {
}
}
/// Creates a `KeyData` from a static name.
/// Creates a [`KeyData`] from a static name.
///
/// This function is const, so it can be used in a static context.
/// This function is `const`, so it can be used in a static context.
pub const fn from_static_name(name: &'static str) -> Self {
Self {
name: ScopedString::Borrowed(name),
name: ScopedString::const_str(name),
labels: Vec::new(),
}
}
@ -68,12 +68,12 @@ impl KeyData {
self
}
/// Consumes this `Key`, returning the name and any labels.
/// Consumes this [`Key`], returning the name and any labels.
pub fn into_parts(self) -> (ScopedString, Vec<Label>) {
(self.name, self.labels)
}
/// Returns a clone of this key with some additional labels.
/// Clones this [`Key`], and expands the existing set of labels.
pub fn with_extra_labels(&self, extra_labels: Vec<Label>) -> Self {
if extra_labels.is_empty() {
return self.clone();
@ -129,20 +129,20 @@ where
}
}
/// Represents the identifier of a metric.
/// A metric identifier.
///
/// [`Key`] holds either an owned or static reference variant of [`KeyData`].
/// While [`KeyData`] holds the actual name and label data for a metric, [`Key`] works similar to
/// [`std::borrow::Cow`] in that we can either hold an owned version of the key data, or a static
/// reference to key data initialized elsewhere.
///
/// This allows for flexibility in the ways that [`KeyData`] can be passed around
/// and reused, enabling performance improvements in specific situations.
/// This allows for flexibility in the ways that [`KeyData`] can be passed around and reused, which
/// allus to enable performance optimizations in specific circumstances.
#[derive(Debug, Clone)]
pub enum Key {
/// A statically borrowed [`KeyData`].
///
/// If you are capable of keeping a static [`KeyData`] around, this variant can
/// be used to reduce allocations and improve performance.
///
/// The reference is read-only, so you can't modify the underlying data.
Borrowed(&'static KeyData),
/// An owned [`KeyData`].
///
@ -152,7 +152,6 @@ pub enum Key {
}
impl PartialEq for Key {
/// We deliberately hide the differences between the containment types.
fn eq(&self, other: &Self) -> bool {
self.as_ref() == other.as_ref()
}
@ -212,12 +211,6 @@ impl fmt::Display for Key {
}
}
// Here we don't provide generic `From` impls
// (i.e. `impl <T: Into<KeyData>> From<T> for Key`) because the decision whether
// to construct the owned or borrowed ref is important for performance, and
// we want users of this type to explicitly make this decision rather than rely
// on the the magic of `.into()`.
impl From<KeyData> for Key {
fn from(key_data: KeyData) -> Self {
Self::Owned(key_data)
@ -230,9 +223,9 @@ impl From<&'static KeyData> for Key {
}
}
/// A type to simplify management of the static `KeyData`.
/// A thread-safe cell which can only be written to once, for key data.
///
/// Allows for an efficient caching of the `KeyData` at the callsites.
/// Allows for efficient caching of static [`KeyData`] at metric callsites.
pub type OnceKeyData = once_cell::sync::OnceCell<KeyData>;
#[cfg(test)]
@ -275,8 +268,10 @@ mod tests {
fn test_key_eq_and_hash() {
let mut keys = HashMap::new();
let owned_basic = Key::from(KeyData::from_name("name"));
let borrowed_basic = Key::from(BORROWED_BASIC.get_or_init(|| KeyData::from_name("name")));
let owned_basic: Key = KeyData::from_name("name").into();
let borrowed_basic: Key = BORROWED_BASIC
.get_or_init(|| KeyData::from_name("name"))
.into();
assert_eq!(owned_basic, borrowed_basic);
let previous = keys.insert(owned_basic, 42);

View File

@ -1,11 +1,20 @@
use crate::ScopedString;
/// A key/value pair used to further describe a metric.
/// Metadata for a metric key in the for of a key/value pair.
///
/// Metrics are always defined by a name, but can optionally be assigned "labels", key/value pairs
/// that provide metadata about the key. Labels are typically used for differentiating the context
/// of when an where a metric are emitted.
///
/// For example, in a web service, you might wish to label metrics with the user ID responsible for
/// the request currently being processed, or the request path being processedd. If a codepath
/// branched internally -- for example, an optimized path and a fallback path -- you may wish to
/// add a label that tracks which codepath was taken.
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub struct Label(pub(crate) ScopedString, pub(crate) ScopedString);
impl Label {
/// Creates a `Label` from a key and value.
/// Creates a [`Label`] from a key and value.
pub fn new<K, V>(key: K, value: V) -> Self
where
K: Into<ScopedString>,
@ -14,17 +23,17 @@ impl Label {
Label(key.into(), value.into())
}
/// The key of this label.
/// Key of this label.
pub fn key(&self) -> &str {
self.0.as_ref()
}
/// The value of this label.
/// Value of this label.
pub fn value(&self) -> &str {
self.1.as_ref()
}
/// Consumes this `Label`, returning the key and value.
/// Consumes this [`Label`], returning the key and value.
pub fn into_parts(self) -> (ScopedString, ScopedString) {
(self.0, self.1)
}
@ -40,9 +49,9 @@ where
}
}
/// A value that can be converted to `Label`s.
/// A value that can be converted to [`Label`]s.
pub trait IntoLabels {
/// Consumes this value, turning it into a vector of `Label`s.
/// Consumes this value, turning it into a vector of [`Label`]s.
fn into_labels(self) -> Vec<Label>;
}

View File

@ -4,17 +4,43 @@
//! implementation. Libraries can use the metrics API provided by this crate, and the consumer of
//! those libraries can choose the metrics implementation that is most suitable for its use case.
//!
//! If no metrics implementation is selected, the facade falls back to a "noop" implementation that
//! ignores all metrics. The overhead in this case is very small - an atomic load and comparison.
//! # Overview
//! `metrics` exposes two main concepts: emitting a metric, and recording it.
//!
//! # Use
//! The basic use of the facade crate is through the three metrics macros: [`counter!`], [`gauge!`],
//! and [`histogram!`]. These macros correspond to updating a counter, updating a gauge,
//! and updating a histogram.
//! ## Emission
//! Metrics are emitted by utilizing the registration or emission macros. There is a macro for
//! registering and emitting each fundamental metric type:
//! - [`register_counter!`] and [`increment_counter!`] for counters
//! - [`register_gauge!`] and [`update_gauge!`] for gauges
//! - [`register_histogram!`] and [`record_histogram!`] for histograms
//!
//! There is also an [`increment!`] macro, which is shorthand for incrementing a counter by one.
//!
//! In order to register or emit a metric, you need a way to record these events, which is where
//! [`Recorder`] comes into play.
//!
//! ## Recording
//! The [`Recorder`] trait defines the interface between the registration/emission macros, and
//! exporters, which is we refer to concrete implementations of [`Recorder`]. The trait defines
//! what the exporters are doing -- recording -- but ultimately exporters are sending data from your
//! application to somewhere else: whether it be a third-party service or logging via standard out.
//! It's "exporting" the metric data somewhere else besides your application.
//!
//! Each metric type is usually reserved for a specific type of use case, whether it be tracking a
//! single value or allowing the summation of multiple values, and the respective macros elaborate
//! more on the usage and invariants provided by each.
//!
//! # Getting Started
//!
//! ## In libraries
//! Libraries should link only to the `metrics` crate, and use the provided macros to record
//! whatever metrics will be useful to downstream consumers.
//! Libraries need only include the `metrics` crate to emit metrics. When an executable installs a
//! recorder, all included crates which emitting metrics will now emit their metrics to that record,
//! which allows library authors to seamless emit their own metrics without knowing or caring which
//! exporter implementation is chosen, or even if one is installed.
//!
//! In cases where no global recorder is installed, a "noop" recorder lives in its place, which has
//! an incredibly very low overhead: an atomic load and comparison. Libraries can safely instrument
//! their code without fear of ruining baseline performance.
//!
//! ### Examples
//!
@ -38,30 +64,40 @@
//!
//! ## In executables
//!
//! Executables should choose a metrics implementation and initialize it early in the runtime of
//! the program. Metrics implementations will typically include a function to do this. Any
//! metrics recordered before the implementation is initialized will be ignored.
//! Executables, which themselves can emit their own metrics, are intended to install a global
//! recorder so that metrics can actually be recorded and exported somewhere.
//!
//! The executable itself may use the `metrics` crate to record metrics well.
//! Initialization of the global recorder isn't required for macros to function, but any metrics
//! emitted before a global recorder is installed will not be recorded, so early initialization is
//! recommended when possible.
//!
//! ### Warning
//!
//! The metrics system may only be initialized once.
//!
//! # Available metrics implementations
//! For most use cases, you'll be using an off-the-shelf exporter implementation that hooks up to an
//! existing metrics collection system, or interacts with the existing systems/processes that you use.
//!
//! * # Native recorder:
//! * [metrics-exporter-tcp] - outputs metrics to clients over TCP
//! * [metrics-exporter-prometheus] - serves a Prometheus scrape endpoint
//! Out of the box, some exporter implementations are available for you to use:
//!
//! # Implementing a Recorder
//! * [metrics-exporter-tcp] - outputs metrics to clients over TCP
//! * [metrics-exporter-prometheus] - serves a Prometheus scrape endpoint
//!
//! Recorders implement the [`Recorder`] trait. Here's a basic example which writes the
//! metrics in text form via the `log` crate.
//! You can also implement your own recorder if a suitable one doesn't already exist.
//!
//! # Development
//!
//! The primary interface with `metrics` is through the [`Recorder`] trait, so we'll show examples
//! below of the trait and implementation notes.
//!
//! ## Implementing and installing a basic recorder
//!
//! Here's a basic example which writes metrics in text form via the `log` crate.
//!
//! ```rust
//! use log::info;
//! use metrics::{Key, Recorder, Unit};
//! use metrics::SetRecorderError;
//!
//! struct LogRecorder;
//!
@ -84,24 +120,9 @@
//! info!("histogram '{}' -> {}", key, value);
//! }
//! }
//! # fn main() {}
//! ```
//!
//! Recorders are installed by calling the [`set_recorder`] function. Recorders should provide a
//! function that wraps the creation and installation of the recorder:
//!
//! ```rust
//! # use metrics::{Key, Recorder, Unit};
//! # struct LogRecorder;
//! # impl Recorder for LogRecorder {
//! # fn register_counter(&self, _key: Key, _unit: Option<Unit>, _description: Option<&'static str>) {}
//! # fn register_gauge(&self, _key: Key, _unit: Option<Unit>, _description: Option<&'static str>) {}
//! # fn register_histogram(&self, _key: Key, _unit: Option<Unit>, _description: Option<&'static str>) {}
//! # fn increment_counter(&self, _key: Key, _value: u64) {}
//! # fn update_gauge(&self, _key: Key, _value: f64) {}
//! # fn record_histogram(&self, _key: Key, _value: u64) {}
//! # }
//! use metrics::SetRecorderError;
//! // Recorders are installed by calling the [`set_recorder`] function. Recorders should provide a
//! // function that wraps the creation and installation of the recorder:
//!
//! static RECORDER: LogRecorder = LogRecorder;
//!
@ -110,36 +131,89 @@
//! }
//! # fn main() {}
//! ```
//! ## Keys
//!
//! # Use with `std`
//! All metrics are, in essence, the combination of a metric type and metric identifier, such as a
//! histogram called "response_latency". You could conceivably have multiple metrics with the same
//! name, so long as they are of different types.
//!
//! `set_recorder` requires you to provide a `&'static Recorder`, which can be hard to
//! obtain if your recorder depends on some runtime configuration. The `set_boxed_recorder`
//! function is available with the `std` Cargo feature. It is identical to `set_recorder` except
//! that it takes a `Box<Recorder>` rather than a `&'static Recorder`:
//! As the types are enforced/limited by the [`Recorder`] trait itself, the remaining piece is the
//! identifier, which we handle by using [`Key`].
//!
//! ```rust
//! # use metrics::{Key, Recorder, Unit};
//! # struct LogRecorder;
//! # impl Recorder for LogRecorder {
//! # fn register_counter(&self, _key: Key, _unit: Option<Unit>, _description: Option<&'static str>) {}
//! # fn register_gauge(&self, _key: Key, _unit: Option<Unit>, _description: Option<&'static str>) {}
//! # fn register_histogram(&self, _key: Key, _unit: Option<Unit>, _description: Option<&'static str>) {}
//! # fn increment_counter(&self, _key: Key, _value: u64) {}
//! # fn update_gauge(&self, _key: Key, _value: f64) {}
//! # fn record_histogram(&self, _key: Key, _value: u64) {}
//! # }
//! use metrics::SetRecorderError;
//! [`Key`] itself is a wrapper for [`KeyData`], which holds not only the name of a metric, but
//! potentially holds labels for it as well. The name of a metric must always be a literal string.
//! The labels are a key/value pair, where both components are strings as well.
//!
//! # #[cfg(feature = "std")]
//! pub fn init() -> Result<(), SetRecorderError> {
//! metrics::set_boxed_recorder(Box::new(LogRecorder))
//! }
//! # fn main() {}
//! ```
//! Internally, `metrics` uses a clone-on-write "smart pointer" for these values to optimize cases
//! where the values are static strings, which can provide significant performance benefits. These
//! smart pointers can also hold owned `String` values, though, so users can mix and match static
//! strings and owned strings for labels without issue. Metric names, as mentioned above, are always
//! static strings.
//!
//! Two [`Key`] objects can be checked for equality and considered to point to the same metric if
//! they are equal. Equality checks both the name of the key and the labels of a key. Labels are
//! _not_ sorted prior to checking for equality, but insertion order is maintained, so any [`Key`]
//! constructed from the same set of labels in the same order should be equal.
//!
//! It is an implementation detail if a recorder wishes to do an deeper equality check that ignores
//! the order of labels, but practically speaking, metric emission, and thus labels, should be
//! fixed in ordering in nearly all cases, and so it isn't typically a problem.
//!
//! ## Registration
//!
//! Recorders must handle the "registration" of a metric.
//!
//! In practice, registration solves two potential problems: providing metadata for a metric, and
//! creating an entry for a metric even though it has not been emitted yet.
//!
//! Callers may wish to provide a human-readable description of what the metric is, or provide the
//! units the metrics uses. Additionally, users may wish to register their metrics so that they
//! show up in the output of the installed exporter even if the metrics have yet to be emitted.
//! This allows callers to ensure the metrics output is stable, or allows them to expose all of the
//! potential metrics a system has to offer, again, even if they have not all yet been emitted.
//!
//! As you can see from the trait, the registration methods treats the metadata as optional, and
//! the macros allow users to mix and match whichever fields they want to provide.
//!
//! When a metric is registered, the expectation is that it will show up in output with a default
//! value, so, for example, a counter should be initialized to zero, a histogram would have no
//! values, and so on.
//!
//! ## Emission
//!
//! Likewise, records must handle the emission of metrics as well.
//!
//! Comparatively speaking, emission is not too different from registration: you have access to the
//! same [`Key`] as well as the value being emitted.
//!
//! For recorders which temporarily buffer or hold on to values before exporting, a typical approach
//! would be to utilize atomic variables for the storage. For counters and gauges, this can be done
//! simply by using types like [`AtomicU64`](std::sync::atomic::AtomicU64). For histograms, this can be
//! slightly tricky as you must hold on to all of the distinct values. In our helper crate,
//! [metrics-util], we've provided a type called [AtomicBucket]. For exporters that will want to get
//! all of the current values in a batch, while clearing the bucket so that values aren't processed
//! again, [AtomicBucket] provides a simple interface to do so, as well as optimized performance on
//! both the insertion and read side.
//!
//! ## Installing recorders
//!
//! In order to actually use an exporter, it must be installed as the "global" recorder. This is a
//! static recorder that the registration and emission macros refer to behind-the-scenes. `metrics`
//! provides a few methods to do so: [`set_recorder`], [`set_boxed_recorder`], and [`set_recorder_racy`].
//!
//! Primarily, you'll use [`set_boxed_recorder`] to pass a boxed version of the exporter to be
//! installed. This is due to the fact that most exporters won't be able to be constructed
//! statically. If you could construct your exporter statically, though, then you could instead
//! choose [`set_recorder`].
//!
//! Similarly, [`set_recorder_racy`] takes a static reference, but is also not thread safe, and
//! should only be used on platforms which do not support atomic operations, such as embedded
//! environments.
//!
//! [metrics-exporter-tcp]: https://docs.rs/metrics-exporter-tcp
//! [metrics-exporter-prometheus]: https://docs.rs/metrics-exporter-prometheus
//! [metrics-util]: https://docs.rs/metrics-util
//! [AtomicBucket]: https://docs.rs/metrics-util/0.4.0-alpha.6/metrics_util/struct.AtomicBucket.html
#![deny(missing_docs)]
use proc_macro_hack::proc_macro_hack;
@ -157,21 +231,29 @@ pub use self::recorder::*;
/// Registers a counter.
///
/// Counters represent a single value that can only be incremented over time, or reset to zero.
/// Counters represent a single monotonic value, which means the value can only be incremented, not
/// decremented, and always starts out with an initial value of zero.
///
/// Metrics can be registered with an optional description. Whether or not the installed recorder
/// does anything with the description is implementation defined. Labels can also be specified
/// when registering a metric.
///
/// Counters, when registered, start at zero.
/// Metrics can be registered with an optional unit and description. Whether or not the installed
/// recorder does anything with the description is implementation defined. Labels can also be
/// specified when registering a metric.
///
/// # Scoped versus unscoped
/// Metrics can be unscoped or scoped, where the scoping is derived by the current module the call
/// is taking place in. This scope is used as a prefix to the provided metric name.
/// is taking place in. This scope is used as a prefix to the provided metric name. For example,
/// take an example metric of "my_counter_metric" being emitted in a binary application "service":
///
/// - unscoped: `my_counter_metric`
/// - scoped: `service.my_counter_metric`
///
/// If the metric was emitted in a nested module within the application, the scope would represent
/// the full module path to where the metric is being emitted, such as
/// `service.some_module.my_counter_metric`.
///
/// # Example
/// ```
/// # use metrics::register_counter;
/// # use metrics::Unit;
/// # fn main() {
/// // A regular, unscoped counter:
/// register_counter!("some_metric_name");
@ -179,17 +261,25 @@ pub use self::recorder::*;
/// // A scoped counter. This inherits a scope derived by the current module:
/// register_counter!(<"some_metric_name">);
///
/// // Providing a unit for a counter:
/// register_counter!("some_metric_name", Unit::Bytes);
///
/// // Providing a description for a counter:
/// register_counter!("some_metric_name", "number of woopsy daisies");
/// register_counter!("some_metric_name", "total number of bytes");
///
/// // Specifying labels:
/// register_counter!("some_metric_name", "service" => "http");
///
/// // And all combined:
/// register_counter!("some_metric_name", "number of woopsy daisies", "service" => "http");
/// register_counter!(<"some_metric_name">, "number of woopsy daisies", "service" => "http");
/// // We can combine the units, description, and labels arbitrarily:
/// register_counter!("some_metric_name", Unit::Bytes, "total number of bytes");
/// register_counter!("some_metric_name", Unit::Bytes, "service" => "http");
/// register_counter!("some_metric_name", "total number of bytes", "service" => "http");
///
/// // And just for an alternative form of passing labels:
/// // And all combined:
/// register_counter!("some_metric_name", Unit::Bytes, "number of woopsy daisies", "service" => "http");
///
/// /// We can also pass labels by giving a vector or slice of key/value pairs. In this scenario,
/// // a unit or description can still be passed in their respective positions:
/// let dynamic_val = "woo";
/// let labels = [("dynamic_key", format!("{}!", dynamic_val))];
/// register_counter!("some_metric_name", &labels);
@ -200,21 +290,29 @@ pub use metrics_macros::register_counter;
/// Registers a gauge.
///
/// Gauges represent a single value that can go up or down over time.
/// Gauges represent a single value that can go up or down over time, and always starts out with an
/// initial value of zero.
///
/// Metrics can be registered with an optional description. Whether or not the installed recorder
/// does anything with the description is implementation defined. Labels can also be specified
/// when registering a metric.
///
/// Gauges, when registered, start at zero.
/// Metrics can be registered with an optional unit and description. Whether or not the installed
/// recorder does anything with the description is implementation defined. Labels can also be
/// specified when registering a metric.
///
/// # Scoped versus unscoped
/// Metrics can be unscoped or scoped, where the scoping is derived by the current module the call
/// is taking place in. This scope is used as a prefix to the provided metric name.
/// is taking place in. This scope is used as a prefix to the provided metric name. For example,
/// take an example metric of "my_gauge_metric" being emitted in a binary application "service":
///
/// - unscoped: `my_gauge_metric`
/// - scoped: `service.my_gauge_metric`
///
/// If the metric was emitted in a nested module within the application, the scope would represent
/// the full module path to where the metric is being emitted, such as
/// `service.some_module.my_gauge_metric`.
///
/// # Example
/// ```
/// # use metrics::register_gauge;
/// # use metrics::Unit;
/// # fn main() {
/// // A regular, unscoped gauge:
/// register_gauge!("some_metric_name");
@ -222,17 +320,25 @@ pub use metrics_macros::register_counter;
/// // A scoped gauge. This inherits a scope derived by the current module:
/// register_gauge!(<"some_metric_name">);
///
/// // Providing a unit for a gauge:
/// register_gauge!("some_metric_name", Unit::Bytes);
///
/// // Providing a description for a gauge:
/// register_gauge!("some_metric_name", "number of woopsy daisies");
/// register_gauge!("some_metric_name", "total number of bytes");
///
/// // Specifying labels:
/// register_gauge!("some_metric_name", "service" => "http");
///
/// // And all combined:
/// register_gauge!("some_metric_name", "number of woopsy daisies", "service" => "http");
/// register_gauge!(<"some_metric_name">, "number of woopsy daisies", "service" => "http");
/// // We can combine the units, description, and labels arbitrarily:
/// register_gauge!("some_metric_name", Unit::Bytes, "total number of bytes");
/// register_gauge!("some_metric_name", Unit::Bytes, "service" => "http");
/// register_gauge!("some_metric_name", "total number of bytes", "service" => "http");
///
/// // And just for an alternative form of passing labels:
/// // And all combined:
/// register_gauge!("some_metric_name", Unit::Bytes, "total number of bytes", "service" => "http");
///
/// // We can also pass labels by giving a vector or slice of key/value pairs. In this scenario,
/// // a unit or description can still be passed in their respective positions:
/// let dynamic_val = "woo";
/// let labels = [("dynamic_key", format!("{}!", dynamic_val))];
/// register_gauge!("some_metric_name", &labels);
@ -243,21 +349,29 @@ pub use metrics_macros::register_gauge;
/// Records a histogram.
///
/// Histograms measure the distribution of values for a given set of measurements.
/// Histograms measure the distribution of values for a given set of measurements, and start with no
/// initial values.
///
/// Metrics can be registered with an optional description. Whether or not the installed recorder
/// does anything with the description is implementation defined. Labels can also be specified
/// when registering a metric.
///
/// Histograms, when registered, start at zero.
/// Metrics can be registered with an optional unit and description. Whether or not the installed
/// recorder does anything with the description is implementation defined. Labels can also be
/// specified when registering a metric.
///
/// # Scoped versus unscoped
/// Metrics can be unscoped or scoped, where the scoping is derived by the current module the call
/// is taking place in. This scope is used as a prefix to the provided metric name.
/// is taking place in. This scope is used as a prefix to the provided metric name. For example,
/// take an example metric of "my_histogram_metric" being emitted in a binary application "service":
///
/// - unscoped: `my_histogram_metric`
/// - scoped: `service.my_histogram_metric`
///
/// If the metric was emitted in a nested module within the application, the scope would represent
/// the full module path to where the metric is being emitted, such as
/// `service.some_module.my_histogram_metric`.
///
/// # Example
/// ```
/// # use metrics::register_histogram;
/// # use metrics::Unit;
/// # fn main() {
/// // A regular, unscoped histogram:
/// register_histogram!("some_metric_name");
@ -265,17 +379,25 @@ pub use metrics_macros::register_gauge;
/// // A scoped histogram. This inherits a scope derived by the current module:
/// register_histogram!(<"some_metric_name">);
///
/// // Providing a unit for a histogram:
/// register_histogram!("some_metric_name", Unit::Nanoseconds);
///
/// // Providing a description for a histogram:
/// register_histogram!("some_metric_name", "number of woopsy daisies");
/// register_histogram!("some_metric_name", "request handler duration");
///
/// // Specifying labels:
/// register_histogram!("some_metric_name", "service" => "http");
///
/// // And all combined:
/// register_histogram!("some_metric_name", "number of woopsy daisies", "service" => "http");
/// register_histogram!(<"some_metric_name">, "number of woopsy daisies", "service" => "http");
/// // We can combine the units, description, and labels arbitrarily:
/// register_histogram!("some_metric_name", Unit::Nanoseconds, "request handler duration");
/// register_histogram!("some_metric_name", Unit::Nanoseconds, "service" => "http");
/// register_histogram!("some_metric_name", "request handler duration", "service" => "http");
///
/// // And just for an alternative form of passing labels:
/// // And all combined:
/// register_histogram!("some_metric_name", Unit::Nanoseconds, "request handler duration", "service" => "http");
///
/// // We can also pass labels by giving a vector or slice of key/value pairs. In this scenario,
/// // a unit or description can still be passed in their respective positions:
/// let dynamic_val = "woo";
/// let labels = [("dynamic_key", format!("{}!", dynamic_val))];
/// register_histogram!("some_metric_name", &labels);
@ -284,13 +406,22 @@ pub use metrics_macros::register_gauge;
#[proc_macro_hack]
pub use metrics_macros::register_histogram;
/// Increments a counter.
/// Increments a counter by one.
///
/// Counters represent a single value that can only be incremented over time, or reset to zero.
/// Counters represent a single monotonic value, which means the value can only be incremented, not
/// decremented, and always starts out with an initial value of zero.
///
/// # Scoped versus unscoped
/// Metrics can be unscoped or scoped, where the scoping is derived by the current module the call
/// is taking place in. This scope is used as a prefix to the provided metric name.
/// is taking place in. This scope is used as a prefix to the provided metric name. For example,
/// take an example metric of "my_counter_metric" being emitted in a binary application "service":
///
/// - unscoped: `my_counter_metric`
/// - scoped: `service.my_counter_metric`
///
/// If the metric was emitted in a nested module within the application, the scope would represent
/// the full module path to where the metric is being emitted, such as
/// `service.some_module.my_counter_metric`.
///
/// # Example
/// ```
@ -305,11 +436,7 @@ pub use metrics_macros::register_histogram;
/// // Specifying labels:
/// increment!("some_metric_name", "service" => "http");
///
/// // And all combined:
/// increment!("some_metric_name", "service" => "http");
/// increment!(<"some_metric_name">, "service" => "http");
///
/// // And just for an alternative form of passing labels:
/// // We can also pass labels by giving a vector or slice of key/value pairs:
/// let dynamic_val = "woo";
/// let labels = [("dynamic_key", format!("{}!", dynamic_val))];
/// increment!("some_metric_name", &labels);
@ -320,11 +447,20 @@ pub use metrics_macros::increment;
/// Increments a counter.
///
/// Counters represent a single value that can only be incremented over time, or reset to zero.
/// Counters represent a single monotonic value, which means the value can only be incremented, not
/// decremented, and always starts out with an initial value of zero.
///
/// # Scoped versus unscoped
/// Metrics can be unscoped or scoped, where the scoping is derived by the current module the call
/// is taking place in. This scope is used as a prefix to the provided metric name.
/// is taking place in. This scope is used as a prefix to the provided metric name. For example,
/// take an example metric of "my_counter_metric" being emitted in a binary application "service":
///
/// - unscoped: `my_counter_metric`
/// - scoped: `service.my_counter_metric`
///
/// If the metric was emitted in a nested module within the application, the scope would represent
/// the full module path to where the metric is being emitted, such as
/// `service.some_module.my_counter_metric`.
///
/// # Example
/// ```
@ -339,11 +475,7 @@ pub use metrics_macros::increment;
/// // Specifying labels:
/// counter!("some_metric_name", 12, "service" => "http");
///
/// // And all combined:
/// counter!("some_metric_name", 12, "service" => "http");
/// counter!(<"some_metric_name">, 12, "service" => "http");
///
/// // And just for an alternative form of passing labels:
/// // We can also pass labels by giving a vector or slice of key/value pairs:
/// let dynamic_val = "woo";
/// let labels = [("dynamic_key", format!("{}!", dynamic_val))];
/// counter!("some_metric_name", 12, &labels);
@ -354,11 +486,20 @@ pub use metrics_macros::counter;
/// Updates a gauge.
///
/// Gauges represent a single value that can go up or down over time.
/// Gauges represent a single value that can go up or down over time, and always starts out with an
/// initial value of zero.
///
/// # Scoped versus unscoped
/// Metrics can be unscoped or scoped, where the scoping is derived by the current module the call
/// is taking place in. This scope is used as a prefix to the provided metric name.
/// is taking place in. This scope is used as a prefix to the provided metric name. For example,
/// take an example metric of "my_gauge_metric" being emitted in a binary application "service":
///
/// - unscoped: `my_gauge_metric`
/// - scoped: `service.my_gauge_metric`
///
/// If the metric was emitted in a nested module within the application, the scope would represent
/// the full module path to where the metric is being emitted, such as
/// `service.some_module.my_gauge_metric`.
///
/// # Example
/// ```
@ -373,11 +514,7 @@ pub use metrics_macros::counter;
/// // Specifying labels:
/// gauge!("some_metric_name", 66.6666, "service" => "http");
///
/// // And all combined:
/// gauge!("some_metric_name", 55.5555, "service" => "http");
/// gauge!(<"some_metric_name">, 11.1111, "service" => "http");
///
/// // And just for an alternative form of passing labels:
/// // We can also pass labels by giving a vector or slice of key/value pairs:
/// let dynamic_val = "woo";
/// let labels = [("dynamic_key", format!("{}!", dynamic_val))];
/// gauge!("some_metric_name", 42.42, &labels);
@ -388,11 +525,20 @@ pub use metrics_macros::gauge;
/// Records a histogram.
///
/// Histograms measure the distribution of values for a given set of measurements.
/// Histograms measure the distribution of values for a given set of measurements, and start with no
/// initial values.
///
/// # Scoped versus unscoped
/// Metrics can be unscoped or scoped, where the scoping is derived by the current module the call
/// is taking place in. This scope is used as a prefix to the provided metric name.
/// is taking place in. This scope is used as a prefix to the provided metric name. For example,
/// take an example metric of "my_histogram_metric" being emitted in a binary application "service":
///
/// - unscoped: `my_histogram_metric`
/// - scoped: `service.my_histogram_metric`
///
/// If the metric was emitted in a nested module within the application, the scope would represent
/// the full module path to where the metric is being emitted, such as
/// `service.some_module.my_histogram_metric`.
///
/// # Implicit conversions
/// Histograms are represented as `u64` values, but often come from another source, such as a time
@ -416,16 +562,11 @@ pub use metrics_macros::gauge;
///
/// // A scoped histogram. This inherits a scope derived by the current module:
/// histogram!(<"some_metric_name">, 38);
/// histogram!(<"some_metric_name">, d);
///
/// // Specifying labels:
/// histogram!("some_metric_name", 38, "service" => "http");
///
/// // And all combined:
/// histogram!("some_metric_name", d, "service" => "http");
/// histogram!(<"some_metric_name">, 57, "service" => "http");
///
/// // And just for an alternative form of passing labels:
/// // We can also pass labels by giving a vector or slice of key/value pairs:
/// let dynamic_val = "woo";
/// let labels = [("dynamic_key", format!("{}!", dynamic_val))];
/// histogram!("some_metric_name", 1337, &labels);

View File

@ -1,6 +1,6 @@
use crate::{Key, Unit};
use std::fmt;
use std::sync::atomic::{AtomicUsize, Ordering};
use core::fmt;
use core::sync::atomic::{AtomicUsize, Ordering};
static mut RECORDER: &'static dyn Recorder = &NoopRecorder;
static STATE: AtomicUsize = AtomicUsize::new(0);
@ -12,13 +12,16 @@ const INITIALIZED: usize = 2;
static SET_RECORDER_ERROR: &str =
"attempted to set a recorder after the metrics system was already initialized";
/// A value that records metrics behind the facade.
/// A trait for registering and recording metrics.
///
/// This is the core trait that allows interoperability between exporter implementations and the
/// macros provided by `metrics`.
pub trait Recorder {
/// Registers a counter.
///
/// Callers may provide the unit or a description of the counter being registered. Whether or
/// not a metric can be reregistered to provide a unit/description, if one was already passed
/// or not, as well as how descriptions are used by the underlying recorder, is an
/// or not, as well as how units/descriptions are used by the underlying recorder, is an
/// implementation detail.
fn register_counter(&self, key: Key, unit: Option<Unit>, description: Option<&'static str>);
@ -26,7 +29,7 @@ pub trait Recorder {
///
/// Callers may provide the unit or a description of the gauge being registered. Whether or
/// not a metric can be reregistered to provide a unit/description, if one was already passed
/// or not, as well as how descriptions are used by the underlying recorder, is an
/// or not, as well as how units/descriptions are used by the underlying recorder, is an
/// implementation detail.
fn register_gauge(&self, key: Key, unit: Option<Unit>, description: Option<&'static str>);
@ -34,7 +37,7 @@ pub trait Recorder {
///
/// Callers may provide the unit or a description of the histogram being registered. Whether or
/// not a metric can be reregistered to provide a unit/description, if one was already passed
/// or not, as well as how descriptions are used by the underlying recorder, is an
/// or not, as well as how units/descriptions are used by the underlying recorder, is an
/// implementation detail.
fn register_histogram(&self, key: Key, unit: Option<Unit>, description: Option<&'static str>);