metrics: Add support for labels
Given that label values may be dynamic, any metrics callsite with labels can't be stored statically. Instead, we contruct a fresh metrics Key each time we hit a metric with labels (if a metrics recorder is installed).
This commit is contained in:
parent
7a96af8260
commit
92e75de46f
|
@ -5,6 +5,9 @@
|
|||
#ifndef ZCASH_RUST_INCLUDE_RUST_METRICS_H
|
||||
#define ZCASH_RUST_INCLUDE_RUST_METRICS_H
|
||||
|
||||
#include "rust/helpers.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -22,6 +25,9 @@ bool metrics_run(const char* listen_address);
|
|||
struct MetricsCallsite;
|
||||
typedef struct MetricsCallsite MetricsCallsite;
|
||||
|
||||
struct MetricsKey;
|
||||
typedef struct MetricsKey MetricsKey;
|
||||
|
||||
/// Creates a metrics callsite.
|
||||
///
|
||||
/// You should usually call one of the helper macros such as `MetricsCounter`
|
||||
|
@ -32,36 +38,85 @@ typedef struct MetricsCallsite MetricsCallsite;
|
|||
/// UTF-8.
|
||||
MetricsCallsite* metrics_callsite(const char* name);
|
||||
|
||||
/// Creates a metrics key.
|
||||
///
|
||||
/// This API supports labels that may not have static values, and is intended
|
||||
/// to be called for each metrics callsite invocation. As such, it returns null
|
||||
/// if a metrics recorder is not installed, to save on construction costs.
|
||||
///
|
||||
/// You should usually call one of the helper macros such as `MetricsCounter`
|
||||
/// instead of calling this directly.
|
||||
///
|
||||
/// API requirements:
|
||||
/// - label_names and label_values, if not null, MUST be the same length.
|
||||
/// - All string arguments MUST be valid UTF-8.
|
||||
MetricsKey* metrics_key(
|
||||
const char* name,
|
||||
const char* const* label_names,
|
||||
const char* const* label_values,
|
||||
size_t labels_len);
|
||||
|
||||
/// Increments a counter.
|
||||
///
|
||||
/// Counters represent a single monotonic value, which means the value can only
|
||||
/// be incremented, not decremented, and always starts out with an initial value
|
||||
/// of zero.
|
||||
void metrics_increment_counter(const MetricsCallsite* callsite, uint64_t value);
|
||||
void metrics_static_increment_counter(const MetricsCallsite* callsite, uint64_t value);
|
||||
|
||||
/// Increments a counter.
|
||||
///
|
||||
/// Counters represent a single monotonic value, which means the value can only
|
||||
/// be incremented, not decremented, and always starts out with an initial value
|
||||
/// of zero.
|
||||
void metrics_increment_counter(MetricsKey* key, uint64_t value);
|
||||
|
||||
/// Updates a gauge.
|
||||
///
|
||||
/// Gauges represent a single value that can go up or down over time, and always
|
||||
/// starts out with an initial value of zero.
|
||||
void metrics_update_gauge(const MetricsCallsite* callsite, double value);
|
||||
void metrics_static_update_gauge(const MetricsCallsite* callsite, double value);
|
||||
|
||||
/// Updates a gauge.
|
||||
///
|
||||
/// Gauges represent a single value that can go up or down over time, and always
|
||||
/// starts out with an initial value of zero.
|
||||
void metrics_update_gauge(MetricsKey* callsite, double value);
|
||||
|
||||
/// Increments a gauge.
|
||||
///
|
||||
/// Gauges represent a single value that can go up or down over time, and always
|
||||
/// starts out with an initial value of zero.
|
||||
void metrics_increment_gauge(const MetricsCallsite* callsite, double value);
|
||||
void metrics_static_increment_gauge(const MetricsCallsite* callsite, double value);
|
||||
|
||||
/// Increments a gauge.
|
||||
///
|
||||
/// Gauges represent a single value that can go up or down over time, and always
|
||||
/// starts out with an initial value of zero.
|
||||
void metrics_increment_gauge(MetricsKey* callsite, double value);
|
||||
|
||||
/// Decrements a gauge.
|
||||
///
|
||||
/// Gauges represent a single value that can go up or down over time, and always
|
||||
/// starts out with an initial value of zero.
|
||||
void metrics_decrement_gauge(const MetricsCallsite* callsite, double value);
|
||||
void metrics_static_decrement_gauge(const MetricsCallsite* callsite, double value);
|
||||
|
||||
/// Decrements a gauge.
|
||||
///
|
||||
/// Gauges represent a single value that can go up or down over time, and always
|
||||
/// starts out with an initial value of zero.
|
||||
void metrics_decrement_gauge(MetricsKey* callsite, double value);
|
||||
|
||||
/// Records a histogram.
|
||||
///
|
||||
/// Histograms measure the distribution of values for a given set of
|
||||
/// measurements, and start with no initial values.
|
||||
void metrics_record_histogram(const MetricsCallsite* callsite, double value);
|
||||
void metrics_static_record_histogram(const MetricsCallsite* callsite, double value);
|
||||
|
||||
/// Records a histogram.
|
||||
///
|
||||
/// Histograms measure the distribution of values for a given set of
|
||||
/// measurements, and start with no initial values.
|
||||
void metrics_record_histogram(MetricsKey* callsite, double value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -90,6 +145,14 @@ void metrics_record_histogram(const MetricsCallsite* callsite, double value);
|
|||
metrics_callsite(name)
|
||||
#endif
|
||||
|
||||
// Constructs a metrics key.
|
||||
#define M_KEY(name, labels, values) \
|
||||
metrics_key( \
|
||||
name, \
|
||||
labels, \
|
||||
values, \
|
||||
T_ARRLEN(labels))
|
||||
|
||||
//
|
||||
// Metrics
|
||||
//
|
||||
|
@ -100,11 +163,19 @@ void metrics_record_histogram(const MetricsCallsite* callsite, double value);
|
|||
/// be incremented, not decremented, and always starts out with an initial value
|
||||
/// of zero.
|
||||
///
|
||||
/// name MUST be a static constant, and MUST be a valid UTF-8 string.
|
||||
#define MetricsCounter(name, value) \
|
||||
do { \
|
||||
static MetricsCallsite* CALLSITE = M_CALLSITE(name); \
|
||||
metrics_increment_counter(CALLSITE, value); \
|
||||
/// name MUST be a static constant, and all strings MUST be valid UTF-8.
|
||||
#define MetricsCounter(name, value, ...) \
|
||||
do { \
|
||||
IFE(__VA_ARGS__) \
|
||||
(static MetricsCallsite* CALLSITE = M_CALLSITE(name); \
|
||||
metrics_static_increment_counter(CALLSITE, value);) \
|
||||
IFN(__VA_ARGS__)(const char* M_LABELS[] = \
|
||||
{T_FIELD_NAMES(__VA_ARGS__)}; \
|
||||
const char* M_VALUES[] = \
|
||||
{T_FIELD_VALUES(__VA_ARGS__)}; \
|
||||
MetricsKey* KEY = \
|
||||
M_KEY(name, M_LABELS, M_VALUES); \
|
||||
metrics_increment_counter(KEY, value);) \
|
||||
} while (0)
|
||||
|
||||
/// Increments a counter by one.
|
||||
|
@ -113,19 +184,27 @@ void metrics_record_histogram(const MetricsCallsite* callsite, double value);
|
|||
/// be incremented, not decremented, and always starts out with an initial value
|
||||
/// of zero.
|
||||
///
|
||||
/// name MUST be a static constant, and MUST be a valid UTF-8 string.
|
||||
#define MetricsIncrementCounter(name) MetricsCounter(name, 1)
|
||||
/// name MUST be a static constant, and all strings MUST be valid UTF-8.
|
||||
#define MetricsIncrementCounter(name, ...) MetricsCounter(name, 1, __VA_ARGS__)
|
||||
|
||||
/// Updates a gauge.
|
||||
///
|
||||
/// 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 MUST be a valid UTF-8 string.
|
||||
#define MetricsGauge(name, value) \
|
||||
do { \
|
||||
static MetricsCallsite* CALLSITE = M_CALLSITE(name); \
|
||||
metrics_update_gauge(CALLSITE, value); \
|
||||
/// name MUST be a static constant, and all strings MUST be valid UTF-8.
|
||||
#define MetricsGauge(name, value, ...) \
|
||||
do { \
|
||||
IFE(__VA_ARGS__) \
|
||||
(static MetricsCallsite* CALLSITE = M_CALLSITE(name); \
|
||||
metrics_static_update_gauge(CALLSITE, value);) \
|
||||
IFN(__VA_ARGS__)(const char* M_LABELS[] = \
|
||||
{T_FIELD_NAMES(__VA_ARGS__)}; \
|
||||
const char* M_VALUES[] = \
|
||||
{T_FIELD_VALUES(__VA_ARGS__)}; \
|
||||
MetricsKey* KEY = \
|
||||
M_KEY(name, M_LABELS, M_VALUES); \
|
||||
metrics_update_gauge(KEY, value);) \
|
||||
} while (0)
|
||||
|
||||
/// Increments a gauge.
|
||||
|
@ -133,11 +212,19 @@ void metrics_record_histogram(const MetricsCallsite* callsite, double value);
|
|||
/// 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 MUST be a valid UTF-8 string.
|
||||
#define MetricsIncrementGauge(name, value) \
|
||||
do { \
|
||||
static MetricsCallsite* CALLSITE = M_CALLSITE(name); \
|
||||
metrics_increment_gauge(CALLSITE, value); \
|
||||
/// name MUST be a static constant, and all strings MUST be valid UTF-8.
|
||||
#define MetricsIncrementGauge(name, value, ...) \
|
||||
do { \
|
||||
IFE(__VA_ARGS__) \
|
||||
(static MetricsCallsite* CALLSITE = M_CALLSITE(name); \
|
||||
metrics_static_increment_gauge(CALLSITE, value);) \
|
||||
IFN(__VA_ARGS__)(const char* M_LABELS[] = \
|
||||
{T_FIELD_NAMES(__VA_ARGS__)}; \
|
||||
const char* M_VALUES[] = \
|
||||
{T_FIELD_VALUES(__VA_ARGS__)}; \
|
||||
MetricsKey* KEY = \
|
||||
M_KEY(name, M_LABELS, M_VALUES); \
|
||||
metrics_increment_gauge(KEY, value);) \
|
||||
} while (0)
|
||||
|
||||
/// Decrements a gauge.
|
||||
|
@ -145,11 +232,19 @@ void metrics_record_histogram(const MetricsCallsite* callsite, double value);
|
|||
/// 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 MUST be a valid UTF-8 string.
|
||||
#define MetricsDecrementGauge(name, value) \
|
||||
do { \
|
||||
static MetricsCallsite* CALLSITE = M_CALLSITE(name); \
|
||||
metrics_decrement_gauge(CALLSITE, value); \
|
||||
/// name MUST be a static constant, and all strings MUST be valid UTF-8.
|
||||
#define MetricsDecrementGauge(name, value, ...) \
|
||||
do { \
|
||||
IFE(__VA_ARGS__) \
|
||||
(static MetricsCallsite* CALLSITE = M_CALLSITE(name); \
|
||||
metrics_static_decrement_gauge(CALLSITE, value);) \
|
||||
IFN(__VA_ARGS__)(const char* M_LABELS[] = \
|
||||
{T_FIELD_NAMES(__VA_ARGS__)}; \
|
||||
const char* M_VALUES[] = \
|
||||
{T_FIELD_VALUES(__VA_ARGS__)}; \
|
||||
MetricsKey* KEY = \
|
||||
M_KEY(name, M_LABELS, M_VALUES); \
|
||||
metrics_decrement_gauge(KEY, value);) \
|
||||
} while (0)
|
||||
|
||||
/// Records a histogram.
|
||||
|
@ -157,11 +252,19 @@ void metrics_record_histogram(const MetricsCallsite* callsite, double value);
|
|||
/// Histograms measure the distribution of values for a given set of
|
||||
/// measurements, and start with no initial values.
|
||||
///
|
||||
/// name MUST be a static constant, and MUST be a valid UTF-8 string.
|
||||
#define MetricsHistogram(name, value) \
|
||||
do { \
|
||||
static MetricsCallsite* CALLSITE = M_CALLSITE(name); \
|
||||
metrics_record_histogram(CALLSITE, value); \
|
||||
/// name MUST be a static constant, and all strings MUST be valid UTF-8.
|
||||
#define MetricsHistogram(name, value, ...) \
|
||||
do { \
|
||||
IFE(__VA_ARGS__) \
|
||||
(static MetricsCallsite* CALLSITE = M_CALLSITE(name); \
|
||||
metrics_static_record_histogram(CALLSITE, value);) \
|
||||
IFN(__VA_ARGS__)(const char* M_LABELS[] = \
|
||||
{T_FIELD_NAMES(__VA_ARGS__)}; \
|
||||
const char* M_VALUES[] = \
|
||||
{T_FIELD_VALUES(__VA_ARGS__)}; \
|
||||
MetricsKey* KEY = \
|
||||
M_KEY(name, M_LABELS, M_VALUES); \
|
||||
metrics_record_histogram(KEY, value);) \
|
||||
} while (0)
|
||||
|
||||
#endif // ZCASH_RUST_INCLUDE_RUST_METRICS_H
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
use libc::{c_char, c_double};
|
||||
use metrics::{try_recorder, GaugeValue, Key, KeyData};
|
||||
use metrics::{try_recorder, GaugeValue, Key, KeyData, Label};
|
||||
use metrics_exporter_prometheus::PrometheusBuilder;
|
||||
use std::ffi::CStr;
|
||||
use std::net::SocketAddr;
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
use tracing::error;
|
||||
|
||||
#[no_mangle]
|
||||
|
@ -41,8 +43,50 @@ pub extern "C" fn metrics_callsite(name: *const c_char) -> *mut FfiCallsite {
|
|||
}))
|
||||
}
|
||||
|
||||
pub struct FfiKey {
|
||||
inner: Key,
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn metrics_increment_counter(callsite: *const FfiCallsite, value: u64) {
|
||||
pub extern "C" fn metrics_key(
|
||||
name: *const c_char,
|
||||
label_names: *const *const c_char,
|
||||
label_values: *const *const c_char,
|
||||
labels_len: usize,
|
||||
) -> *mut FfiKey {
|
||||
if try_recorder().is_none() {
|
||||
// No recorder is currently installed, so don't genenerate a key. We check for
|
||||
// null inside each API that consumes an FfiKey, just in case a recorder was
|
||||
// installed in a racy way.
|
||||
ptr::null_mut()
|
||||
} else {
|
||||
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::<Vec<_>>()
|
||||
};
|
||||
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(FfiKey {
|
||||
inner: Key::Owned(KeyData::from_parts(name, labels)),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn metrics_static_increment_counter(callsite: *const FfiCallsite, value: u64) {
|
||||
if let Some(recorder) = try_recorder() {
|
||||
let callsite = unsafe { callsite.as_ref().unwrap() };
|
||||
recorder.increment_counter(Key::Borrowed(&callsite.key_data), value);
|
||||
|
@ -50,7 +94,17 @@ pub extern "C" fn metrics_increment_counter(callsite: *const FfiCallsite, value:
|
|||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn metrics_update_gauge(callsite: *const FfiCallsite, value: c_double) {
|
||||
pub extern "C" fn metrics_increment_counter(key: *mut FfiKey, value: u64) {
|
||||
if let Some(recorder) = try_recorder() {
|
||||
if !key.is_null() {
|
||||
let key = unsafe { Box::from_raw(key) };
|
||||
recorder.increment_counter(key.inner, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn metrics_static_update_gauge(callsite: *const FfiCallsite, value: c_double) {
|
||||
if let Some(recorder) = try_recorder() {
|
||||
let callsite = unsafe { callsite.as_ref().unwrap() };
|
||||
recorder.update_gauge(
|
||||
|
@ -61,7 +115,17 @@ pub extern "C" fn metrics_update_gauge(callsite: *const FfiCallsite, value: c_do
|
|||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn metrics_increment_gauge(callsite: *const FfiCallsite, value: c_double) {
|
||||
pub extern "C" fn metrics_update_gauge(key: *mut FfiKey, value: c_double) {
|
||||
if let Some(recorder) = try_recorder() {
|
||||
if !key.is_null() {
|
||||
let key = unsafe { Box::from_raw(key) };
|
||||
recorder.update_gauge(key.inner, GaugeValue::Absolute(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn metrics_static_increment_gauge(callsite: *const FfiCallsite, value: c_double) {
|
||||
if let Some(recorder) = try_recorder() {
|
||||
let callsite = unsafe { callsite.as_ref().unwrap() };
|
||||
recorder.update_gauge(
|
||||
|
@ -72,7 +136,17 @@ pub extern "C" fn metrics_increment_gauge(callsite: *const FfiCallsite, value: c
|
|||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn metrics_decrement_gauge(callsite: *const FfiCallsite, value: c_double) {
|
||||
pub extern "C" fn metrics_increment_gauge(key: *mut FfiKey, value: c_double) {
|
||||
if let Some(recorder) = try_recorder() {
|
||||
if !key.is_null() {
|
||||
let key = unsafe { Box::from_raw(key) };
|
||||
recorder.update_gauge(key.inner, GaugeValue::Increment(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn metrics_static_decrement_gauge(callsite: *const FfiCallsite, value: c_double) {
|
||||
if let Some(recorder) = try_recorder() {
|
||||
let callsite = unsafe { callsite.as_ref().unwrap() };
|
||||
recorder.update_gauge(
|
||||
|
@ -83,9 +157,29 @@ pub extern "C" fn metrics_decrement_gauge(callsite: *const FfiCallsite, value: c
|
|||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn metrics_record_histogram(callsite: *const FfiCallsite, value: c_double) {
|
||||
pub extern "C" fn metrics_decrement_gauge(key: *mut FfiKey, value: c_double) {
|
||||
if let Some(recorder) = try_recorder() {
|
||||
if !key.is_null() {
|
||||
let key = unsafe { Box::from_raw(key) };
|
||||
recorder.update_gauge(key.inner, GaugeValue::Decrement(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn metrics_static_record_histogram(callsite: *const FfiCallsite, value: c_double) {
|
||||
if let Some(recorder) = try_recorder() {
|
||||
let callsite = unsafe { callsite.as_ref().unwrap() };
|
||||
recorder.record_histogram(Key::Borrowed(&callsite.key_data), value);
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn metrics_record_histogram(key: *mut FfiKey, value: c_double) {
|
||||
if let Some(recorder) = try_recorder() {
|
||||
if !key.is_null() {
|
||||
let key = unsafe { Box::from_raw(key) };
|
||||
recorder.record_histogram(key.inner, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue