include/ylt/metric/counter.hpp (296 lines of code) (raw):
#pragma once
#include <algorithm>
#include <array>
#include <atomic>
#include <memory>
#include <thread>
#include <variant>
#include "dynamic_metric.hpp"
#include "thread_local_value.hpp"
namespace ylt::metric {
enum class op_type_t { INC, DEC, SET };
#ifdef CINATRA_ENABLE_METRIC_JSON
struct json_counter_metric_t {
std::vector<std::string_view> labels;
std::variant<int64_t, double> value;
};
YLT_REFL(json_counter_metric_t, labels, value);
struct json_counter_t {
std::string_view name;
std::string_view help;
std::string_view type;
std::vector<std::string_view> labels_name;
std::vector<json_counter_metric_t> metrics;
};
YLT_REFL(json_counter_t, name, help, type, labels_name, metrics);
#endif
template <typename value_type>
class basic_static_counter : public static_metric {
public:
// static counter, no labels, only contains an atomic value.
basic_static_counter(std::string name, std::string help,
uint32_t dupli_count = (std::min)(
128u, std::thread::hardware_concurrency()))
: static_metric(MetricType::Counter, std::move(name), std::move(help)),
dupli_count_((std::max)(1u, dupli_count)),
default_label_value_(dupli_count_) {}
// static counter, contains a static labels with atomic value.
basic_static_counter(std::string name, std::string help,
std::map<std::string, std::string> labels,
uint32_t dupli_count = (std::min)(
128u, std::thread::hardware_concurrency()))
: static_metric(MetricType::Counter, std::move(name), std::move(help),
std::move(labels)),
dupli_count_((std::max)(1u, dupli_count)),
default_label_value_(dupli_count_) {}
void inc(value_type val = 1) {
if (val <= 0) {
return;
}
default_label_value_.inc(val);
}
value_type update(value_type value) {
if (!has_change_.load(std::memory_order::relaxed)) [[unlikely]] {
has_change_.store(true, std::memory_order::relaxed);
}
return default_label_value_.update(value);
}
value_type reset() { return default_label_value_.reset(); }
value_type value() { return default_label_value_.value(); }
void serialize(std::string &str) override {
auto value = default_label_value_.value();
if (value == 0 && !has_change_.load(std::memory_order::relaxed)) {
return;
}
metric_t::serialize_head(str);
serialize_default_label(str, value);
}
#ifdef CINATRA_ENABLE_METRIC_JSON
void serialize_to_json(std::string &str) override {
auto value = default_label_value_.value();
if (value == 0 && !has_change_.load(std::memory_order::relaxed)) {
return;
}
json_counter_t counter{name_, help_, metric_name()};
counter.labels_name.reserve(static_labels_.size());
for (auto &[k, _] : static_labels_) {
counter.labels_name.emplace_back(k);
}
counter.metrics.resize(1);
counter.metrics[0].labels.reserve(static_labels_.size());
for (auto &[k, _] : static_labels_) {
counter.metrics[0].labels.emplace_back(k);
}
counter.metrics[0].value = value;
iguana::to_json(counter, str);
}
#endif
private:
protected:
void serialize_default_label(std::string &str, value_type value) {
str.append(name_);
if (labels_name_.empty()) {
str.append(" ");
}
else {
str.append("{");
build_string(str, labels_name_, labels_value_);
str.append("} ");
}
str.append(std::to_string(value));
str.append("\n");
}
void build_string(std::string &str, const std::vector<std::string> &v1,
const std::vector<std::string> &v2) {
for (size_t i = 0; i < v1.size(); i++) {
str.append(v1[i]).append("=\"").append(v2[i]).append("\"").append(",");
}
str.pop_back();
}
std::atomic<bool> has_change_ = false;
uint32_t dupli_count_;
thread_local_value<value_type> default_label_value_;
};
using counter_t = basic_static_counter<int64_t>;
using counter_d = basic_static_counter<double>;
template <typename value_type, uint8_t N>
class basic_dynamic_counter
: public dynamic_metric_impl<std::atomic<value_type>, N> {
using Base = dynamic_metric_impl<std::atomic<value_type>, N>;
public:
// dynamic labels value
basic_dynamic_counter(std::string name, std::string help,
std::array<std::string, N> labels_name)
: Base(MetricType::Counter, std::move(name), std::move(help),
std::move(labels_name)) {}
using label_key_type = const std::array<std::string, N> &;
void inc(label_key_type labels_value, value_type value = 1) {
detail::inc_impl(Base::try_emplace(labels_value).first->value, value);
}
value_type update(label_key_type labels_value, value_type value) {
return Base::try_emplace(labels_value)
.first->value.exchange(value, std::memory_order::relaxed);
}
value_type value(label_key_type labels_value) {
if (auto ptr = Base::find(labels_value); ptr != nullptr) {
return ptr->value.load(std::memory_order::relaxed);
}
else {
return value_type{};
}
}
void remove_label_value(
const std::map<std::string, std::string> &labels) override {
if (Base::empty()) {
return;
}
const auto &labels_name = this->labels_name();
if (labels.size() > labels_name.size()) {
return;
}
// if (labels.size() == labels_name.size()) { // TODO: speed up for this
// case
// }
// else {
size_t count = 0;
std::vector<std::string_view> vec;
for (auto &lb_name : labels_name) {
if (auto i = labels.find(lb_name); i != labels.end()) {
vec.push_back(i->second);
}
else {
vec.push_back("");
count++;
}
}
if (count == labels_name.size()) {
return;
}
Base::erase_if([&](auto &pair) {
auto &[arr, _] = pair;
if constexpr (N > 0) {
for (size_t i = 0; i < vec.size(); i++) {
if (!vec[i].empty() && vec[i] != arr[i]) {
return false;
}
}
}
return true;
});
//}
}
bool has_label_value(const std::string &value) override {
auto map = Base::copy();
for (auto &e : map) {
auto &label_value = e->label;
if (auto it = std::find(label_value.begin(), label_value.end(), value);
it != label_value.end()) {
return true;
}
}
return false;
}
bool has_label_value(const std::regex ®ex) override {
auto map = Base::copy();
for (auto &e : map) {
auto &label_value = e->label;
if (auto it = std::find_if(label_value.begin(), label_value.end(),
[&](auto &val) {
return std::regex_match(val, regex);
});
it != label_value.end()) {
return true;
}
}
return false;
}
bool has_label_value(const std::vector<std::string> &label_value) override {
std::array<std::string, N> arr{};
size_t size = (std::min)((size_t)N, label_value.size());
if (label_value.size() > N) {
return false;
}
for (size_t i = 0; i < size; i++) {
arr[i] = label_value[i];
}
return Base::find(arr) != nullptr;
}
void serialize(std::string &str) override {
auto map = Base::copy();
if (map.empty()) {
return;
}
std::string value_str;
serialize_map(map, value_str);
if (!value_str.empty()) {
Base::serialize_head(str);
str.append(value_str);
}
}
#ifdef CINATRA_ENABLE_METRIC_JSON
void serialize_to_json(std::string &str) override {
auto map = Base::copy();
json_counter_t counter{Base::name_, Base::help_, Base::metric_name()};
counter.labels_name.reserve(Base::labels_name().size());
for (auto &e : Base::labels_name()) {
counter.labels_name.emplace_back(e);
}
to_json(counter, map, str);
}
template <typename T>
void to_json(json_counter_t &counter, T &map, std::string &str) {
for (auto &e : map) {
auto &k = e->label;
auto &val = e->value;
json_counter_metric_t metric;
size_t index = 0;
metric.labels.reserve(k.size());
for (auto &label_value : k) {
metric.labels.emplace_back(label_value);
}
metric.value = val.load(std::memory_order::relaxed);
counter.metrics.push_back(std::move(metric));
}
if (!counter.metrics.empty()) {
iguana::to_json(counter, str);
}
}
#endif
protected:
template <typename T>
void serialize_map(T &value_map, std::string &str) {
for (auto &e : value_map) {
auto &labels_value = e->label;
auto val = e->value.load(std::memory_order::relaxed);
str.append(Base::name_);
if (Base::labels_name_.empty()) {
str.append(" ");
}
else {
str.append("{");
build_string(str, Base::labels_name_, labels_value);
str.append("} ");
}
str.append(std::to_string(val));
str.append("\n");
}
}
template <class T, std::size_t Size>
bool equal(const std::vector<T> &v, const std::array<T, Size> &a) {
if (v.size() != N)
return false;
return std::equal(v.begin(), v.end(), a.begin());
}
void build_string(std::string &str, const std::vector<std::string> &v1,
const auto &v2) {
for (size_t i = 0; i < v1.size(); i++) {
str.append(v1[i]).append("=\"").append(v2[i]).append("\"").append(",");
}
str.pop_back();
}
};
using dynamic_counter_1t = basic_dynamic_counter<int64_t, 1>;
using dynamic_counter_1d = basic_dynamic_counter<double, 1>;
using dynamic_counter_2t = basic_dynamic_counter<int64_t, 2>;
using dynamic_counter_2d = basic_dynamic_counter<double, 2>;
using dynamic_counter_t = dynamic_counter_2t;
using dynamic_counter_d = dynamic_counter_2d;
using dynamic_counter_3t = basic_dynamic_counter<int64_t, 3>;
using dynamic_counter_3d = basic_dynamic_counter<double, 3>;
using dynamic_counter_4t = basic_dynamic_counter<int64_t, 4>;
using dynamic_counter_4d = basic_dynamic_counter<double, 4>;
using dynamic_counter_5t = basic_dynamic_counter<int64_t, 5>;
using dynamic_counter_5d = basic_dynamic_counter<double, 5>;
} // namespace ylt::metric