]> git.sesse.net Git - nageru/commitdiff
Store the metrics sorted.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 12 Jun 2017 22:47:45 +0000 (00:47 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 12 Jun 2017 22:47:45 +0000 (00:47 +0200)
This makes it easier to remove them later (since you can index by
name), which we're going to need.

metrics.cpp
metrics.h

index fd5e54c1c7f48b09d38f64ddd91d84bee4a3b6c0..db3c4b8c4da97c571f264c0b7f1eb60810d3208b 100644 (file)
@@ -18,9 +18,7 @@ double get_timestamp_for_metrics()
        return duration<double>(system_clock::now().time_since_epoch()).count();
 }
 
-namespace {
-
-string serialize_name(const string &name, const vector<pair<string, string>> &labels)
+string Metrics::serialize_name(const string &name, const vector<pair<string, string>> &labels)
 {
        if (labels.empty()) {
                return "nageru_" + name;
@@ -36,18 +34,14 @@ string serialize_name(const string &name, const vector<pair<string, string>> &la
        return "nageru_" + name + "{" + label_str + "}";
 }
 
-}  // namespace
-
 void Metrics::add(const string &name, const vector<pair<string, string>> &labels, atomic<int64_t> *location, Metrics::Type type)
 {
        Metric metric;
        metric.data_type = DATA_TYPE_INT64;
-       metric.name = name;
-       metric.labels = labels;
        metric.location_int64 = location;
 
        lock_guard<mutex> lock(mu);
-       metrics.push_back(metric);
+       metrics.emplace(MetricKey(name, labels), metric);
        assert(types.count(name) == 0 || types[name] == type);
        types[name] = type;
 }
@@ -56,12 +50,10 @@ void Metrics::add(const string &name, const vector<pair<string, string>> &labels
 {
        Metric metric;
        metric.data_type = DATA_TYPE_DOUBLE;
-       metric.name = name;
-       metric.labels = labels;
        metric.location_double = location;
 
        lock_guard<mutex> lock(mu);
-       metrics.push_back(metric);
+       metrics.emplace(MetricKey(name, labels), metric);
        assert(types.count(name) == 0 || types[name] == type);
        types[name] = type;
 }
@@ -70,12 +62,10 @@ void Metrics::add(const string &name, const vector<pair<string, string>> &labels
 {
        Metric metric;
        metric.data_type = DATA_TYPE_HISTOGRAM;
-       metric.name = name;
-       metric.labels = labels;
        metric.location_histogram = location;
 
        lock_guard<mutex> lock(mu);
-       metrics.push_back(metric);
+       metrics.emplace(MetricKey(name, labels), metric);
        assert(types.count(name) == 0 || types[name] == TYPE_HISTOGRAM);
        types[name] = TYPE_HISTOGRAM;
 }
@@ -87,22 +77,29 @@ string Metrics::serialize() const
        ss.precision(20);
 
        lock_guard<mutex> lock(mu);
-       for (const auto &name_and_type : types) {
-               if (name_and_type.second == TYPE_GAUGE) {
-                       ss << "# TYPE nageru_" << name_and_type.first << " gauge\n";
-               } else if (name_and_type.second == TYPE_HISTOGRAM) {
-                       ss << "# TYPE nageru_" << name_and_type.first << " histogram\n";
+       auto type_it = types.cbegin();
+       for (const auto &key_and_metric : metrics) {
+               const string &name = key_and_metric.first.serialized;
+               const Metric &metric = key_and_metric.second;
+
+               if (type_it != types.cend() &&
+                   key_and_metric.first.name == type_it->first) {
+                       // It's the first time we print out any metric with this name,
+                       // so add the type header.
+                       if (type_it->second == TYPE_GAUGE) {
+                               ss << "# TYPE nageru_" << type_it->first << " gauge\n";
+                       } else if (type_it->second == TYPE_HISTOGRAM) {
+                               ss << "# TYPE nageru_" << type_it->first << " histogram\n";
+                       }
+                       ++type_it;
                }
-       }
-       for (const Metric &metric : metrics) {
-               string name = serialize_name(metric.name, metric.labels);
 
                if (metric.data_type == DATA_TYPE_INT64) {
                        ss << name << " " << metric.location_int64->load() << "\n";
                } else if (metric.data_type == DATA_TYPE_DOUBLE) {
                        ss << name << " " << metric.location_double->load() << "\n";
                } else {
-                       ss << metric.location_histogram->serialize(metric.name, metric.labels);
+                       ss << metric.location_histogram->serialize(key_and_metric.first.name, key_and_metric.first.labels);
                }
        }
 
@@ -167,13 +164,13 @@ string Histogram::serialize(const string &name, const vector<pair<string, string
                bucket_labels.emplace_back("le", le_ss.str());
 
                count += buckets[bucket_idx].count.load();
-               ss << serialize_name(name + "_bucket", bucket_labels) << " " << count << "\n";
+               ss << Metrics::serialize_name(name + "_bucket", bucket_labels) << " " << count << "\n";
        }
 
        count += count_after_last_bucket.load();
 
-       ss << serialize_name(name + "_sum", labels) << " " << sum.load() << "\n";
-       ss << serialize_name(name + "_count", labels) << " " << count << "\n";
+       ss << Metrics::serialize_name(name + "_sum", labels) << " " << sum.load() << "\n";
+       ss << Metrics::serialize_name(name + "_count", labels) << " " << count << "\n";
 
        return ss.str();
 }
index bd506a97c6c11dfdbc19247706acae5cb995b497..40aa8f7bf387159c6b31a40688e9e7b06b1fa37f 100644 (file)
--- a/metrics.h
+++ b/metrics.h
@@ -51,16 +51,30 @@ public:
        std::string serialize() const;
 
 private:
+       static std::string serialize_name(const std::string &name, const std::vector<std::pair<std::string, std::string>> &labels);
+
        enum DataType {
                DATA_TYPE_INT64,
                DATA_TYPE_DOUBLE,
                DATA_TYPE_HISTOGRAM,
        };
-
+       struct MetricKey {
+               MetricKey(const std::string &name, const std::vector<std::pair<std::string, std::string>> labels)
+                       : name(name), labels(labels), serialized(serialize_name(name, labels))
+               {
+               }
+
+               bool operator< (const MetricKey &other) const
+               {
+                       return serialized < other.serialized;
+               }
+
+               const std::string name;
+               const std::vector<std::pair<std::string, std::string>> labels;
+               const std::string serialized;
+       };
        struct Metric {
                DataType data_type;
-               std::string name;
-               std::vector<std::pair<std::string, std::string>> labels;
                union {
                        std::atomic<int64_t> *location_int64;
                        std::atomic<double> *location_double;
@@ -69,9 +83,11 @@ private:
        };
 
        mutable std::mutex mu;
-       std::map<std::string, Type> types;
-       std::vector<Metric> metrics;
+       std::map<std::string, Type> types;  // Ordered the same as metrics, assuming no { signs in names (which are illegal).
+       std::map<MetricKey, Metric> metrics;
        std::vector<Histogram> histograms;
+
+       friend class Histogram;
 };
 
 class Histogram {