You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@doris.apache.org by ya...@apache.org on 2021/04/20 01:14:38 UTC

[incubator-doris] branch master updated: [Metric] Standardise histogram metric output for prometheus (#5671)

This is an automated email from the ASF dual-hosted git repository.

yangzhg pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-doris.git


The following commit(s) were added to refs/heads/master by this push:
     new caa7af3  [Metric] Standardise histogram metric output for prometheus (#5671)
caa7af3 is described below

commit caa7af3d1fbb6863a54bca24624bd29307bbdcba
Author: Yingchun Lai <40...@qq.com>
AuthorDate: Tue Apr 20 09:14:28 2021 +0800

    [Metric] Standardise histogram metric output for prometheus (#5671)
    
    Update histogram metric's output to prometheus standard, the output
    like following:
    
    test_registry_task_duration{quantile="0.50"} 50
    test_registry_task_duration{quantile="0.75"} 75
    test_registry_task_duration{quantile="0.90"} 95.8333
    test_registry_task_duration{quantile="0.95"} 100
    test_registry_task_duration{quantile="0.99"} 100
    test_registry_task_duration_sum 5050
    test_registry_task_duration_count 100
---
 be/src/util/metrics.cpp           | 97 +++++++++++++++++++++++++++------------
 be/src/util/metrics.h             | 20 ++++++--
 be/test/util/new_metrics_test.cpp | 65 ++++++++++++++++++++++++++
 3 files changed, 148 insertions(+), 34 deletions(-)

diff --git a/be/src/util/metrics.cpp b/be/src/util/metrics.cpp
index 402b169..fda8873 100644
--- a/be/src/util/metrics.cpp
+++ b/be/src/util/metrics.cpp
@@ -77,31 +77,49 @@ const char* unit_name(MetricUnit unit) {
     }
 }
 
-std::string labels_to_string(const Labels& entity_labels, const Labels& metric_labels) {
-    if (entity_labels.empty() && metric_labels.empty()) {
+std::string labels_to_string(std::initializer_list<const Labels*> multi_labels) {
+    bool all_empty = true;
+    for (const auto& labels : multi_labels) {
+        if (!labels->empty()) {
+            all_empty = false;
+            break;
+        }
+    }
+    if (all_empty) {
         return std::string();
     }
 
     std::stringstream ss;
     ss << "{";
     int i = 0;
-    for (const auto& label : entity_labels) {
-        if (i++ > 0) {
-            ss << ",";
-        }
-        ss << label.first << "=\"" << label.second << "\"";
-    }
-    for (const auto& label : metric_labels) {
-        if (i++ > 0) {
-            ss << ",";
+    for (auto labels : multi_labels) {
+        for (const auto& label : *labels) {
+            if (i++ > 0) {
+                ss << ",";
+            }
+            ss << label.first << "=\"" << label.second << "\"";
         }
-        ss << label.first << "=\"" << label.second << "\"";
     }
     ss << "}";
 
     return ss.str();
 }
 
+std::string Metric::to_prometheus(const std::string& display_name,
+                                  const Labels& entity_labels,
+                                  const Labels& metric_labels) const {
+    std::stringstream ss;
+    ss << display_name                                          // metric name
+       << labels_to_string({&entity_labels, &metric_labels})    // metric labels
+       << " " << to_string() << "\n";                           // metric value
+    return ss.str();
+}
+
+std::map<std::string, double> HistogramMetric::_s_output_percentiles = {{"0.50", 50.0},
+                                                                        {"0.75", 75.0},
+                                                                        {"0.90", 90.0},
+                                                                        {"0.95", 95.0},
+                                                                        {"0.99", 99.0}};
 void HistogramMetric::clear() {
     std::lock_guard<SpinLock> l(_lock);
     _stats.clear();
@@ -140,20 +158,36 @@ std::string HistogramMetric::to_string() const {
     return _stats.to_string();
 }
 
-rj::Value HistogramMetric::to_json_value() const {
-    rj::Document document;
-    rj::Document::AllocatorType& allocator = document.GetAllocator();
-    rj::Value json_value(rj::kObjectType);
+std::string HistogramMetric::to_prometheus(const std::string& display_name,
+                                           const Labels& entity_labels,
+                                           const Labels& metric_labels) const {
+    std::stringstream ss;
+    for (const auto& percentile : _s_output_percentiles) {
+        auto quantile_lable = Labels({{"quantile", percentile.first}});
+        ss << display_name
+           << labels_to_string({&entity_labels, &metric_labels, &quantile_lable})
+           << " " << _stats.percentile(percentile.second) << "\n";
+    }
+    ss << display_name << "_sum"
+       << labels_to_string({&entity_labels, &metric_labels})
+       << " " << _stats.sum() << "\n";
+    ss << display_name << "_count"
+       << labels_to_string({&entity_labels, &metric_labels})
+       << " " << _stats.num() << "\n";
 
+    return ss.str();
+}
+
+rj::Value HistogramMetric::to_json_value(rj::Document::AllocatorType& allocator) const {
+    rj::Value json_value(rj::kObjectType);
     json_value.AddMember("total_count", rj::Value(_stats.num()), allocator);
     json_value.AddMember("min", rj::Value(_stats.min()), allocator);
     json_value.AddMember("average", rj::Value(_stats.average()), allocator);
     json_value.AddMember("median", rj::Value(_stats.median()), allocator);
-    json_value.AddMember("percentile_75", rj::Value(_stats.percentile(75.0)), allocator);
-    json_value.AddMember("percentile_95", rj::Value(_stats.percentile(95)), allocator);
-    json_value.AddMember("percentile_99", rj::Value(_stats.percentile(99)), allocator);
-    json_value.AddMember("percentile_99_9", rj::Value(_stats.percentile(99.9)), allocator);
-    json_value.AddMember("percentile_99_99", rj::Value(_stats.percentile(99.99)), allocator);
+    for (const auto& percentile : _s_output_percentiles) {
+        json_value.AddMember(rj::Value(std::string("percentile_").append(percentile.first.substr(2)).c_str(), allocator),
+                             rj::Value(_stats.percentile(percentile.second)), allocator);
+    }
     json_value.AddMember("standard_deviation", rj::Value(_stats.standard_deviation()), allocator);
     json_value.AddMember("max", rj::Value(_stats.max()), allocator);
     json_value.AddMember("total_sum", rj::Value(_stats.sum()), allocator);
@@ -169,6 +203,12 @@ std::string MetricPrototype::combine_name(const std::string& registry_name) cons
     return (registry_name.empty() ? std::string() : registry_name + "_") + simple_name();
 }
 
+std::string MetricPrototype::to_prometheus(const std::string& registry_name) const {
+    std::stringstream ss;
+    ss << "# TYPE " << combine_name(registry_name) << " " << type << "\n";
+    return ss.str();
+}
+
 void MetricEntity::deregister_metric(const MetricPrototype* metric_type) {
     std::lock_guard<SpinLock> l(_lock);
     auto metric = _metrics.find(metric_type);
@@ -260,7 +300,6 @@ void MetricRegistry::trigger_all_hooks(bool force) const {
 }
 
 std::string MetricRegistry::to_prometheus(bool with_tablet_metrics) const {
-    std::stringstream ss;
     // Reorder by MetricPrototype
     EntityMetricsByType entity_metrics_by_types;
     std::lock_guard<SpinLock> l(_lock);
@@ -282,21 +321,21 @@ std::string MetricRegistry::to_prometheus(bool with_tablet_metrics) const {
             }
         }
     }
+
     // Output
+    std::stringstream ss;
     std::string last_group_name;
     for (const auto& entity_metrics_by_type : entity_metrics_by_types) {
         if (last_group_name.empty() ||
             last_group_name != entity_metrics_by_type.first->group_name) {
-            ss << "# TYPE " << entity_metrics_by_type.first->combine_name(_name) << " "
-               << entity_metrics_by_type.first->type << "\n"; // metric TYPE line
+            ss << entity_metrics_by_type.first->to_prometheus(_name);   // metric TYPE line
         }
         last_group_name = entity_metrics_by_type.first->group_name;
         std::string display_name = entity_metrics_by_type.first->combine_name(_name);
         for (const auto& entity_metric : entity_metrics_by_type.second) {
-            ss << display_name // metric name
-               << labels_to_string(entity_metric.first->_labels,
-                                   entity_metrics_by_type.first->labels) // metric labels
-               << " " << entity_metric.second->to_string() << "\n";      // metric value
+            ss << entity_metric.second->to_prometheus(display_name,     // metric key-value line
+                                                      entity_metric.first->_labels,
+                                                      entity_metrics_by_type.first->labels);
         }
     }
 
@@ -334,7 +373,7 @@ std::string MetricRegistry::to_json(bool with_tablet_metrics) const {
             rj::Value unit_val(unit_name(metric.first->unit), allocator);
             metric_obj.AddMember("unit", unit_val, allocator);
             // value
-            metric_obj.AddMember("value", metric.second->to_json_value(), allocator);
+            metric_obj.AddMember("value", metric.second->to_json_value(allocator), allocator);
             doc.PushBack(metric_obj, allocator);
         }
     }
diff --git a/be/src/util/metrics.h b/be/src/util/metrics.h
index 732f532..a9a81d2 100644
--- a/be/src/util/metrics.h
+++ b/be/src/util/metrics.h
@@ -63,12 +63,17 @@ enum class MetricUnit {
 std::ostream& operator<<(std::ostream& os, MetricType type);
 const char* unit_name(MetricUnit unit);
 
+using Labels = std::unordered_map<std::string, std::string>;
+
 class Metric {
 public:
     Metric() {}
     virtual ~Metric() {}
     virtual std::string to_string() const = 0;
-    virtual rj::Value to_json_value() const = 0;
+    virtual std::string to_prometheus(const std::string& display_name,
+                                      const Labels& entity_labels,
+                                      const Labels& metric_labels) const;
+    virtual rj::Value to_json_value(rj::Document::AllocatorType& allocator) const = 0;
 
 private:
     friend class MetricRegistry;
@@ -89,7 +94,7 @@ public:
 
     void set_value(const T& value) { _value.store(value); }
 
-    rj::Value to_json_value() const override { return rj::Value(value()); }
+    rj::Value to_json_value(rj::Document::AllocatorType& allocator) const override { return rj::Value(value()); }
 
 protected:
     std::atomic<T> _value;
@@ -118,7 +123,7 @@ public:
         _value = value;
     }
 
-    rj::Value to_json_value() const override { return rj::Value(value()); }
+    rj::Value to_json_value(rj::Document::AllocatorType& allocator) const override { return rj::Value(value()); }
 
 protected:
     // We use spinlock instead of std::atomic is because atomic don't support
@@ -154,7 +159,7 @@ public:
 
     void increment(const T& delta) { __sync_fetch_and_add(_value.access(), delta); }
 
-    rj::Value to_json_value() const override { return rj::Value(value()); }
+    rj::Value to_json_value(rj::Document::AllocatorType& allocator) const override { return rj::Value(value()); }
 
 protected:
     CoreLocalValue<T> _value;
@@ -182,9 +187,13 @@ public:
     double average() const;
     double standard_deviation() const;
     std::string to_string() const override;
-    rj::Value to_json_value() const override;
+    std::string to_prometheus(const std::string& display_name,
+                              const Labels& entity_labels,
+                              const Labels& metric_labels) const override;
+    rj::Value to_json_value(rj::Document::AllocatorType& allocator) const override;
 
 protected:
+    static std::map<std::string, double> _s_output_percentiles;
     mutable SpinLock _lock;
     HistogramStat _stats;
 };
@@ -242,6 +251,7 @@ public:
 
     std::string simple_name() const;
     std::string combine_name(const std::string& registry_name) const;
+    std::string to_prometheus(const std::string& registry_name) const;
 
     bool is_core_metric;
     MetricType type;
diff --git a/be/test/util/new_metrics_test.cpp b/be/test/util/new_metrics_test.cpp
index 3c56f85..10c4f5a 100644
--- a/be/test/util/new_metrics_test.cpp
+++ b/be/test/util/new_metrics_test.cpp
@@ -407,6 +407,71 @@ test_registry_cpu{mode="guest"} 58
         registry.deregister_entity(entity);
     }
 }
+
+TEST_F(MetricsTest, HistogramRegistryOutput) {
+    MetricRegistry registry("test_registry");
+
+    {
+        // Register one histogram metric to the entity
+        auto entity = registry.register_entity("test_entity");
+
+        MetricPrototype task_duration_type(MetricType::HISTOGRAM,
+                                           MetricUnit::MILLISECONDS,
+                                           "task_duration");
+        HistogramMetric* task_duration = (HistogramMetric*)entity->register_metric<HistogramMetric>(&task_duration_type);
+        for (int j = 1; j <= 100; j++) {
+            task_duration->add(j);
+        }
+
+        ASSERT_EQ(R"(# TYPE test_registry_task_duration histogram
+test_registry_task_duration{quantile="0.50"} 50
+test_registry_task_duration{quantile="0.75"} 75
+test_registry_task_duration{quantile="0.90"} 95.8333
+test_registry_task_duration{quantile="0.95"} 100
+test_registry_task_duration{quantile="0.99"} 100
+test_registry_task_duration_sum 5050
+test_registry_task_duration_count 100
+)",
+                  registry.to_prometheus());
+        ASSERT_EQ(R"*([{"tags":{"metric":"task_duration"},"unit":"milliseconds",)*"
+                R"*("value":{"total_count":100,"min":1,"average":50.5,"median":50.0,)*"
+                R"*("percentile_50":50.0,"percentile_75":75.0,"percentile_90":95.83333333333334,"percentile_95":100.0,"percentile_99":100.0,)*"
+                R"*("standard_deviation":28.86607004772212,"max":100,"total_sum":5050}}])*",
+                  registry.to_json());
+        registry.deregister_entity(entity);
+    }
+
+    {
+        // Register one histogram metric with lables to the entity
+        auto entity = registry.register_entity("test_entity", {{"instance", "test"}});
+
+        MetricPrototype task_duration_type(MetricType::HISTOGRAM,
+                                           MetricUnit::MILLISECONDS,
+                                           "task_duration", "", "",
+                                           {{"type", "create_tablet"}});
+        HistogramMetric* task_duration = (HistogramMetric*)entity->register_metric<HistogramMetric>(&task_duration_type);
+        for (int j = 1; j <= 100; j++) {
+            task_duration->add(j);
+        }
+
+        ASSERT_EQ(R"(# TYPE test_registry_task_duration histogram
+test_registry_task_duration{instance="test",type="create_tablet",quantile="0.50"} 50
+test_registry_task_duration{instance="test",type="create_tablet",quantile="0.75"} 75
+test_registry_task_duration{instance="test",type="create_tablet",quantile="0.90"} 95.8333
+test_registry_task_duration{instance="test",type="create_tablet",quantile="0.95"} 100
+test_registry_task_duration{instance="test",type="create_tablet",quantile="0.99"} 100
+test_registry_task_duration_sum{instance="test",type="create_tablet"} 5050
+test_registry_task_duration_count{instance="test",type="create_tablet"} 100
+)",
+                  registry.to_prometheus());
+        ASSERT_EQ(R"*([{"tags":{"metric":"task_duration","type":"create_tablet","instance":"test"},"unit":"milliseconds",)*"
+                R"*("value":{"total_count":100,"min":1,"average":50.5,"median":50.0,)*"
+                R"*("percentile_50":50.0,"percentile_75":75.0,"percentile_90":95.83333333333334,"percentile_95":100.0,"percentile_99":100.0,)*"
+                R"*("standard_deviation":28.86607004772212,"max":100,"total_sum":5050}}])*",
+                  registry.to_json());
+        registry.deregister_entity(entity);
+    }
+}
 } // namespace doris
 
 int main(int argc, char** argv) {

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org