include/ylt/metric/histogram.hpp (294 lines of code) (raw):
#pragma once
#include <algorithm>
#include <cstddef>
#include <memory>
#include <vector>
#include "counter.hpp"
#include "dynamic_metric.hpp"
#include "gauge.hpp"
namespace ylt::metric {
#ifdef CINATRA_ENABLE_METRIC_JSON
struct json_histogram_metric_t {
std::map<std::string, std::string> labels;
std::map<double, int64_t> quantiles;
int64_t count;
double sum;
};
YLT_REFL(json_histogram_metric_t, labels, quantiles, count, sum);
struct json_histogram_t {
std::string name;
std::string help;
std::string type;
std::vector<json_histogram_metric_t> metrics;
};
YLT_REFL(json_histogram_t, name, help, type, metrics);
#endif
template <typename value_type>
class basic_static_histogram : public static_metric {
public:
basic_static_histogram(std::string name, std::string help,
std::vector<double> buckets, size_t dupli_count = 2)
: bucket_boundaries_(std::move(buckets)),
static_metric(MetricType::Histogram, std::move(name), std::move(help)),
sum_(std::make_shared<gauge_t>("", "", dupli_count)) {
init_bucket_counter(dupli_count, bucket_boundaries_.size());
}
basic_static_histogram(std::string name, std::string help,
std::vector<double> buckets,
std::map<std::string, std::string> labels,
size_t dupli_count = 2)
: bucket_boundaries_(std::move(buckets)),
static_metric(MetricType::Histogram, name, help, labels),
sum_(std::make_shared<gauge_t>("", "", dupli_count)) {
init_bucket_counter(dupli_count, bucket_boundaries_.size());
}
void observe(value_type value) {
const auto bucket_index = static_cast<std::size_t>(
std::distance(bucket_boundaries_.begin(),
std::lower_bound(bucket_boundaries_.begin(),
bucket_boundaries_.end(), value)));
sum_->inc(value);
bucket_counts_[bucket_index]->inc();
}
auto get_bucket_counts() { return bucket_counts_; }
void serialize(std::string &str) override {
auto val = sum_->value();
if (val == 0) {
return;
}
serialize_head(str);
value_type count = 0;
auto bucket_counts = get_bucket_counts();
for (size_t i = 0; i < bucket_counts.size(); i++) {
auto counter = bucket_counts[i];
str.append(name_).append("_bucket{");
if (!labels_name_.empty()) {
build_label_string(str, labels_name_, labels_value_);
str.append(",");
}
if (i == bucket_boundaries_.size()) {
str.append("le=\"").append("+Inf").append("\"} ");
}
else {
str.append("le=\"")
.append(std::to_string(bucket_boundaries_[i]))
.append("\"} ");
}
count += counter->value();
str.append(std::to_string(count));
str.append("\n");
}
str.append(name_).append("_sum ").append(std::to_string(val)).append("\n");
str.append(name_)
.append("_count ")
.append(std::to_string(count))
.append("\n");
}
#ifdef CINATRA_ENABLE_METRIC_JSON
void serialize_to_json(std::string &str) override {
auto val = sum_->value();
if (val == 0) {
return;
}
json_histogram_t hist{name_, help_, std::string(metric_name())};
value_type count = 0;
auto bucket_counts = get_bucket_counts();
json_histogram_metric_t metric{};
for (size_t i = 0; i < bucket_counts.size(); i++) {
auto counter = bucket_counts[i];
count += counter->value();
if (i == bucket_boundaries_.size()) {
metric.quantiles.emplace(std::numeric_limits<int>::max(),
(int64_t)count);
}
else {
metric.quantiles.emplace(bucket_boundaries_[i],
(int64_t)counter->value());
}
}
metric.count = (int64_t)count;
metric.sum = val;
for (size_t i = 0; i < labels_value_.size(); i++) {
metric.labels[labels_name_[i]] = labels_value_[i];
}
hist.metrics.push_back(std::move(metric));
iguana::to_json(hist, str);
}
#endif
private:
void init_bucket_counter(size_t dupli_count, size_t bucket_size) {
for (size_t i = 0; i < bucket_size + 1; i++) {
bucket_counts_.push_back(
std::make_shared<counter_t>("", "", dupli_count));
}
}
template <class ForwardIterator>
bool is_strict_sorted(ForwardIterator first, ForwardIterator last) {
return std::adjacent_find(first, last,
std::greater_equal<typename std::iterator_traits<
ForwardIterator>::value_type>()) == last;
}
std::vector<double> bucket_boundaries_;
std::vector<std::shared_ptr<counter_t>> bucket_counts_; // readonly
std::shared_ptr<gauge_t> sum_;
};
using histogram_t = basic_static_histogram<int64_t>;
using histogram_d = basic_static_histogram<double>;
template <typename value_type, uint8_t N>
class basic_dynamic_histogram : public dynamic_metric {
public:
basic_dynamic_histogram(std::string name, std::string help,
std::vector<double> buckets,
std::array<std::string, N> labels_name)
: bucket_boundaries_(buckets),
dynamic_metric(MetricType::Histogram, name, help, labels_name),
sum_(std::make_shared<basic_dynamic_gauge<value_type, N>>(
name, help, labels_name)) {
for (size_t i = 0; i < buckets.size() + 1; i++) {
bucket_counts_.push_back(
std::make_shared<basic_dynamic_counter<value_type, N>>(name, help,
labels_name));
}
}
void observe(const std::array<std::string, N> &labels_value,
value_type value) {
const auto bucket_index = static_cast<std::size_t>(
std::distance(bucket_boundaries_.begin(),
std::lower_bound(bucket_boundaries_.begin(),
bucket_boundaries_.end(), value)));
sum_->inc(labels_value, value);
bucket_counts_[bucket_index]->inc(labels_value);
}
void clean_expired_label() override {
sum_->clean_expired_label();
for (auto &m : bucket_counts_) {
m->clean_expired_label();
}
}
auto get_bucket_counts() { return bucket_counts_; }
bool has_label_value(const std::string &label_val) override {
return sum_->has_label_value(label_val);
}
bool has_label_value(const std::regex ®ex) override {
return sum_->has_label_value(regex);
}
bool has_label_value(const std::vector<std::string> &label_value) override {
return sum_->has_label_value(label_value);
}
size_t label_value_count() const { return sum_->label_value_count(); }
void serialize(std::string &str) override {
auto value_map = sum_->copy();
if (value_map.empty()) {
return;
}
serialize_head(str);
std::string value_str;
auto bucket_counts = get_bucket_counts();
for (auto &e : value_map) {
auto &labels_value = e->label;
auto &value = e->value;
if (value == 0) {
continue;
}
value_type count = 0;
for (size_t i = 0; i < bucket_counts.size(); i++) {
auto counter = bucket_counts[i];
value_str.append(name_).append("_bucket{");
if (!labels_name_.empty()) {
build_label_string(value_str, labels_name_, labels_value);
value_str.append(",");
}
if (i == bucket_boundaries_.size()) {
value_str.append("le=\"").append("+Inf").append("\"} ");
}
else {
value_str.append("le=\"")
.append(std::to_string(bucket_boundaries_[i]))
.append("\"} ");
}
count += counter->value(labels_value);
value_str.append(std::to_string(count));
value_str.append("\n");
}
str.append(value_str);
str.append(name_);
str.append("_sum{");
build_label_string(str, sum_->labels_name(), labels_value);
str.append("} ");
str.append(std::to_string(value));
str.append("\n");
str.append(name_).append("_count{");
build_label_string(str, sum_->labels_name(), labels_value);
str.append("} ");
str.append(std::to_string(count));
str.append("\n");
}
if (value_str.empty()) {
str.clear();
}
}
#ifdef CINATRA_ENABLE_METRIC_JSON
void serialize_to_json(std::string &str) override {
auto value_map = sum_->copy();
if (value_map.empty()) {
return;
}
json_histogram_t hist{name_, help_, std::string(metric_name())};
auto bucket_counts = get_bucket_counts();
for (auto &e : value_map) {
auto &labels_value = e->label;
auto &value = e->value;
if (value == 0) {
continue;
}
size_t count = 0;
json_histogram_metric_t metric{};
for (size_t i = 0; i < bucket_counts.size(); i++) {
auto counter = bucket_counts[i];
count += counter->value(labels_value);
if (i == bucket_boundaries_.size()) {
metric.quantiles.emplace(std::numeric_limits<int>::max(),
(int64_t)count);
}
else {
metric.quantiles.emplace(bucket_boundaries_[i],
(int64_t)counter->value(labels_value));
}
}
metric.count = (int64_t)count;
metric.sum = sum_->value(labels_value);
for (size_t i = 0; i < labels_value.size(); i++) {
metric.labels[sum_->labels_name()[i]] = labels_value[i];
}
hist.metrics.push_back(std::move(metric));
}
if (!hist.metrics.empty()) {
iguana::to_json(hist, str);
}
}
#endif
private:
template <class ForwardIterator>
bool is_strict_sorted(ForwardIterator first, ForwardIterator last) {
return std::adjacent_find(first, last,
std::greater_equal<typename std::iterator_traits<
ForwardIterator>::value_type>()) == last;
}
std::vector<double> bucket_boundaries_;
std::vector<std::shared_ptr<basic_dynamic_counter<value_type, N>>>
bucket_counts_; // readonly
std::shared_ptr<basic_dynamic_gauge<value_type, N>> sum_;
};
using dynamic_histogram_1t = basic_dynamic_histogram<int64_t, 1>;
using dynamic_histogram_1d = basic_dynamic_histogram<double, 1>;
using dynamic_histogram_2t = basic_dynamic_histogram<int64_t, 2>;
using dynamic_histogram_2d = basic_dynamic_histogram<double, 2>;
using dynamic_histogram_t = dynamic_histogram_2t;
using dynamic_histogram_d = dynamic_histogram_2d;
using dynamic_histogram_3t = basic_dynamic_histogram<int64_t, 3>;
using dynamic_histogram_3d = basic_dynamic_histogram<double, 3>;
using dynamic_histogram_4t = basic_dynamic_histogram<int64_t, 4>;
using dynamic_histogram_4d = basic_dynamic_histogram<double, 4>;
using dynamic_histogram_5t = basic_dynamic_histogram<int64_t, 5>;
using dynamic_histogram_5d = basic_dynamic_histogram<double, 5>;
} // namespace ylt::metric