From c7989b57470d329643b1a3e57515ffafa1ed7dfa Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 20 Nov 2024 18:31:22 +0200 Subject: [PATCH] net: prometheus: Add more parameters to metric macros Add collector parameter to metric creation macros so that it is possible to bind the metric to collector already at built time. Also add optional user_data to metric macro calls so that user can add optional data there. This will be used by network statistics Prometheus support in subsequent commits. Signed-off-by: Jukka Rissanen --- include/zephyr/net/prometheus/collector.h | 37 ++++++++++++++++++- include/zephyr/net/prometheus/counter.h | 14 ++++++- include/zephyr/net/prometheus/gauge.h | 14 ++++++- include/zephyr/net/prometheus/histogram.h | 14 ++++++- include/zephyr/net/prometheus/metric.h | 7 ++++ include/zephyr/net/prometheus/summary.h | 15 +++++++- samples/net/prometheus/src/main.c | 2 +- subsys/net/lib/prometheus/formatter.c | 14 +++++++ tests/net/lib/prometheus/collector/src/main.c | 8 ++-- tests/net/lib/prometheus/counter/src/main.c | 3 +- tests/net/lib/prometheus/gauge/src/main.c | 2 +- tests/net/lib/prometheus/histogram/src/main.c | 2 +- tests/net/lib/prometheus/summary/src/main.c | 2 +- 13 files changed, 117 insertions(+), 17 deletions(-) diff --git a/include/zephyr/net/prometheus/collector.h b/include/zephyr/net/prometheus/collector.h index e63dc7aa827..876ed706338 100644 --- a/include/zephyr/net/prometheus/collector.h +++ b/include/zephyr/net/prometheus/collector.h @@ -26,6 +26,22 @@ #include +struct prometheus_collector; + +/** + * @typedef prometheus_scrape_cb_t + * @brief Callback used to scrape a collector for a specific metric. + * + * @param collector A valid pointer on the collector to scrape + * @param metric A valid pointer on the metric to scrape + * @param user_data A valid pointer to a user data or NULL + * + * @return 0 if successful, otherwise a negative error code. + */ +typedef int (*prometheus_scrape_cb_t)(struct prometheus_collector *collector, + struct prometheus_metric *metric, + void *user_data); + /** * @brief Prometheus collector definition * @@ -38,6 +54,12 @@ struct prometheus_collector { sys_slist_t metrics; /** Mutex to protect the metrics list manipulation */ struct k_mutex lock; + /** User callback function. If set, then the metric data is fetched + * via the function callback. + */ + prometheus_scrape_cb_t user_cb; + /** User data */ + void *user_data; }; /** @@ -46,12 +68,25 @@ struct prometheus_collector { * This macro defines a Collector. * * @param _name The collector's name. + * @param ... Optional user callback function. If set, this function is called + * when the collector is scraped. The function should be of type + * prometheus_scrape_cb_t. + * Optional user data to pass to the user callback function. */ -#define PROMETHEUS_COLLECTOR_DEFINE(_name) \ +#define PROMETHEUS_COLLECTOR_DEFINE(_name, ...) \ STRUCT_SECTION_ITERABLE(prometheus_collector, _name) = { \ .name = STRINGIFY(_name), \ .metrics = SYS_SLIST_STATIC_INIT(&_name.metrics), \ .lock = Z_MUTEX_INITIALIZER(_name.lock), \ + .user_cb = COND_CODE_0( \ + NUM_VA_ARGS_LESS_1( \ + LIST_DROP_EMPTY(__VA_ARGS__, _)), \ + (NULL), \ + (GET_ARG_N(1, __VA_ARGS__))), \ + .user_data = COND_CODE_0( \ + NUM_VA_ARGS_LESS_1(__VA_ARGS__), (NULL), \ + (GET_ARG_N(1, \ + GET_ARGS_LESS_N(1, __VA_ARGS__)))), \ } /** diff --git a/include/zephyr/net/prometheus/counter.h b/include/zephyr/net/prometheus/counter.h index 89c1ff123d3..591e7ec25c5 100644 --- a/include/zephyr/net/prometheus/counter.h +++ b/include/zephyr/net/prometheus/counter.h @@ -33,6 +33,8 @@ struct prometheus_counter { struct prometheus_metric base; /** Value of the Prometheus counter metric */ uint64_t value; + /** User data */ + void *user_data; }; /** @@ -44,22 +46,30 @@ struct prometheus_counter { * @param _name The counter metric name * @param _desc Counter description * @param _label Label for the metric. Additional labels can be added at runtime. + * @param _collector Collector to map this metric. Can be set to NULL if it not yet known. + * @param ... Optional user data specific to this metric instance. * * Example usage: * @code{.c} * * PROMETHEUS_COUNTER_DEFINE(http_request_counter, "HTTP request counter", - * ({ .key = "http_request", .value = "request_count" })); + * ({ .key = "http_request", .value = "request_count" }), + * NULL); * @endcode */ -#define PROMETHEUS_COUNTER_DEFINE(_name, _desc, _label) \ +#define PROMETHEUS_COUNTER_DEFINE(_name, _desc, _label, _collector, ...) \ STRUCT_SECTION_ITERABLE(prometheus_counter, _name) = { \ .base.name = STRINGIFY(_name), \ .base.type = PROMETHEUS_COUNTER, \ .base.description = _desc, \ .base.labels[0] = __DEBRACKET _label, \ .base.num_labels = 1, \ + .base.collector = _collector, \ .value = 0ULL, \ + .user_data = COND_CODE_0( \ + NUM_VA_ARGS_LESS_1(LIST_DROP_EMPTY(__VA_ARGS__, _)), \ + (NULL), \ + (GET_ARG_N(1, __VA_ARGS__))), \ } /** diff --git a/include/zephyr/net/prometheus/gauge.h b/include/zephyr/net/prometheus/gauge.h index dd0f4dee4da..0703e3818a7 100644 --- a/include/zephyr/net/prometheus/gauge.h +++ b/include/zephyr/net/prometheus/gauge.h @@ -31,6 +31,8 @@ struct prometheus_gauge { struct prometheus_metric base; /** Value of the Prometheus gauge metric */ double value; + /** User data */ + void *user_data; }; /** @@ -42,23 +44,31 @@ struct prometheus_gauge { * @param _name The gauge metric name. * @param _desc Gauge description * @param _label Label for the metric. Additional labels can be added at runtime. + * @param _collector Collector to map this metric. Can be set to NULL if it not yet known. + * @param ... Optional user data specific to this metric instance. * * Example usage: * @code{.c} * * PROMETHEUS_GAUGE_DEFINE(http_request_gauge, "HTTP request gauge", - * ({ .key = "http_request", .value = "request_count" })); + * ({ .key = "http_request", .value = "request_count" }), + * NULL); * * @endcode */ -#define PROMETHEUS_GAUGE_DEFINE(_name, _desc, _label) \ +#define PROMETHEUS_GAUGE_DEFINE(_name, _desc, _label, _collector, ...) \ STRUCT_SECTION_ITERABLE(prometheus_gauge, _name) = { \ .base.name = STRINGIFY(_name), \ .base.type = PROMETHEUS_GAUGE, \ .base.description = _desc, \ .base.labels[0] = __DEBRACKET _label, \ .base.num_labels = 1, \ + .base.collector = _collector, \ .value = 0.0, \ + .user_data = COND_CODE_0( \ + NUM_VA_ARGS_LESS_1(LIST_DROP_EMPTY(__VA_ARGS__, _)), \ + (NULL), \ + (GET_ARG_N(1, __VA_ARGS__))), \ } /** diff --git a/include/zephyr/net/prometheus/histogram.h b/include/zephyr/net/prometheus/histogram.h index 54c70632305..dbf5538f96e 100644 --- a/include/zephyr/net/prometheus/histogram.h +++ b/include/zephyr/net/prometheus/histogram.h @@ -51,6 +51,8 @@ struct prometheus_histogram { double sum; /** Total count of observations in the histogram */ unsigned long count; + /** User data */ + void *user_data; }; /** @@ -62,26 +64,34 @@ struct prometheus_histogram { * @param _name The histogram metric name. * @param _desc Histogram description * @param _label Label for the metric. Additional labels can be added at runtime. + * @param _collector Collector to map this metric. Can be set to NULL if it not yet known. + * @param ... Optional user data specific to this metric instance. * * Example usage: * @code{.c} * * PROMETHEUS_HISTOGRAM_DEFINE(http_request_histogram, "HTTP request histogram", - * ({ .key = "request_latency", .value = "request_latency_seconds" })); + * ({ .key = "request_latency", .value = "request_latency_seconds" }), + * NULL); * * @endcode */ -#define PROMETHEUS_HISTOGRAM_DEFINE(_name, _desc, _label) \ +#define PROMETHEUS_HISTOGRAM_DEFINE(_name, _desc, _label, _collector, ...) \ STRUCT_SECTION_ITERABLE(prometheus_histogram, _name) = { \ .base.name = STRINGIFY(_name), \ .base.type = PROMETHEUS_HISTOGRAM, \ .base.description = _desc, \ .base.labels[0] = __DEBRACKET _label, \ .base.num_labels = 1, \ + .base.collector = _collector, \ .buckets = NULL, \ .num_buckets = 0, \ .sum = 0.0, \ .count = 0U, \ + .user_data = COND_CODE_0( \ + NUM_VA_ARGS_LESS_1(LIST_DROP_EMPTY(__VA_ARGS__, _)), \ + (NULL), \ + (GET_ARG_N(1, __VA_ARGS__))), \ } /** diff --git a/include/zephyr/net/prometheus/metric.h b/include/zephyr/net/prometheus/metric.h index c9f329e5082..bbe3a0e3eb7 100644 --- a/include/zephyr/net/prometheus/metric.h +++ b/include/zephyr/net/prometheus/metric.h @@ -49,6 +49,11 @@ struct prometheus_metric { sys_snode_t node; /** Back pointer to the collector that this metric belongs to */ struct prometheus_collector *collector; + /** Back pointer to the actual metric (counter, gauge, etc.). + * This is just a temporary solution, ultimate goal is to place + * this generic metrict struct into the actual metric struct. + */ + void *metric; /** Type of the Prometheus metric. */ enum prometheus_metric_type type; /** Name of the Prometheus metric. */ @@ -59,6 +64,8 @@ struct prometheus_metric { struct prometheus_label labels[MAX_PROMETHEUS_LABELS_PER_METRIC]; /** Number of labels associated with the Prometheus metric. */ int num_labels; + /** User defined data */ + void *user_data; /* Add any other necessary fields */ }; diff --git a/include/zephyr/net/prometheus/summary.h b/include/zephyr/net/prometheus/summary.h index ea1d5f1d087..d36b34c643c 100644 --- a/include/zephyr/net/prometheus/summary.h +++ b/include/zephyr/net/prometheus/summary.h @@ -32,6 +32,8 @@ struct prometheus_summary_quantile { double quantile; /** Value of the quantile */ double value; + /** User data */ + void *user_data; }; /** @@ -51,6 +53,8 @@ struct prometheus_summary { double sum; /** Total count of observations in the summary metric */ unsigned long count; + /** User data */ + void *user_data; }; /** @@ -62,6 +66,8 @@ struct prometheus_summary { * @param _name The summary metric name. * @param _desc Summary description * @param _label Label for the metric. Additional labels can be added at runtime. + * @param _collector Collector to map this metric. Can be set to NULL if it not yet known. + * @param ... Optional user data specific to this metric instance. * * * Example usage: @@ -69,22 +75,27 @@ struct prometheus_summary { * * PROMETHEUS_SUMMARY_DEFINE(http_request_summary, "HTTP request summary", * ({ .key = "request_latency", - * .value = "request_latency_seconds" })); + * .value = "request_latency_seconds" }), NULL); * * @endcode */ -#define PROMETHEUS_SUMMARY_DEFINE(_name, _desc, _label) \ +#define PROMETHEUS_SUMMARY_DEFINE(_name, _desc, _label, _collector, ...) \ STRUCT_SECTION_ITERABLE(prometheus_summary, _name) = { \ .base.name = STRINGIFY(_name), \ .base.type = PROMETHEUS_SUMMARY, \ .base.description = _desc, \ .base.labels[0] = __DEBRACKET _label, \ .base.num_labels = 1, \ + .base.collector = _collector, \ .quantiles = NULL, \ .num_quantiles = 0, \ .sum = 0.0, \ .count = 0U, \ + .user_data = COND_CODE_0( \ + NUM_VA_ARGS_LESS_1(LIST_DROP_EMPTY(__VA_ARGS__, _)), \ + (NULL), \ + (GET_ARG_N(1, __VA_ARGS__))), \ } /** diff --git a/samples/net/prometheus/src/main.c b/samples/net/prometheus/src/main.c index e554279cc83..161368bd072 100644 --- a/samples/net/prometheus/src/main.c +++ b/samples/net/prometheus/src/main.c @@ -146,7 +146,7 @@ static void setup_tls(void) } PROMETHEUS_COUNTER_DEFINE(http_request_counter, "HTTP request counter", - ({ .key = "http_request", .value = "request_count" })); + ({ .key = "http_request", .value = "request_count" }), NULL); PROMETHEUS_COLLECTOR_DEFINE(test_collector); diff --git a/subsys/net/lib/prometheus/formatter.c b/subsys/net/lib/prometheus/formatter.c index dd29e3d3cac..65a26366bb9 100644 --- a/subsys/net/lib/prometheus/formatter.c +++ b/subsys/net/lib/prometheus/formatter.c @@ -63,6 +63,20 @@ int prometheus_format_exposition(struct prometheus_collector *collector, char *b SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&collector->metrics, metric, tmp, node) { + /* If there is a user callback, use it to update the metric data. */ + if (collector->user_cb) { + ret = collector->user_cb(collector, metric, collector->user_data); + if (ret < 0) { + if (ret == -EAGAIN) { + /* Skip this metric for now */ + continue; + } + + LOG_ERR("Error in user callback (%d)", ret); + goto out; + } + } + /* write HELP line if available */ if (metric->description[0] != '\0') { ret = write_metric_to_buffer(buffer + written, buffer_size - written, diff --git a/tests/net/lib/prometheus/collector/src/main.c b/tests/net/lib/prometheus/collector/src/main.c index 613ae3bb004..f005d9dd860 100644 --- a/tests/net/lib/prometheus/collector/src/main.c +++ b/tests/net/lib/prometheus/collector/src/main.c @@ -10,7 +10,7 @@ #include PROMETHEUS_COUNTER_DEFINE(test_counter_m, "Test counter", - ({ .key = "test_counter", .value = "test" })); + ({ .key = "test_counter", .value = "test" }), NULL); PROMETHEUS_COLLECTOR_DEFINE(test_custom_collector); @@ -31,9 +31,11 @@ ZTEST(test_collector, test_prometheus_collector_register) prometheus_collector_register_metric(&test_custom_collector, &test_counter_m.base); counter = (struct prometheus_counter *)prometheus_collector_get_metric( - &test_custom_collector, "test_counter"); + &test_custom_collector, "test_counter_m"); - zassert_equal(counter, &test_counter_m, "Counter not found in collector"); + zassert_equal_ptr(counter, &test_counter_m, + "Counter not found in collector (expected %p, got %p)", + &test_counter_m, counter); zassert_equal(test_counter_m.value, 0, "Counter value is not 0"); diff --git a/tests/net/lib/prometheus/counter/src/main.c b/tests/net/lib/prometheus/counter/src/main.c index 5830c8da973..c869daf8f3e 100644 --- a/tests/net/lib/prometheus/counter/src/main.c +++ b/tests/net/lib/prometheus/counter/src/main.c @@ -9,7 +9,8 @@ #include PROMETHEUS_COUNTER_DEFINE(test_counter_m, "Test counter", - ({ .key = "test_counter", .value = "test" })); + ({ .key = "test_counter", .value = "test" }), + NULL); /** * @brief Test prometheus_counter_inc diff --git a/tests/net/lib/prometheus/gauge/src/main.c b/tests/net/lib/prometheus/gauge/src/main.c index 08ee1b74966..acf8481ed72 100644 --- a/tests/net/lib/prometheus/gauge/src/main.c +++ b/tests/net/lib/prometheus/gauge/src/main.c @@ -9,7 +9,7 @@ #include PROMETHEUS_GAUGE_DEFINE(test_gauge_m, "Test gauge", - ({ .key = "test", .value = "gauge" })); + ({ .key = "test", .value = "gauge" }), NULL); /** * @brief Test prometheus_gauge_set diff --git a/tests/net/lib/prometheus/histogram/src/main.c b/tests/net/lib/prometheus/histogram/src/main.c index 934dfe08e79..8be4d0c8dc4 100644 --- a/tests/net/lib/prometheus/histogram/src/main.c +++ b/tests/net/lib/prometheus/histogram/src/main.c @@ -9,7 +9,7 @@ #include PROMETHEUS_HISTOGRAM_DEFINE(test_histogram_m, "Test histogram", - ({ .key = "test", .value = "histogram" })); + ({ .key = "test", .value = "histogram" }), NULL); /** * @brief Test prometheus_histogram_observe diff --git a/tests/net/lib/prometheus/summary/src/main.c b/tests/net/lib/prometheus/summary/src/main.c index b8be3f3dd12..e1246b04dab 100644 --- a/tests/net/lib/prometheus/summary/src/main.c +++ b/tests/net/lib/prometheus/summary/src/main.c @@ -9,7 +9,7 @@ #include PROMETHEUS_SUMMARY_DEFINE(test_summary_m, "Test summary", - ({ .key = "test", .value = "summary" })); + ({ .key = "test", .value = "summary" }), NULL); /** * @brief Test prometheus_summary_observe