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}