From 424df641cc1217cb38bd93336fceb5903bc312dd Mon Sep 17 00:00:00 2001 From: fsck-block <58307481+fsck-block@users.noreply.github.com> Date: Mon, 24 Feb 2025 23:07:29 +0100 Subject: [PATCH] openmetrics endpoint extension (#3521) * added pre-value and raw-value to openmetrics endpoint * added flow_error to openmentrics endpoint --- code/components/openmetrics/openmetrics.cpp | 59 +++++++++++++++++++ .../openmetrics/test_openmetrics.cpp | 47 +++++++++++++-- 2 files changed, 100 insertions(+), 6 deletions(-) diff --git a/code/components/openmetrics/openmetrics.cpp b/code/components/openmetrics/openmetrics.cpp index c90100e8..fd893e7d 100644 --- a/code/components/openmetrics/openmetrics.cpp +++ b/code/components/openmetrics/openmetrics.cpp @@ -1,4 +1,6 @@ #include "openmetrics.h" +#include "functional" +#include "esp_log.h" /** * create a singe metric from the given input @@ -10,10 +12,66 @@ std::string createMetric(const std::string &metricName, const std::string &help, metricName + " " + value + "\n"; } +typedef struct sequence_metric { + const char *name; + const char *help; + const char *type; + std::function valueFunc; +} sequence_metric_t; + + +sequence_metric_t sequenceMetrics[4] = { + { "flow_value", "current value of meter readout", "gauge", [](NumberPost *number)-> std::string {return number->ReturnValue;} }, + { "flow_raw_value", "current raw value of meter readout", "gauge", [](NumberPost *number)-> std::string {return number->ReturnRawValue;} }, + { "flow_pre_value", "previous value of meter readout", "gauge", [](NumberPost *number)-> std::string {return number->ReturnPreValue;} }, + { "flow_error", "Error message text != 'no error'", "gauge", [](NumberPost *number)-> std::string {return number->ErrorMessageText.compare("no error") == 0 ? "0" : "1";} }, +}; + +std::string createSequenceMetrics(std::string prefix, const std::vector &numbers) +{ + std::string result; + for (int i = 0; iname.c_str(), value.c_str()); + + // only valid data is reported (https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#missing-data) + if (value.length() > 0) + { + auto label = number->name; + // except newline, double quote, and backslash (https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#abnf) + // to keep it simple, these characters are just removed from the label + replaceAll(label, "\\", ""); + replaceAll(label, "\"", ""); + replaceAll(label, "\n", ""); + + res += prefix + "_" + sequenceMetrics[i].name + "{sequence=\"" + label + "\"} " + value + "\n"; + } + } + // prepend metadata if a valid metric was created + if (res.length() > 0) + { + res = "# HELP " + prefix + "_" + sequenceMetrics[i].name + " " + sequenceMetrics[i].help + "\n" + + "# TYPE " + prefix + "_" + sequenceMetrics[i].name + " " + sequenceMetrics[i].type + "\n" + + res; + } + result += res; + } + + return result; +} + /** * Generate the MetricFamily from all available sequences * @returns the string containing the text wire format of the MetricFamily **/ +/* std::string createSequenceMetrics(std::string prefix, const std::vector &numbers) { std::string res; @@ -41,3 +99,4 @@ std::string createSequenceMetrics(std::string prefix, const std::vectorname = "main"; number_1->ReturnValue = "123.456"; + number_1->ReturnRawValue = "N23.456"; + number_1->ReturnPreValue = "986.543"; + number_1->ErrorMessageText = ""; NUMBERS.push_back(number_1); const std::string metricNamePrefix = "ai_on_the_edge_device"; - const std::string metricName = metricNamePrefix + "_flow_value"; + const std::string metricName1 = metricNamePrefix + "_flow_value"; + const std::string metricName2 = metricNamePrefix + "_flow_raw_value"; + const std::string metricName3 = metricNamePrefix + "_flow_pre_value"; + const std::string metricName4 = metricNamePrefix + "_flow_error"; + + std::string expected1 ; + expected1 = "# HELP " + metricName1 + " current value of meter readout\n# TYPE " + metricName1 + " gauge\n" + + metricName1 + "{sequence=\"" + number_1->name + "\"} " + number_1->ReturnValue + "\n"; + + expected1 += "# HELP " + metricName2 + " current raw value of meter readout\n# TYPE " + metricName2 + " gauge\n" + + metricName2 + "{sequence=\"" + number_1->name + "\"} " + "NaN" + "\n"; + + expected1 += "# HELP " + metricName3 + " previous value of meter readout\n# TYPE " + metricName3 + " gauge\n" + + metricName3 + "{sequence=\"" + number_1->name + "\"} " + number_1->ReturnPreValue + "\n"; + + expected1 += "# HELP " + metricName4 + " Error message text != 'no error'\n# TYPE " + metricName4 + " gauge\n" + + metricName4 + "{sequence=\"" + number_1->name + "\"} " + "1" + "\n"; - std::string expected1 = "# HELP " + metricName + " current value of meter readout\n# TYPE " + metricName + " gauge\n" + - metricName + "{sequence=\"" + number_1->name + "\"} " + number_1->ReturnValue + "\n"; TEST_ASSERT_EQUAL_STRING(expected1.c_str(), createSequenceMetrics(metricNamePrefix, NUMBERS).c_str()); NumberPost *number_2 = new NumberPost; number_2->name = "secondary"; number_2->ReturnValue = "1.0"; + number_2->ReturnRawValue = "01.000"; + number_2->ReturnPreValue = "0.987"; + number_2->ErrorMessageText = "no error"; NUMBERS.push_back(number_2); - std::string expected2 = "# HELP " + metricName + " current value of meter readout\n# TYPE " + metricName + " gauge\n" + - metricName + "{sequence=\"" + number_1->name + "\"} " + number_1->ReturnValue + "\n" + - metricName + "{sequence=\"" + number_2->name + "\"} " + number_2->ReturnValue + "\n"; + std::string expected2 ; + expected2 = "# HELP " + metricName1 + " current value of meter readout\n# TYPE " + metricName1 + " gauge\n" + + metricName1 + "{sequence=\"" + number_1->name + "\"} " + number_1->ReturnValue + "\n" + + metricName1 + "{sequence=\"" + number_2->name + "\"} " + number_2->ReturnValue + "\n"; + + expected2 += "# HELP " + metricName2 + " current raw value of meter readout\n# TYPE " + metricName2 + " gauge\n" + + metricName2 + "{sequence=\"" + number_1->name + "\"} " + "NaN" + "\n" + + metricName2 + "{sequence=\"" + number_2->name + "\"} " + number_2->ReturnRawValue + "\n"; + + expected2 += "# HELP " + metricName3 + " previous value of meter readout\n# TYPE " + metricName3 + " gauge\n" + + metricName3 + "{sequence=\"" + number_1->name + "\"} " + number_1->ReturnPreValue + "\n" + + metricName3 + "{sequence=\"" + number_2->name + "\"} " + number_2->ReturnPreValue + "\n"; + + expected2 += "# HELP " + metricName4 + " Error message text != 'no error'\n# TYPE " + metricName4 + " gauge\n" + + metricName4 + "{sequence=\"" + number_1->name + "\"} " + "1" + "\n" + + metricName4 + "{sequence=\"" + number_2->name + "\"} " + "0" + "\n"; + + TEST_ASSERT_EQUAL_STRING(expected2.c_str(), createSequenceMetrics(metricNamePrefix, NUMBERS).c_str()); }