From: Michael Tremer Date: Fri, 28 Nov 2025 14:19:21 +0000 (+0000) Subject: metrics: Add a convenience layer to parse command output X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6f579d0fd9e69413b611b2261b7aa95b9b823c9a;p=telemetry.git metrics: Add a convenience layer to parse command output Instead of copying data into a variable and then submitting it to the metrics, this will do it all in one step resulting in much shorter code. Signed-off-by: Michael Tremer --- diff --git a/src/daemon/metrics.c b/src/daemon/metrics.c index 7cfd1e2..67d1e0f 100644 --- a/src/daemon/metrics.c +++ b/src/daemon/metrics.c @@ -442,3 +442,82 @@ int __td_metrics_serialize(td_metrics* self, char* buffer, size_t length) { return 0; } + +typedef struct td_metrics_parser_state { + td_metrics* metrics; + const td_metrics_parser* parser; +} td_metrics_parser_state; + +static int __td_metrics_parse_file(td_ctx* ctx, td_file* self, + unsigned long lineno, char* line, size_t length, void* data) { + const td_metrics_parser_state* state = data; + int r; + + // The parsed value + union { + int64_t _int64; + uint64_t _uint64; + double _double; + } value; + + // Skip processing empty lines + if (!length) + return 0; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + + // Run the parser + for (const td_metrics_parser* p = state->parser; p->field; p++) { + switch (p->type) { + case TD_METRIC_UINT64: + r = sscanf(line, p->pattern, &value._uint64); + break; + + case TD_METRIC_INT64: + r = sscanf(line, p->pattern, &value._int64); + break; + + case TD_METRIC_FLOAT: + r = sscanf(line, p->pattern, &value._double); + break; + + // We currently don't support the other types + default: + return -ENOTSUP; + } + + // Continue if we could not match a value + if (r < 1) + continue; + + // Submit the value if we could match + switch (p->type) { + case TD_METRIC_UINT64: + return td_metrics_push_uint64(state->metrics, p->field, value._uint64); + + case TD_METRIC_INT64: + return td_metrics_push_int64(state->metrics, p->field, value._int64); + + case TD_METRIC_FLOAT: + return td_metrics_push_float(state->metrics, p->field, value._double); + + // We currently don't support the other types + default: + return -ENOTSUP; + } + } + +#pragma GCC diagnostic pop + + return 0; +} + +int td_metrics_parse_file(td_metrics* self, td_file* file, const td_metrics_parser* parser) { + td_metrics_parser_state state = { + .metrics = self, + .parser = parser, + }; + + return td_file_walk(file, __td_metrics_parse_file, &state); +} diff --git a/src/daemon/metrics.h b/src/daemon/metrics.h index 88b2e0f..9a0b1e3 100644 --- a/src/daemon/metrics.h +++ b/src/daemon/metrics.h @@ -42,6 +42,17 @@ typedef struct td_metric_value { const void* value; } td_metric_value; +typedef struct td_metrics_parser { + // Field + const char* field; + + // Type + td_metric_type type; + + // Pattern + const char* pattern; +} td_metrics_parser; + #define VALUES(...) ((const td_metric_value[]) { __VA_ARGS__, { NULL } }) #define VALUE(field, type, value) { field, type, value } @@ -77,4 +88,15 @@ int td_metrics_set(td_metrics* self, const td_metric_value* values); int __td_metrics_serialize(td_metrics* self, char* buffer, size_t length); +// Parser + +#define METRICS(...) ((const td_metrics_parser[]) { __VA_ARGS__ { NULL } }) + +#define METRIC(field, type, pattern) { field, type, pattern } +#define METRIC_INT64(field, pattern) METRIC(field, TD_METRIC_INT64, pattern) +#define METRIC_UINT64(field, pattern) METRIC(field, TD_METRIC_UINT64, pattern) +#define METRIC_FLOAT(field, pattern) METRIC(field, TD_METRIC_FLOAT, pattern) + +int td_metrics_parse_file(td_metrics* self, td_file* file, const td_metrics_parser* parser); + #endif /* TELEMETRY_METRICS_H */ diff --git a/src/daemon/source.c b/src/daemon/source.c index b9fc620..74c9609 100644 --- a/src/daemon/source.c +++ b/src/daemon/source.c @@ -34,6 +34,7 @@ #include "command.h" #include "ctx.h" #include "daemon.h" +#include "metrics.h" #include "source.h" #include "string.h" #include "time.h" @@ -589,6 +590,33 @@ int td_source_submit_metrics(td_source* self, td_metrics* metrics) { return td_daemon_submit_metrics(self->daemon, metrics); } +int td_source_parse_metrics(td_source* self, const char* object, + td_file* file, const td_metrics_parser* parser) { + td_metrics* metrics = NULL; + int r; + + // Create a new metrics object + r = td_source_create_metrics(self, &metrics, object); + if (r < 0) + goto ERROR; + + // Parse the metrics + r = td_metrics_parse_file(metrics, file, parser); + if (r < 0) + goto ERROR; + + // Submit the metrics + r = td_source_submit_metrics(self, metrics); + if (r < 0) + goto ERROR; + +ERROR: + if (metrics) + td_metrics_unref(metrics); + + return r; +} + static int td_source_create_database(td_source* self, const char* path, const char* source) { td_args* args = NULL; char min[24]; diff --git a/src/daemon/source.h b/src/daemon/source.h index bc0423b..4696d7e 100644 --- a/src/daemon/source.h +++ b/src/daemon/source.h @@ -94,6 +94,9 @@ int td_source_create_command(td_source* self, td_command** command); int td_source_run_command(td_source* self, const char** argv, td_command_success_callback callback, void* data); +int td_source_parse_metrics(td_source* self, const char* object, + td_file* file, const td_metrics_parser* parser); + int td_source_submit_metrics(td_source* self, td_metrics* metrics); int td_source_submit_values(td_source* self, const char* object, const td_metric_value* values);