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;
}