X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=metrics.cpp;h=5079acb2064e5515a1a549af00c35924edcc57dd;hb=96cb6414f85e0ef4d660b7bd56267303e80fcd05;hp=1f3f8a1c18b6478b2c7d5b3956137a3aa67169d9;hpb=1df7849fad1d0647a02951abaa60f9e4f40ce360;p=nageru diff --git a/metrics.cpp b/metrics.cpp index 1f3f8a1..5079acb 100644 --- a/metrics.cpp +++ b/metrics.cpp @@ -4,19 +4,29 @@ #include #include +#include #include #include using namespace std; +using namespace std::chrono; Metrics global_metrics; -namespace { +double get_timestamp_for_metrics() +{ + return duration(system_clock::now().time_since_epoch()).count(); +} + +string Metrics::serialize_name(const string &name, const vector> &labels) +{ + return "nageru_" + name + serialize_labels(labels); +} -string serialize_name(const string &name, const vector> &labels) +string Metrics::serialize_labels(const vector> &labels) { if (labels.empty()) { - return "nageru_" + name; + return ""; } string label_str; @@ -26,21 +36,17 @@ string serialize_name(const string &name, const vector> &la } label_str += label.first + "=\"" + label.second + "\""; } - return "nageru_" + name + "{" + label_str + "}"; + return "{" + label_str + "}"; } -} // namespace - void Metrics::add(const string &name, const vector> &labels, atomic *location, Metrics::Type type) { Metric metric; metric.data_type = DATA_TYPE_INT64; - metric.name = name; - metric.labels = labels; metric.location_int64 = location; lock_guard lock(mu); - metrics.push_back(metric); + metrics.emplace(MetricKey(name, labels), metric); assert(types.count(name) == 0 || types[name] == type); types[name] = type; } @@ -49,30 +55,43 @@ void Metrics::add(const string &name, const vector> &labels { Metric metric; metric.data_type = DATA_TYPE_DOUBLE; - metric.name = name; - metric.labels = labels; metric.location_double = location; lock_guard lock(mu); - metrics.push_back(metric); + metrics.emplace(MetricKey(name, labels), metric); assert(types.count(name) == 0 || types[name] == type); types[name] = type; } -void Metrics::add(const string &name, const vector> &labels, Histogram *location) +void Metrics::add(const string &name, const vector> &labels, Histogram *location, Laziness laziness) { Metric metric; metric.data_type = DATA_TYPE_HISTOGRAM; - metric.name = name; - metric.labels = labels; + metric.laziness = laziness; metric.location_histogram = location; lock_guard 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; } +void Metrics::remove(const string &name, const vector> &labels) +{ + lock_guard lock(mu); + + auto it = metrics.find(MetricKey(name, labels)); + assert(it != metrics.end()); + + // If this is the last metric with this name, remove the type as well. + if (!((it != metrics.begin() && prev(it)->first.name == name) || + (it != metrics.end() && next(it)->first.name == name))) { + types.erase(name); + } + + metrics.erase(it); +} + string Metrics::serialize() const { stringstream ss; @@ -80,22 +99,35 @@ string Metrics::serialize() const ss.precision(20); lock_guard 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) { + string name = "nageru_" + key_and_metric.first.name + key_and_metric.first.serialized_labels; + 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"; + double val = metric.location_double->load(); + if (isnan(val)) { + // Prometheus can't handle “-nan”. + ss << name << " NaN\n"; + } else { + ss << name << " " << val << "\n"; + } } else { - ss << metric.location_histogram->serialize(metric.name, metric.labels); + ss << metric.location_histogram->serialize(metric.laziness, key_and_metric.first.name, key_and_metric.first.labels); } } @@ -144,8 +176,22 @@ void Histogram::count_event(double val) sum = sum + val; } -string Histogram::serialize(const string &name, const vector> &labels) const +string Histogram::serialize(Metrics::Laziness laziness, const string &name, const vector> &labels) const { + // Check if the histogram is empty and should not be serialized. + if (laziness == Metrics::PRINT_WHEN_NONEMPTY && count_after_last_bucket.load() == 0) { + bool empty = true; + for (size_t bucket_idx = 0; bucket_idx < num_buckets; ++bucket_idx) { + if (buckets[bucket_idx].count.load() != 0) { + empty = false; + break; + } + } + if (empty) { + return ""; + } + } + stringstream ss; ss.imbue(locale("C")); ss.precision(20); @@ -160,13 +206,13 @@ string Histogram::serialize(const string &name, const vector