소스 검색

openmetrics endpoint extension (#3521)

* added pre-value and raw-value to openmetrics endpoint

* added flow_error to openmentrics endpoint
fsck-block 11 달 전
부모
커밋
424df641cc
2개의 변경된 파일100개의 추가작업 그리고 6개의 파일을 삭제
  1. 59 0
      code/components/openmetrics/openmetrics.cpp
  2. 41 6
      code/test/components/openmetrics/test_openmetrics.cpp

+ 59 - 0
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<std::string(NumberPost *number)> 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<NumberPost *> &numbers)
+{
+    std::string result;
+    for (int i = 0; i<sizeof(sequenceMetrics)/sizeof(sequence_metric_t);i++) 
+    {
+        std::string res;
+        for (const auto &number : numbers)
+        {
+            std::string value = sequenceMetrics[i].valueFunc(number); 
+            if (value.find("N") != std::string::npos) {
+                value = "NaN";
+            }
+            ESP_LOGD("METRICS", "metric=%s, name=%s, value = %s ",sequenceMetrics[i].name,number->name.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<NumberPost *> &numbers)
 {
     std::string res;
@@ -41,3 +99,4 @@ std::string createSequenceMetrics(std::string prefix, const std::vector<NumberPo
     }
     return res;
 }
+*/

+ 41 - 6
code/test/components/openmetrics/test_openmetrics.cpp

@@ -37,23 +37,58 @@ void test_createSequenceMetrics()
     NumberPost *number_1 = new NumberPost;
     number_1->name = "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());
 }