From 78b83fd6e9330d2b986aad17ed4fd793dd07189e Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Sun, 14 Mar 2021 11:25:27 +1300 Subject: [PATCH] metrics: Enable gauges with fully-static labels --- src/rust/include/rust/metrics.h | 72 +++++++++++++++++++++++++-------- src/rust/src/metrics_ffi.rs | 27 ++++++++++++- 2 files changed, 81 insertions(+), 18 deletions(-) diff --git a/src/rust/include/rust/metrics.h b/src/rust/include/rust/metrics.h index 8c8c53c90..c3133981e 100644 --- a/src/rust/include/rust/metrics.h +++ b/src/rust/include/rust/metrics.h @@ -34,13 +34,20 @@ typedef struct MetricsKey MetricsKey; /// Creates a metrics callsite. /// +/// This API supports labels that MUST have static values. For non-static label +/// values, use `metrics_key`. +/// /// You should usually call one of the helper macros such as `MetricsCounter` /// instead of calling this directly. /// /// This MUST ONLY be called to assign a `static MetricsCallsite*`, and all /// string arguments MUST be static `const char*` constants, and MUST be valid /// UTF-8. -MetricsCallsite* metrics_callsite(const char* name); +MetricsCallsite* metrics_callsite( + const char* name, + const char* const* label_names, + const char* const* label_values, + size_t labels_len); /// Creates a metrics key. /// @@ -133,20 +140,26 @@ void metrics_record_histogram(MetricsKey* callsite, double value); #ifdef __cplusplus // Constructs a metrics callsite. // -// The 'static constexpr' hack ensures that name is a compile-time constant -// with static storage duration. The output of this macro MUST be stored as a -// static MetricsCallsite*. -#define M_CALLSITE(name) ([&] { \ - static constexpr const char* _m_name = name; \ - return metrics_callsite(_m_name); \ +// The 'static constexpr' hack ensures that all arguments are compile-time +// constants with static storage duration. The output of this macro MUST be +// stored as a static MetricsCallsite*. +#define M_CALLSITE(name, label_names, label_values) ([&] { \ + static constexpr const char* _m_name = name; \ + static constexpr const char* const* _m_label_names = \ + label_names; \ + static constexpr const char* const* _m_label_values = \ + label_values; \ + return metrics_callsite( \ + _m_name, _m_label_names, _m_label_values, \ + T_ARRLEN(label_names)); \ }()) #else // Constructs a metrics callsite. // -// name MUST be a static constant, and the output of this macro MUST be stored -// as a static MetricsCallsite*. -#define M_CALLSITE(name) \ - metrics_callsite(name) +// All arguments MUST be static constants, and the output of this macro MUST be +// stored as a static MetricsCallsite*. +#define M_CALLSITE(name, label_names, label_values) \ + metrics_callsite(name, label_names, label_values, T_ARRLEN(label_names)) #endif // Constructs a metrics key. @@ -171,7 +184,9 @@ void metrics_record_histogram(MetricsKey* callsite, double value); #define MetricsCounter(name, value, ...) \ do { \ IFE(__VA_ARGS__) \ - (static MetricsCallsite* CALLSITE = M_CALLSITE(name); \ + (static constexpr const char* const EMPTY[] = {}; \ + static MetricsCallsite* CALLSITE = \ + M_CALLSITE(name, EMPTY, EMPTY); \ metrics_static_increment_counter(CALLSITE, value);) \ IFN(__VA_ARGS__)(const char* M_LABELS[] = \ {T_FIELD_NAMES(__VA_ARGS__)}; \ @@ -200,7 +215,9 @@ void metrics_record_histogram(MetricsKey* callsite, double value); #define MetricsGauge(name, value, ...) \ do { \ IFE(__VA_ARGS__) \ - (static MetricsCallsite* CALLSITE = M_CALLSITE(name); \ + (static constexpr const char* const EMPTY[] = {}; \ + static MetricsCallsite* CALLSITE = \ + M_CALLSITE(name, EMPTY, EMPTY); \ metrics_static_update_gauge(CALLSITE, value);) \ IFN(__VA_ARGS__)(const char* M_LABELS[] = \ {T_FIELD_NAMES(__VA_ARGS__)}; \ @@ -211,6 +228,23 @@ void metrics_record_histogram(MetricsKey* callsite, double value); metrics_update_gauge(KEY, value);) \ } while (0) +/// Updates a gauge with optional static labels. +/// +/// Gauges represent a single value that can go up or down over time, and always +/// starts out with an initial value of zero. +/// +/// name MUST be a static constant, and all strings MUST be valid UTF-8. +#define MetricsStaticGauge(name, value, ...) \ + do { \ + static constexpr const char* M_LABELS[] = \ + {T_FIELD_NAMES(__VA_ARGS__)}; \ + static constexpr const char* M_VALUES[] = \ + {T_FIELD_VALUES(__VA_ARGS__)}; \ + static MetricsCallsite* CALLSITE = \ + M_CALLSITE(name, M_LABELS, M_VALUES); \ + metrics_static_update_gauge(CALLSITE, value); \ + } while (0) + /// Increments a gauge. /// /// Gauges represent a single value that can go up or down over time, and always @@ -220,7 +254,9 @@ void metrics_record_histogram(MetricsKey* callsite, double value); #define MetricsIncrementGauge(name, value, ...) \ do { \ IFE(__VA_ARGS__) \ - (static MetricsCallsite* CALLSITE = M_CALLSITE(name); \ + (static constexpr const char* const EMPTY[] = {}; \ + static MetricsCallsite* CALLSITE = \ + M_CALLSITE(name, EMPTY, EMPTY); \ metrics_static_increment_gauge(CALLSITE, value);) \ IFN(__VA_ARGS__)(const char* M_LABELS[] = \ {T_FIELD_NAMES(__VA_ARGS__)}; \ @@ -240,7 +276,9 @@ void metrics_record_histogram(MetricsKey* callsite, double value); #define MetricsDecrementGauge(name, value, ...) \ do { \ IFE(__VA_ARGS__) \ - (static MetricsCallsite* CALLSITE = M_CALLSITE(name); \ + (static constexpr const char* const EMPTY[] = {}; \ + static MetricsCallsite* CALLSITE = \ + M_CALLSITE(name, EMPTY, EMPTY); \ metrics_static_decrement_gauge(CALLSITE, value);) \ IFN(__VA_ARGS__)(const char* M_LABELS[] = \ {T_FIELD_NAMES(__VA_ARGS__)}; \ @@ -260,7 +298,9 @@ void metrics_record_histogram(MetricsKey* callsite, double value); #define MetricsHistogram(name, value, ...) \ do { \ IFE(__VA_ARGS__) \ - (static MetricsCallsite* CALLSITE = M_CALLSITE(name); \ + (static constexpr const char* const EMPTY[] = {}; \ + static MetricsCallsite* CALLSITE = \ + M_CALLSITE(name, EMPTY, EMPTY); \ metrics_static_record_histogram(CALLSITE, value);) \ IFN(__VA_ARGS__)(const char* M_LABELS[] = \ {T_FIELD_NAMES(__VA_ARGS__)}; \ diff --git a/src/rust/src/metrics_ffi.rs b/src/rust/src/metrics_ffi.rs index 1c215f9c0..6acb51347 100644 --- a/src/rust/src/metrics_ffi.rs +++ b/src/rust/src/metrics_ffi.rs @@ -72,10 +72,33 @@ pub struct FfiCallsite { } #[no_mangle] -pub extern "C" fn metrics_callsite(name: *const c_char) -> *mut FfiCallsite { +pub extern "C" fn metrics_callsite( + name: *const c_char, + label_names: *const *const c_char, + label_values: *const *const c_char, + labels_len: usize, +) -> *mut FfiCallsite { let name = unsafe { CStr::from_ptr(name) }.to_str().unwrap(); + let labels = unsafe { slice::from_raw_parts(label_names, labels_len) }; + let values = unsafe { slice::from_raw_parts(label_values, labels_len) }; + + let stringify = |s: &[_]| { + s.iter() + .map(|&p| unsafe { CStr::from_ptr(p) }) + .map(|cs| cs.to_string_lossy().into_owned()) + .collect::>() + }; + let labels = stringify(labels); + let values = stringify(values); + + let labels: Vec<_> = labels + .into_iter() + .zip(values.into_iter()) + .map(|(name, value)| Label::new(name, value)) + .collect(); + Box::into_raw(Box::new(FfiCallsite { - key_data: KeyData::from_name(name), + key_data: KeyData::from_parts(name, labels), })) }