saluki_metrics/macros.rs
1#[doc(hidden)]
2#[macro_export]
3macro_rules! metric_type_from_lower {
4 // TRACE-level metrics.
5 (trace_counter) => {
6 $crate::reexport::metrics::Counter
7 };
8 (trace_gauge) => {
9 $crate::reexport::metrics::Gauge
10 };
11 (trace_histogram) => {
12 $crate::reexport::metrics::Histogram
13 };
14 // DEBUG-level metrics.
15 (debug_counter) => {
16 $crate::reexport::metrics::Counter
17 };
18 (debug_gauge) => {
19 $crate::reexport::metrics::Gauge
20 };
21 (debug_histogram) => {
22 $crate::reexport::metrics::Histogram
23 };
24 // INFO-level metrics.
25 (counter) => {
26 $crate::reexport::metrics::Counter
27 };
28 (gauge) => {
29 $crate::reexport::metrics::Gauge
30 };
31 (histogram) => {
32 $crate::reexport::metrics::Histogram
33 };
34 ($($other:tt)*) => {
35 ()
36 };
37}
38
39#[doc(hidden)]
40#[macro_export]
41macro_rules! register_metric {
42 // TRACE-level metrics.
43 (trace_counter, $metric_name:expr, $labels:expr) => {
44 $crate::reexport::metrics::counter!(level: $crate::reexport::metrics::Level::TRACE, $metric_name, $labels)
45 };
46 (trace_gauge, $metric_name:expr, $labels:expr) => {
47 $crate::reexport::metrics::gauge!(level: $crate::reexport::metrics::Level::TRACE, $metric_name, $labels)
48 };
49 (trace_histogram, $metric_name:expr, $labels:expr) => {
50 $crate::reexport::metrics::histogram!(level: $crate::reexport::metrics::Level::TRACE, $metric_name, $labels)
51 };
52 // DEBUG-level metrics.
53 (debug_counter, $metric_name:expr, $labels:expr) => {
54 $crate::reexport::metrics::counter!(level: $crate::reexport::metrics::Level::DEBUG, $metric_name, $labels)
55 };
56 (debug_gauge, $metric_name:expr, $labels:expr) => {
57 $crate::reexport::metrics::gauge!(level: $crate::reexport::metrics::Level::DEBUG, $metric_name, $labels)
58 };
59 (debug_histogram, $metric_name:expr, $labels:expr) => {
60 $crate::reexport::metrics::histogram!(level: $crate::reexport::metrics::Level::DEBUG, $metric_name, $labels)
61 };
62 // INFO-level metrics.
63 (counter, $metric_name:expr, $labels:expr) => {
64 $crate::reexport::metrics::counter!(level: $crate::reexport::metrics::Level::INFO, $metric_name, $labels)
65 };
66 (gauge, $metric_name:expr, $labels:expr) => {
67 $crate::reexport::metrics::gauge!(level: $crate::reexport::metrics::Level::INFO, $metric_name, $labels)
68 };
69 (histogram, $metric_name:expr, $labels:expr) => {
70 $crate::reexport::metrics::histogram!(level: $crate::reexport::metrics::Level::INFO, $metric_name, $labels)
71 };
72 ($($other:tt)*) => {
73 compile_error!("metric type must be `counter`, `gauge`, or `histogram` (can be prefixed with `trace_` or `debug_` to set metric level to something other than INFO)");
74 };
75}
76
77/// Creates statically-defined metrics within a dedicated container struct.
78///
79/// In some cases, the metrics needed for a component are well-established and do not require a high-level of dynamism:
80/// perhaps only a single label is needed for all metrics, and the value is known ahead of time. In these cases, it can
81/// be useful to declare the metrics up front, and in a contained way, to avoid having to deal with the string-y metric
82/// names (and any labels) at each and every callsite.
83///
84/// `static_metrics!` allows defining a number of metrics contained within a single struct. The struct can be
85/// initialized with a fixed set of labels, which is used when registering the metrics. The metrics can then be accessed
86/// with simple accessors on the struct, allowing for ergonomic access from the calling code.
87///
88/// # Labels
89///
90/// A fixed set of labels can be configured for all metrics that are registered. These labels have their definition
91/// defined when calling `static_metrics!`, and the label value is provided when initializing the generated struct.
92///
93/// This allows for quickly applying the same set of labels to all metrics defined within the container struct, and
94/// being able to handle them in a strong-typed way right up until the moment where they need to be rendered as strings.
95///
96/// # Levels
97///
98/// In `metrics`, metrics have an inherent "level", similar to logs: trace, debug, info, warn, and error. Levels can be
99/// used to filter metrics based on their importance or severity. For example, a trace-level metric might be only be
100/// required for debugging and should not be sent all the time, while warn- or error-level metrics should be sent all the
101/// time.
102///
103/// We expose the ability to specify the level to use for a metric by specifying it as a prefix to the metric type. See the
104/// below examples for more details. Metrics can be defined at the trace, debug, or info level, and will default to the info
105/// level if no level is specified.
106///
107/// # Examples
108///
109/// ## Basic
110///
111/// ```rust
112/// # use saluki_metrics::static_metrics;
113/// // We are required to provide a name for the struct, as well as the metric prefix to apply to each of the defined metrics.
114/// //
115/// // Naturally, we also have to define metrics, but labels are optionally and can be excluded from the macro usage entirely.
116/// static_metrics!(
117/// name => FrobulatorMetrics,
118/// prefix => frobulator,
119/// labels => [process_id: u32],
120/// metrics => [
121/// counter(successful_frobulations),
122/// ],
123/// );
124///
125/// struct Frobulator {
126/// metrics: FrobulatorMetrics,
127/// }
128///
129/// impl Frobulator {
130/// fn new(process_id: u32) -> Self {
131/// Self {
132/// metrics: FrobulatorMetrics::new(process_id),
133/// }
134/// }
135///
136/// fn frobulate(&self) {
137/// /* Do the frobulation...*/
138/// self.metrics.successful_frobulations().increment(1)
139/// }
140/// }
141/// ```
142///
143/// ## Customized Levels
144///
145/// ```rust
146/// # use saluki_metrics::static_metrics;
147///
148/// // In this example, we define the level of metrics by prefixing them, such that we have two debug metrics
149/// // (`tasks_preempted` and `pending_io_wakeups`) and one trace metric (`task_poll_duration`). Our non-prefixed
150/// // metric, `tasks_completed`, defaults to the INFO level.
151/// static_metrics!(
152/// name => RuntimeMetrics,
153/// prefix => runtime,
154/// metrics => [
155/// counter(tasks_completed),
156/// debug_counter(tasks_preempted),
157/// debug_gauge(pending_io_wakeups),
158/// trace_histogram(task_poll_duration),
159/// ],
160/// );
161/// ```
162#[macro_export]
163macro_rules! static_metrics {
164 (name => $name:ident, prefix => $prefix:ident, metrics => [$($metric_type:ident($metric_name:ident)),+ $(,)?] $(,)?) => {
165 static_metrics!(name => $name, prefix => $prefix, labels => [], metrics => [$($metric_type($metric_name)),+]);
166 };
167 (name => $name:ident, prefix => $prefix:ident, labels => [$($label_key:ident: $label_ty:ty),*], metrics => [$($metric_type:ident($metric_name:ident)),+ $(,)?] $(,)?) => {
168 #[derive(Clone)]
169 struct $name {
170 $(
171 $metric_name: $crate::metric_type_from_lower!($metric_type),
172 )*
173 }
174
175 impl $name {
176 pub fn new($($label_key: $label_ty,)*) -> Self
177 where
178 Self: Sized,
179 $(
180 $label_ty: $crate::Stringable,
181 )*
182 {
183 #[allow(unused_imports)]
184 use $crate::Stringable;
185
186 let labels = vec![
187 $(
188 $crate::reexport::metrics::Label::new(stringify!($label_key), $label_key.to_shared_string()),
189 )*
190 ];
191
192 Self {
193 $(
194 $metric_name: $crate::register_metric!($metric_type, concat!(stringify!($prefix), "_", stringify!($metric_name)), labels.iter()),
195 )*
196 }
197 }
198
199 $crate::reexport::paste! {
200 $(
201 pub fn $metric_name(&self) -> &$crate::metric_type_from_lower!($metric_type) {
202 &self.$metric_name
203 }
204
205 #[doc = "Gets the full name of the `" $metric_name "` metric as it will be registered."]
206 #[doc = ""]
207 #[doc = "This can be useful when testing metrics, as it ensures you can grab the correct metric name to search for."]
208 pub fn [<$metric_name _name>]() -> &'static str {
209 concat!(stringify!($prefix), "_", stringify!($metric_name))
210 }
211 )*
212 }
213 }
214
215 impl ::std::fmt::Debug for $name {
216 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
217 write!(f, stringify!($name))
218 }
219 }
220 };
221}