1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
//! # Fast, ergonomic metrics for Rust!
//!
//! Metered helps you measure the performance of your programs in production.
//! Inspired by Coda Hale's Java metrics library, Metered makes live
//! measurements easy by providing measurement declarative and procedural
//! macros, and a variety of useful metrics ready out-of-the-box:
//! * [`HitCount`]: a counter tracking how much a piece of code was hit.
//! * [`ErrorCount`]: a counter tracking how many errors were returned -- (works
//! on any expression returning a std `Result`)
//! * [`InFlight`]: a gauge tracking how many requests are active
//! * [`ResponseTime`]: statistics backed by an HdrHistogram of the duration of
//! an expression
//! * [`Throughput`]: statistics backed by an HdrHistogram of how many times an
//! expression is called per second.
//!
//! These metrics are usually applied to methods, using provided procedural
//! macros that generate the boilerplate.
//!
//! To achieve higher performance, these stock metrics can be customized to use
//! non-thread safe (`!Sync`/`!Send`) datastructures, but they default to
//! thread-safe datastructures implemented using lock-free strategies where
//! possible. This is an ergonomical choice to provide defaults that work in all
//! situations.
//!
//! Metered is designed as a zero-overhead abstraction -- in the sense that the
//! higher-level ergonomics should not cost over manually adding metrics.
//! Notably, stock metrics will *not* allocate memory after they're initialized
//! the first time. However, they are triggered at every method call and it can
//! be interesting to use lighter metrics (e.g
//! [`HitCount`]) in hot code paths and favour
//! heavier metrics ([`Throughput`],
//! [`ResponseTime`]) in higher-level entry
//! points.
//!
//! If a metric you need is missing, or if you want to customize a metric (for
//! instance, to track how many times a specific error occurs, or react
//! depending on your return type), it is possible to implement your own metrics
//! simply by implementing the [`Metric`] trait .
//!
//! Metered does not use statics or shared global state. Instead, it lets you
//! either build your own metric registry using the metrics you need, or can
//! generate a metric registry for you using method attributes. Metered will
//! generate one registry per `impl` block annotated with the `metered`
//! attribute, under the name provided as the `registry` parameter. By default,
//! Metered will expect the registry to be accessed as `self.metrics` but the
//! expression can be overridden with the `registry_expr` attribute parameter.
//! See the demos for more examples.
//!
//! Metered will generate metric registries that derive [`std::fmt::Debug`] and
//! [`serde::Serialize`] to extract your metrics easily. Metered generates one
//! sub-registry per method annotated with the `measure` attribute, hence
//! organizing metrics hierarchically. This ensures access time to metrics in
//! generated registries is always constant (and, when possible,
//! cache-friendly), without any overhead other than the metric itself.
//!
//! Metered will happily measure any method, whether it is `async` or not, and
//! the metrics will work as expected (e.g,
//! [`ResponseTime`] will return the completion
//! time across `await`'ed invocations).
//!
//! Metered's serialized metrics can be used in conjunction with
//! [`serde_prometheus`](https://github.com/w4/serde_prometheus) to publish
//! metrics to Prometheus.
//!
//! ## Example using procedural macros (recommended)
//!
//! ```
//! # extern crate metered;
//! # extern crate rand;
//!
//! use metered::{metered, Throughput, HitCount};
//!
//! #[derive(Default, Debug)]
//! pub struct Biz {
//! metrics: BizMetrics,
//! }
//!
//! #[metered::metered(registry = BizMetrics)]
//! impl Biz {
//! #[measure([HitCount, Throughput])]
//! pub fn biz(&self) {
//! let delay = std::time::Duration::from_millis(rand::random::<u64>() % 200);
//! std::thread::sleep(delay);
//! }
//! }
//!
//! # fn main() {
//! # }
//! ```
//!
//! In the snippet above, we will measure the
//! [`HitCount`] and
//! [`Throughput`] of the `biz` method.
//!
//! This works by first annotating the `impl` block with the `metered`
//! annotation and specifying the name Metered should give to the metric
//! registry (here `BizMetrics`). Later, Metered will assume the expression to
//! access that repository is `self.metrics`, hence we need a `metrics` field
//! with the `BizMetrics` type in `Biz`. It would be possible to use another
//! field name by specificying another registry expression, such as
//! `#[metered(registry = BizMetrics, registry_expr = self.my_custom_metrics)]`.
//!
//! Then, we must annotate which methods we wish to measure using the `measure`
//! attribute, specifying the metrics we wish to apply: the metrics here are
//! simply types of structures implementing the `Metric` trait, and you can
//! define your own. Since there is no magic, we must ensure `self.metrics` can
//! be accessed, and this will only work on methods with a `&self` or `&mut
//! self` receiver.
//!
//! ## Example of manually using metrics
//!
//! ```
//! use metered::{measure, HitCount, ErrorCount};
//!
//! #[derive(Default, Debug)]
//! struct TestMetrics {
//! hit_count: HitCount,
//! error_count: ErrorCount,
//! }
//!
//! fn test(should_fail: bool, metrics: &TestMetrics) -> Result<u32, &'static str> {
//! let hit_count = &metrics.hit_count;
//! let error_count = &metrics.error_count;
//! measure!(hit_count, {
//! measure!(error_count, {
//! if should_fail {
//! Err("Failed!")
//! } else {
//! Ok(42)
//! }
//! })
//! })
//! }
//! ```
//!
//! The code above shows how different metrics compose, and in general the kind
//! of boilerplate generated by the `#[metered]` procedural macro.
#![deny(missing_docs)]
#![deny(warnings)]
pub mod atomic;
pub mod clear;
pub mod common;
pub mod hdr_histogram;
pub mod int_counter;
pub mod int_gauge;
pub mod metric;
pub(crate) mod num_wrapper;
pub mod time_source;
pub use common::{ErrorCount, HitCount, InFlight, ResponseTime, Throughput};
pub use metered_macro::{error_count, metered};
pub use metric::{Counter, Gauge, Histogram, Metric};
/// Re-export this type so 3rd-party crates don't need to depend on the
/// `aspect-rs` crate.
pub use aspect::Enter;
/// The `measure!` macro takes a reference to a metric and an expression.
///
/// It applies the metric and the expression is returned unchanged.
#[macro_export]
macro_rules! measure {
($metric:expr, $e:expr) => {{
let metric = $metric;
let guard = $crate::metric::ExitGuard::new(metric);
let mut result = $e;
guard.on_result(&mut result);
result
}};
}
/// Serializer for values within a struct generated by
/// `metered::metered_error_variants` that adds an `error_kind` label when being
/// serialized by `serde_prometheus`.
pub fn error_variant_serializer<S: serde::Serializer, T: serde::Serialize>(
value: &T,
serializer: S,
) -> Result<S::Ok, S::Error> {
serializer.serialize_newtype_struct("!|variant[::]==<", value)
}
/// Serializer for values within a struct generated by
/// `metered::metered_error_variants` that adds an `error_kind` label when being
/// serialized by `serde_prometheus`. If the `value` has been cleared. This
/// operation is a no-op and the value wont be written to the `serializer`.
pub fn error_variant_serializer_skip_cleared<
S: serde::Serializer,
T: serde::Serialize + clear::Clearable,
>(
value: &T,
serializer: S,
) -> Result<S::Ok, S::Error> {
if value.is_cleared() {
serializer.serialize_none()
} else {
error_variant_serializer(value, serializer)
}
}
/// Trait applied to error enums by `#[metered::error_count]` to identify
/// generated error count structs.
pub trait ErrorBreakdown<C: metric::Counter> {
/// The generated error count struct.
type ErrorCount;
}