net: prometheus: Add way to format output by a metric
Instead of requiring one big buffer for formatting the output, have a walk function that can be used to generate output by one metric at a time. Signed-off-by: Jukka Rissanen <jukka.rissanen@nordicsemi.no>
This commit is contained in:
parent
6803e343ef
commit
34ed76b185
@ -116,6 +116,60 @@ int prometheus_collector_register_metric(struct prometheus_collector *collector,
|
||||
const void *prometheus_collector_get_metric(struct prometheus_collector *collector,
|
||||
const char *name);
|
||||
|
||||
/** @cond INTERNAL_HIDDEN */
|
||||
|
||||
enum prometheus_walk_state {
|
||||
PROMETHEUS_WALK_START,
|
||||
PROMETHEUS_WALK_CONTINUE,
|
||||
PROMETHEUS_WALK_STOP,
|
||||
};
|
||||
|
||||
struct prometheus_collector_walk_context {
|
||||
struct prometheus_collector *collector;
|
||||
struct prometheus_metric *metric;
|
||||
struct prometheus_metric *tmp;
|
||||
enum prometheus_walk_state state;
|
||||
};
|
||||
|
||||
/** @endcond */
|
||||
|
||||
/**
|
||||
* @brief Walk through all metrics in a Prometheus collector and format them
|
||||
* into a buffer.
|
||||
*
|
||||
* @param ctx Pointer to the walker context.
|
||||
* @param buffer Pointer to the buffer to store the formatted metrics.
|
||||
* @param buffer_size Size of the buffer.
|
||||
* @return 0 if successful and we went through all metrics, -EAGAIN if we
|
||||
* need to call this function again, any other negative error code
|
||||
* means an error occurred.
|
||||
*/
|
||||
int prometheus_collector_walk_metrics(struct prometheus_collector_walk_context *ctx,
|
||||
uint8_t *buffer, size_t buffer_size);
|
||||
|
||||
/**
|
||||
* @brief Initialize the walker context to walk through all metrics.
|
||||
*
|
||||
* @param ctx Pointer to the walker context.
|
||||
* @param collector Pointer to the collector to walk through.
|
||||
*
|
||||
* @return 0 if successful, otherwise a negative error code.
|
||||
*/
|
||||
static inline int prometheus_collector_walk_init(struct prometheus_collector_walk_context *ctx,
|
||||
struct prometheus_collector *collector)
|
||||
{
|
||||
if (collector == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ctx->collector = collector;
|
||||
ctx->state = PROMETHEUS_WALK_START;
|
||||
ctx->metric = NULL;
|
||||
ctx->tmp = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
* @brief Format exposition data for Prometheus
|
||||
*
|
||||
* Formats the exposition data collected by the specified collector into the provided buffer.
|
||||
* Function to format metric data according to Prometheus text-based format
|
||||
* Function will format metric data according to Prometheus text-based format
|
||||
*
|
||||
* @param collector Pointer to the collector containing the data to format.
|
||||
* @param buffer Pointer to the buffer where the formatted exposition data will be stored.
|
||||
@ -33,6 +33,22 @@
|
||||
int prometheus_format_exposition(struct prometheus_collector *collector, char *buffer,
|
||||
size_t buffer_size);
|
||||
|
||||
/**
|
||||
* @brief Format exposition data for one metric for Prometheus
|
||||
*
|
||||
* Formats the exposition data of one specific metric into the provided buffer.
|
||||
* Function will format metric data according to Prometheus text-based format.
|
||||
*
|
||||
* @param metric Pointer to the metric containing the data to format.
|
||||
* @param buffer Pointer to the buffer where the formatted exposition data will be stored.
|
||||
* @param buffer_size Size of the buffer.
|
||||
* @param written How many bytes have been written to the buffer.
|
||||
*
|
||||
* @return 0 on success, negative errno on error.
|
||||
*/
|
||||
int prometheus_format_one_metric(struct prometheus_metric *metric, char *buffer,
|
||||
size_t buffer_size, int *written);
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
#include <zephyr/net/prometheus/summary.h>
|
||||
#include <zephyr/net/prometheus/counter.h>
|
||||
#include <zephyr/net/prometheus/gauge.h>
|
||||
#include <zephyr/net/prometheus/formatter.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -157,3 +158,77 @@ const void *prometheus_collector_get_metric(struct prometheus_collector *collect
|
||||
out:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int prometheus_collector_walk_metrics(struct prometheus_collector_walk_context *ctx,
|
||||
uint8_t *buffer, size_t buffer_size)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (ctx->collector == NULL) {
|
||||
LOG_ERR("Invalid arguments");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ctx->state == PROMETHEUS_WALK_START) {
|
||||
k_mutex_lock(&ctx->collector->lock, K_FOREVER);
|
||||
ctx->state = PROMETHEUS_WALK_CONTINUE;
|
||||
|
||||
/* Start of the loop is taken from
|
||||
* SYS_SLIST_FOR_EACH_CONTAINER_SAFE macro to simulate
|
||||
* a loop.
|
||||
*/
|
||||
|
||||
ctx->metric = Z_GENLIST_PEEK_HEAD_CONTAINER(slist,
|
||||
&ctx->collector->metrics,
|
||||
ctx->metric,
|
||||
node);
|
||||
ctx->tmp = Z_GENLIST_PEEK_NEXT_CONTAINER(slist,
|
||||
ctx->metric,
|
||||
node);
|
||||
}
|
||||
|
||||
if (ctx->state == PROMETHEUS_WALK_CONTINUE) {
|
||||
int len = 0;
|
||||
|
||||
ctx->metric = ctx->tmp;
|
||||
ctx->tmp = Z_GENLIST_PEEK_NEXT_CONTAINER(slist,
|
||||
ctx->metric,
|
||||
node);
|
||||
|
||||
if (ctx->metric == NULL) {
|
||||
ctx->state = PROMETHEUS_WALK_STOP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* If there is a user callback, use it to update the metric data. */
|
||||
if (ctx->collector->user_cb) {
|
||||
ret = ctx->collector->user_cb(ctx->collector, ctx->metric,
|
||||
ctx->collector->user_data);
|
||||
if (ret < 0) {
|
||||
if (ret != -EAGAIN) {
|
||||
ctx->state = PROMETHEUS_WALK_STOP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Skip this metric for now */
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = prometheus_format_one_metric(ctx->metric, buffer, buffer_size, &len);
|
||||
if (ret < 0) {
|
||||
ctx->state = PROMETHEUS_WALK_STOP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = -EAGAIN;
|
||||
}
|
||||
|
||||
out:
|
||||
if (ctx->state == PROMETHEUS_WALK_STOP) {
|
||||
k_mutex_unlock(&ctx->collector->lock);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -46,6 +46,200 @@ static int write_metric_to_buffer(char *buffer, size_t buffer_size, const char *
|
||||
return 0;
|
||||
}
|
||||
|
||||
int prometheus_format_one_metric(struct prometheus_metric *metric, char *buffer,
|
||||
size_t buffer_size, int *written)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* write HELP line if available */
|
||||
if (metric->description[0] != '\0') {
|
||||
ret = write_metric_to_buffer(buffer + *written, buffer_size - *written,
|
||||
"# HELP %s %s\n", metric->name,
|
||||
metric->description);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing to buffer");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* write TYPE line */
|
||||
switch (metric->type) {
|
||||
case PROMETHEUS_COUNTER:
|
||||
ret = write_metric_to_buffer(buffer + *written, buffer_size - *written,
|
||||
"# TYPE %s counter\n", metric->name);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing counter");
|
||||
goto out;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PROMETHEUS_GAUGE:
|
||||
ret = write_metric_to_buffer(buffer + *written, buffer_size - *written,
|
||||
"# TYPE %s gauge\n", metric->name);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing gauge");
|
||||
goto out;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PROMETHEUS_HISTOGRAM:
|
||||
ret = write_metric_to_buffer(buffer + *written, buffer_size - *written,
|
||||
"# TYPE %s histogram\n", metric->name);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing histogram");
|
||||
goto out;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PROMETHEUS_SUMMARY:
|
||||
ret = write_metric_to_buffer(buffer + *written, buffer_size - *written,
|
||||
"# TYPE %s summary\n", metric->name);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing summary");
|
||||
goto out;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = write_metric_to_buffer(buffer + *written, buffer_size - *written,
|
||||
"# TYPE %s untyped\n", metric->name);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing untyped");
|
||||
goto out;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* write metric-specific fields */
|
||||
switch (metric->type) {
|
||||
case PROMETHEUS_COUNTER: {
|
||||
const struct prometheus_counter *counter =
|
||||
CONTAINER_OF(metric, struct prometheus_counter, base);
|
||||
|
||||
LOG_DBG("counter->value: %llu", counter->value);
|
||||
|
||||
for (int i = 0; i < metric->num_labels; ++i) {
|
||||
ret = write_metric_to_buffer(
|
||||
buffer + *written, buffer_size - *written,
|
||||
"%s{%s=\"%s\"} %llu\n", metric->name, metric->labels[i].key,
|
||||
metric->labels[i].value, counter->value);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing counter");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PROMETHEUS_GAUGE: {
|
||||
const struct prometheus_gauge *gauge =
|
||||
CONTAINER_OF(metric, struct prometheus_gauge, base);
|
||||
|
||||
LOG_DBG("gauge->value: %f", gauge->value);
|
||||
|
||||
for (int i = 0; i < metric->num_labels; ++i) {
|
||||
ret = write_metric_to_buffer(
|
||||
buffer + *written, buffer_size - *written,
|
||||
"%s{%s=\"%s\"} %f\n", metric->name, metric->labels[i].key,
|
||||
metric->labels[i].value, gauge->value);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing gauge");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PROMETHEUS_HISTOGRAM: {
|
||||
const struct prometheus_histogram *histogram =
|
||||
CONTAINER_OF(metric, struct prometheus_histogram, base);
|
||||
|
||||
LOG_DBG("histogram->count: %lu", histogram->count);
|
||||
|
||||
for (int i = 0; i < histogram->num_buckets; ++i) {
|
||||
ret = write_metric_to_buffer(
|
||||
buffer + *written, buffer_size - *written,
|
||||
"%s_bucket{le=\"%f\"} %lu\n", metric->name,
|
||||
histogram->buckets[i].upper_bound,
|
||||
histogram->buckets[i].count);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing histogram");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = write_metric_to_buffer(buffer + *written, buffer_size - *written,
|
||||
"%s_sum %f\n", metric->name, histogram->sum);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing histogram");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = write_metric_to_buffer(buffer + *written, buffer_size - *written,
|
||||
"%s_count %lu\n", metric->name,
|
||||
histogram->count);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing histogram");
|
||||
goto out;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PROMETHEUS_SUMMARY: {
|
||||
const struct prometheus_summary *summary =
|
||||
CONTAINER_OF(metric, struct prometheus_summary, base);
|
||||
|
||||
LOG_DBG("summary->count: %lu", summary->count);
|
||||
|
||||
for (int i = 0; i < summary->num_quantiles; ++i) {
|
||||
ret = write_metric_to_buffer(
|
||||
buffer + *written, buffer_size - *written,
|
||||
"%s{%s=\"%f\"} %f\n", metric->name, "quantile",
|
||||
summary->quantiles[i].quantile,
|
||||
summary->quantiles[i].value);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing summary");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = write_metric_to_buffer(buffer + *written, buffer_size - *written,
|
||||
"%s_sum %f\n", metric->name, summary->sum);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing summary");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = write_metric_to_buffer(buffer + *written, buffer_size - *written,
|
||||
"%s_count %lu\n", metric->name,
|
||||
summary->count);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing summary");
|
||||
goto out;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
/* should not happen */
|
||||
LOG_ERR("Unsupported metric type %d", metric->type);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int prometheus_format_exposition(struct prometheus_collector *collector, char *buffer,
|
||||
size_t buffer_size)
|
||||
{
|
||||
@ -77,188 +271,8 @@ int prometheus_format_exposition(struct prometheus_collector *collector, char *b
|
||||
}
|
||||
}
|
||||
|
||||
/* write HELP line if available */
|
||||
if (metric->description[0] != '\0') {
|
||||
ret = write_metric_to_buffer(buffer + written, buffer_size - written,
|
||||
"# HELP %s %s\n", metric->name,
|
||||
metric->description);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing to buffer");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* write TYPE line */
|
||||
switch (metric->type) {
|
||||
case PROMETHEUS_COUNTER:
|
||||
ret = write_metric_to_buffer(buffer + written, buffer_size - written,
|
||||
"# TYPE %s counter\n", metric->name);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing counter");
|
||||
goto out;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PROMETHEUS_GAUGE:
|
||||
ret = write_metric_to_buffer(buffer + written, buffer_size - written,
|
||||
"# TYPE %s gauge\n", metric->name);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing gauge");
|
||||
goto out;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PROMETHEUS_HISTOGRAM:
|
||||
ret = write_metric_to_buffer(buffer + written, buffer_size - written,
|
||||
"# TYPE %s histogram\n", metric->name);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing histogram");
|
||||
goto out;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PROMETHEUS_SUMMARY:
|
||||
ret = write_metric_to_buffer(buffer + written, buffer_size - written,
|
||||
"# TYPE %s summary\n", metric->name);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing summary");
|
||||
goto out;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = write_metric_to_buffer(buffer + written, buffer_size - written,
|
||||
"# TYPE %s untyped\n", metric->name);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing untyped");
|
||||
goto out;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* write metric-specific fields */
|
||||
switch (metric->type) {
|
||||
case PROMETHEUS_COUNTER: {
|
||||
const struct prometheus_counter *counter =
|
||||
CONTAINER_OF(metric, struct prometheus_counter, base);
|
||||
|
||||
LOG_DBG("counter->value: %llu", counter->value);
|
||||
|
||||
for (int i = 0; i < metric->num_labels; ++i) {
|
||||
ret = write_metric_to_buffer(
|
||||
buffer + written, buffer_size - written,
|
||||
"%s{%s=\"%s\"} %llu\n", metric->name, metric->labels[i].key,
|
||||
metric->labels[i].value, counter->value);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing counter");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PROMETHEUS_GAUGE: {
|
||||
const struct prometheus_gauge *gauge =
|
||||
CONTAINER_OF(metric, struct prometheus_gauge, base);
|
||||
|
||||
LOG_DBG("gauge->value: %f", gauge->value);
|
||||
|
||||
for (int i = 0; i < metric->num_labels; ++i) {
|
||||
ret = write_metric_to_buffer(
|
||||
buffer + written, buffer_size - written,
|
||||
"%s{%s=\"%s\"} %f\n", metric->name, metric->labels[i].key,
|
||||
metric->labels[i].value, gauge->value);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing gauge");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PROMETHEUS_HISTOGRAM: {
|
||||
const struct prometheus_histogram *histogram =
|
||||
CONTAINER_OF(metric, struct prometheus_histogram, base);
|
||||
|
||||
LOG_DBG("histogram->count: %lu", histogram->count);
|
||||
|
||||
for (int i = 0; i < histogram->num_buckets; ++i) {
|
||||
ret = write_metric_to_buffer(
|
||||
buffer + written, buffer_size - written,
|
||||
"%s_bucket{le=\"%f\"} %lu\n", metric->name,
|
||||
histogram->buckets[i].upper_bound,
|
||||
histogram->buckets[i].count);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing histogram");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = write_metric_to_buffer(buffer + written, buffer_size - written,
|
||||
"%s_sum %f\n", metric->name, histogram->sum);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing histogram");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = write_metric_to_buffer(buffer + written, buffer_size - written,
|
||||
"%s_count %lu\n", metric->name,
|
||||
histogram->count);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing histogram");
|
||||
goto out;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case PROMETHEUS_SUMMARY: {
|
||||
const struct prometheus_summary *summary =
|
||||
CONTAINER_OF(metric, struct prometheus_summary, base);
|
||||
|
||||
LOG_DBG("summary->count: %lu", summary->count);
|
||||
|
||||
for (int i = 0; i < summary->num_quantiles; ++i) {
|
||||
ret = write_metric_to_buffer(
|
||||
buffer + written, buffer_size - written,
|
||||
"%s{%s=\"%f\"} %f\n", metric->name, "quantile",
|
||||
summary->quantiles[i].quantile,
|
||||
summary->quantiles[i].value);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing summary");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = write_metric_to_buffer(buffer + written, buffer_size - written,
|
||||
"%s_sum %f\n", metric->name, summary->sum);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing summary");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = write_metric_to_buffer(buffer + written, buffer_size - written,
|
||||
"%s_count %lu\n", metric->name,
|
||||
summary->count);
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error writing summary");
|
||||
goto out;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
/* should not happen */
|
||||
LOG_ERR("Unsupported metric type %d", metric->type);
|
||||
ret = -EINVAL;
|
||||
ret = prometheus_format_one_metric(metric, buffer, buffer_size, &written);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user