From 6ec628b9c737d7c0980d12696c0739380684ec3b Mon Sep 17 00:00:00 2001 From: Florian Forster Date: Fri, 29 Dec 2023 16:58:37 +0100 Subject: [PATCH] write_prometheus plugin: Use the `unit` field to create metric names. Reference: https://opentelemetry.io/docs/specs/otel/compatibility/prometheus_and_openmetrics/#metric-metadata-1 --- src/write_prometheus.c | 71 +++++++++++++++++++++++++++++++++++++ src/write_prometheus_test.c | 54 +++++++++++++++++++++++++--- 2 files changed, 121 insertions(+), 4 deletions(-) diff --git a/src/write_prometheus.c b/src/write_prometheus.c index d4c4f812e..631f1b47c 100644 --- a/src/write_prometheus.c +++ b/src/write_prometheus.c @@ -76,6 +76,70 @@ static struct MHD_Daemon *httpd; static cdtime_t staleness_delta = PROMETHEUS_DEFAULT_STALENESS_DELTA; +typedef struct { + char const *open_telemetry; + char const *prometheus; +} unit_map_t; + +/* The list is sorted at runtime (in `unit_map_lookup`) to avoid issues with + * different locales sorting the list in a different order. */ +static unit_map_t unit_map[] = { + // Time + {"d", "days"}, + {"h", "hours"}, + {"min", "minutes"}, + {"s", "seconds"}, + {"ms", "milliseconds"}, + {"us", "microseconds"}, + {"ns", "nanoseconds"}, + // Bytes + {"By", "bytes"}, + {"KiBy", "kibibytes"}, + {"MiBy", "mebibytes"}, + {"GiBy", "gibibytes"}, + {"TiBy", "tibibytes"}, + {"KBy", "kilobytes"}, + {"MBy", "megabytes"}, + {"GBy", "gigabytes"}, + {"TBy", "terabytes"}, + // SI units + {"m", "meters"}, + {"V", "volts"}, + {"A", "amperes"}, + {"J", "joules"}, + {"W", "watts"}, + {"g", "grams"}, + // Misc + {"1", "ratio"}, + {"%", "percent"}, + {"Cel", "celsius"}, + {"Hz", "hertz"}, +}; + +static int unit_map_compare(void const *a, void const *b) { + unit_map_t const *ma = a; + unit_map_t const *mb = b; + + return strcmp(ma->open_telemetry, mb->open_telemetry); +} + +static unit_map_t const *unit_map_lookup(char const *unit) { + static bool is_sorted; + if (!is_sorted) { + qsort(unit_map, STATIC_ARRAY_SIZE(unit_map), sizeof(unit_map[0]), unit_map_compare); + is_sorted = true; + } + + if (unit == NULL) { + return NULL; + } + + unit_map_t key = { + .open_telemetry = unit, + }; + return bsearch(&key, unit_map, STATIC_ARRAY_SIZE(unit_map), sizeof(unit_map[0]), unit_map_compare); +} + /* Visible for testing */ int format_label_name(strbuf_t *buf, char const *name) { int status = 0; @@ -193,6 +257,13 @@ void format_metric_family_name(strbuf_t *buf, metric_family_t const *fam) { strbuf_print(buf, name); + unit_map_t const *unit = unit_map_lookup(fam->unit); + if (unit != NULL) { + strbuf_printf(buf, "_%s", unit->prometheus); + } else if (fam->unit != NULL && fam->unit[0] != '{') { + strbuf_printf(buf, "_%s", fam->unit); + } + if (fam->type == METRIC_TYPE_COUNTER) { strbuf_print(buf, "_total"); } diff --git a/src/write_prometheus_test.c b/src/write_prometheus_test.c index ecbaa9a07..61d1de5cf 100644 --- a/src/write_prometheus_test.c +++ b/src/write_prometheus_test.c @@ -65,12 +65,57 @@ DEF_TEST(format_metric_family_name) { struct { char *name; metric_type_t type; + char *unit; char *want; } cases[] = { - {"(lambda).function.executions(#)", METRIC_TYPE_UNTYPED, - "lambda_function_executions"}, - {"system.processes.created", METRIC_TYPE_COUNTER, - "system_processes_created_total"}, + { + .name = "(lambda).function.executions(#)", + .type = METRIC_TYPE_UNTYPED, + .want = "lambda_function_executions", + }, + { + .name = "system.processes.created", + .type = METRIC_TYPE_COUNTER, + .want = "system_processes_created_total", + }, + { + .name = "system.filesystem.usage", + .type = METRIC_TYPE_GAUGE, + .unit = "By", + .want = "system_filesystem_usage_bytes", + }, + { + .name = "system.network.dropped", + .type = METRIC_TYPE_GAUGE, + .unit = "{packets}", + .want = "system_network_dropped", + }, + { + .name = "system.network.dropped", + .type = METRIC_TYPE_GAUGE, + .unit = "packets", + .want = "system_network_dropped_packets", + }, + { + .name = "system.memory.utilization", + .type = METRIC_TYPE_GAUGE, + .unit = "1", + .want = "system_memory_utilization_ratio", + }, + { + .name = "storage.filesystem.utilization", + .type = METRIC_TYPE_GAUGE, + .unit = "%", + .want = "storage_filesystem_utilization_percent", + }, + /* Not yet supported: + { + .name = "astro.light.speed", + .type = METRIC_TYPE_GAUGE, + .unit = "m/s", + .want = "astro_light_speed_meters_per_second", + }, + */ }; for (size_t i = 0; i < STATIC_ARRAY_SIZE(cases); i++) { @@ -80,6 +125,7 @@ DEF_TEST(format_metric_family_name) { metric_family_t fam = { .name = cases[i].name, .type = cases[i].type, + .unit = cases[i].unit, }; format_metric_family_name(&got, &fam); -- 2.47.2