include/ylt/metric/summary.hpp (239 lines of code) (raw):
#pragma once
#include <algorithm>
#include <atomic>
#include <chrono>
#include <cstdint>
#include <mutex>
#include "counter.hpp"
#include "dynamic_metric.hpp"
#include "summary_impl.hpp"
#if __has_include("ylt/util/concurrentqueue.h")
#include "ylt/util/concurrentqueue.h"
#else
#include "cinatra/ylt/util/concurrentqueue.h"
#endif
namespace ylt::metric {
#ifdef CINATRA_ENABLE_METRIC_JSON
struct json_summary_metric_t {
std::vector<std::string_view> labels;
std::vector<float> quantiles_value;
uint64_t count;
double sum;
};
YLT_REFL(json_summary_metric_t, labels, quantiles_value, count, sum);
struct json_summary_t {
std::string_view name;
std::string_view help;
std::string_view type;
const std::vector<std::string>& labels_name;
const std::vector<double>& quantiles_key;
std::vector<json_summary_metric_t> metrics;
};
YLT_REFL(json_summary_t, name, help, type, labels_name, quantiles_key, metrics);
#endif
class summary_t : public static_metric {
public:
summary_t(std::string name, std::string help, std::vector<double> quantiles,
std::chrono::seconds max_age = std::chrono::seconds{0})
: static_metric(MetricType::Summary, std::move(name), std::move(help)),
quantiles_(std::move(quantiles)),
impl_(quantiles_,
std::chrono::duration_cast<std::chrono::seconds>(max_age)) {
if (!std::is_sorted(quantiles_.begin(), quantiles_.end()))
std::sort(quantiles_.begin(), quantiles_.end());
}
summary_t(std::string name, std::string help, std::vector<double> quantiles,
std::map<std::string, std::string> static_labels,
std::chrono::seconds max_age = std::chrono::seconds{60})
: static_metric(MetricType::Summary, std::move(name), std::move(help),
std::move(static_labels)),
quantiles_(std::move(quantiles)),
impl_(quantiles_,
std::chrono::duration_cast<std::chrono::seconds>(max_age)) {
if (!std::is_sorted(quantiles_.begin(), quantiles_.end()))
std::sort(quantiles_.begin(), quantiles_.end());
}
void observe(float value) {
has_refreshed_.store(true, std::memory_order_relaxed);
impl_.insert(value);
}
std::vector<float> get_rates() {
uint64_t count;
double sum;
return get_rates(sum, count);
}
std::vector<float> get_rates(uint64_t& count) {
double sum;
return get_rates(sum, count);
}
std::vector<float> get_rates(double& sum) {
uint64_t count;
return get_rates(sum, count);
}
std::vector<float> get_rates(double& sum, uint64_t& count) {
return impl_.stat(sum, count);
}
virtual void serialize(std::string& str) override {
if (quantiles_.empty()) {
return;
}
double sum = 0;
uint64_t count = 0;
auto rates = get_rates(sum, count);
if (count == 0 && !has_refreshed_.load(std::memory_order_relaxed)) {
return;
}
serialize_head(str);
for (size_t i = 0; i < quantiles_.size(); i++) {
str.append(name_);
str.append("{");
if (!labels_name_.empty()) {
build_label_string(str, labels_name_, labels_value_);
str.append(",");
}
str.append("quantile=\"");
str.append(std::to_string(quantiles_[i])).append("\"} ");
str.append(std::to_string(rates[i])).append("\n");
}
str.append(name_).append("_sum ").append(std::to_string(sum)).append("\n");
str.append(name_)
.append("_count ")
.append(std::to_string((uint64_t)count))
.append("\n");
}
#ifdef CINATRA_ENABLE_METRIC_JSON
virtual void serialize_to_json(std::string& str) override {
if (quantiles_.empty()) {
return;
}
json_summary_t summary{name_, help_, metric_name(), labels_name(),
quantiles_};
json_summary_metric_t metric;
metric.quantiles_value = get_rates(metric.sum, metric.count);
if (metric.count == 0 && !has_refreshed_.load(std::memory_order_relaxed)) {
return;
}
metric.labels.reserve(labels_value_.size());
for (auto& e : labels_value_) metric.labels.emplace_back(e);
summary.metrics.push_back(std::move(metric));
iguana::to_json(summary, str);
}
#endif
private:
std::atomic<bool> has_refreshed_;
std::vector<double> quantiles_;
ylt::metric::detail::summary_impl<uint64_t> impl_;
};
template <size_t N>
class basic_dynamic_summary
: public dynamic_metric_impl<ylt::metric::detail::summary_impl<uint32_t>,
N> {
private:
using Base =
dynamic_metric_impl<ylt::metric::detail::summary_impl<uint32_t>, N>;
public:
basic_dynamic_summary(
std::string name, std::string help, std::vector<double> quantiles,
std::array<std::string, N> labels_name,
std::chrono::milliseconds max_age = std::chrono::seconds{60})
: Base(MetricType::Summary, std::move(name), std::move(help),
std::move(labels_name)),
quantiles_(std::move(quantiles)),
max_age_(max_age) {
if (!std::is_sorted(quantiles_.begin(), quantiles_.end()))
std::sort(quantiles_.begin(), quantiles_.end());
}
void observe(const std::array<std::string, N>& labels_value, float value) {
Base::try_emplace(labels_value, quantiles_).first->value.insert(value);
}
std::vector<float> get_rates(const std::array<std::string, N>& labels_value) {
double sum;
uint64_t count;
return Base::try_emplace(labels_value, quantiles_)
.first->value.get_rates(sum, count);
}
std::vector<float> get_rates(const std::array<std::string, N>& labels_value,
uint64_t& count) {
double sum;
return Base::try_emplace(labels_value, quantiles_)
.first->value.get_rates(sum, count);
}
std::vector<float> get_rates(const std::array<std::string, N>& labels_value,
double& sum) {
uint64_t count;
return Base::try_emplace(labels_value, quantiles_)
.first->value.get_rates(sum, count);
}
std::vector<float> get_rates(const std::array<std::string, N>& labels_value,
double& sum, uint64_t& count) {
return Base::try_emplace(labels_value, quantiles_)
.first->value.stat(sum, count);
}
virtual void serialize(std::string& str) override {
double sum = 0;
uint64_t count = 0;
auto map = Base::copy();
for (auto& e : map) {
auto& labels_value = e->label;
auto& summary_value = e->value;
auto rates = summary_value.stat(sum, count);
for (size_t i = 0; i < quantiles_.size(); i++) {
str.append(Base::name_);
str.append("{");
Base::build_label_string(str, Base::labels_name_, labels_value);
str.append(",");
str.append("quantile=\"");
str.append(std::to_string(quantiles_[i])).append("\"} ");
str.append(std::to_string(rates[i])).append("\n");
}
str.append(Base::name_).append("_sum ");
str.append("{");
Base::build_label_string(str, Base::labels_name_, labels_value);
str.append("} ");
str.append(std::to_string(sum)).append("\n");
str.append(Base::name_).append("_count ");
str.append("{");
Base::build_label_string(str, Base::labels_name_, labels_value);
str.append("} ");
str.append(std::to_string((uint64_t)count)).append("\n");
}
}
#ifdef CINATRA_ENABLE_METRIC_JSON
virtual void serialize_to_json(std::string& str) override {
auto map = Base::copy();
if (map.empty()) {
return;
}
json_summary_t summary{Base::name_, Base::help_, Base::metric_name(),
Base::labels_name(), quantiles_};
summary.metrics.reserve(map.size());
for (size_t i = 0; i < map.size(); ++i) {
auto& labels_value = map[i]->label;
auto& summary_value = map[i]->value;
double sum = 0;
uint64_t count = 0;
auto rates = summary_value.stat(sum, count);
if (count == 0)
continue;
summary.metrics.emplace_back();
json_summary_metric_t& metric = summary.metrics.back();
metric.count = count;
metric.sum = sum;
metric.quantiles_value = std::move(rates);
metric.labels.reserve(labels_value.size());
for (auto& e : labels_value) metric.labels.emplace_back(e);
}
iguana::to_json(summary, str);
}
#endif
private:
std::vector<double> quantiles_;
std::chrono::milliseconds max_age_;
};
using dynamic_summary_1 = basic_dynamic_summary<1>;
using dynamic_summary_2 = basic_dynamic_summary<2>;
using dynamic_summary = dynamic_summary_2;
using dynamic_summary_3 = basic_dynamic_summary<3>;
using dynamic_summary_4 = basic_dynamic_summary<4>;
using dynamic_summary_5 = basic_dynamic_summary<5>;
} // namespace ylt::metric