301 lines
7.7 KiB
C++
301 lines
7.7 KiB
C++
#pragma once
|
|
#include <atomic>
|
|
#include <chrono>
|
|
|
|
#include "metric.hpp"
|
|
|
|
namespace ylt::metric {
|
|
enum class op_type_t { INC, DEC, SET };
|
|
|
|
#ifdef CINATRA_ENABLE_METRIC_JSON
|
|
struct json_counter_metric_t {
|
|
std::unordered_multimap<std::string, std::string> labels;
|
|
int64_t value;
|
|
};
|
|
REFLECTION(json_counter_metric_t, labels, value);
|
|
struct json_counter_t {
|
|
std::string name;
|
|
std::string help;
|
|
std::string type;
|
|
std::vector<json_counter_metric_t> metrics;
|
|
};
|
|
REFLECTION(json_counter_t, name, help, type, metrics);
|
|
#endif
|
|
|
|
class counter_t : public metric_t {
|
|
public:
|
|
// default, no labels, only contains an atomic value.
|
|
counter_t(std::string name, std::string help)
|
|
: metric_t(MetricType::Counter, std::move(name), std::move(help)) {
|
|
use_atomic_ = true;
|
|
}
|
|
|
|
// static labels value, contains a map with atomic value.
|
|
counter_t(std::string name, std::string help,
|
|
std::map<std::string, std::string> labels)
|
|
: metric_t(MetricType::Counter, std::move(name), std::move(help),
|
|
std::move(labels)) {
|
|
atomic_value_map_.emplace(labels_value_, 0);
|
|
use_atomic_ = true;
|
|
}
|
|
|
|
// dynamic labels value
|
|
counter_t(std::string name, std::string help,
|
|
std::vector<std::string> labels_name)
|
|
: metric_t(MetricType::Counter, std::move(name), std::move(help),
|
|
std::move(labels_name)) {}
|
|
|
|
virtual ~counter_t() {}
|
|
|
|
double value() { return default_lable_value_; }
|
|
|
|
double value(const std::vector<std::string> &labels_value) {
|
|
if (use_atomic_) {
|
|
double val = atomic_value_map_[labels_value];
|
|
return val;
|
|
}
|
|
else {
|
|
std::lock_guard lock(mtx_);
|
|
return value_map_[labels_value];
|
|
}
|
|
}
|
|
|
|
metric_hash_map<double> value_map() override {
|
|
metric_hash_map<double> map;
|
|
if (use_atomic_) {
|
|
map = {atomic_value_map_.begin(), atomic_value_map_.end()};
|
|
}
|
|
else {
|
|
std::lock_guard lock(mtx_);
|
|
map = value_map_;
|
|
}
|
|
return map;
|
|
}
|
|
|
|
void serialize(std::string &str) override {
|
|
if (labels_name_.empty()) {
|
|
if (default_lable_value_ == 0) {
|
|
return;
|
|
}
|
|
serialize_head(str);
|
|
serialize_default_label(str);
|
|
return;
|
|
}
|
|
|
|
serialize_head(str);
|
|
std::string s;
|
|
if (use_atomic_) {
|
|
serialize_map(atomic_value_map_, s);
|
|
}
|
|
else {
|
|
serialize_map(value_map_, s);
|
|
}
|
|
|
|
if (s.empty()) {
|
|
str.clear();
|
|
}
|
|
else {
|
|
str.append(s);
|
|
}
|
|
}
|
|
|
|
#ifdef CINATRA_ENABLE_METRIC_JSON
|
|
void serialize_to_json(std::string &str) override {
|
|
std::string s;
|
|
if (labels_name_.empty()) {
|
|
if (default_lable_value_ == 0) {
|
|
return;
|
|
}
|
|
json_counter_t counter{name_, help_, std::string(metric_name())};
|
|
int64_t value = default_lable_value_;
|
|
counter.metrics.push_back({{}, value});
|
|
iguana::to_json(counter, str);
|
|
return;
|
|
}
|
|
|
|
json_counter_t counter{name_, help_, std::string(metric_name())};
|
|
if (use_atomic_) {
|
|
to_json(counter, atomic_value_map_, str);
|
|
}
|
|
else {
|
|
to_json(counter, value_map_, str);
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
void to_json(json_counter_t &counter, T &map, std::string &str) {
|
|
for (auto &[k, v] : map) {
|
|
json_counter_metric_t metric;
|
|
size_t index = 0;
|
|
for (auto &label_value : k) {
|
|
metric.labels.emplace(labels_name_[index++], label_value);
|
|
}
|
|
metric.value = (int64_t)v;
|
|
counter.metrics.push_back(std::move(metric));
|
|
}
|
|
iguana::to_json(counter, str);
|
|
}
|
|
#endif
|
|
|
|
void inc(double val = 1) {
|
|
if (val < 0) {
|
|
throw std::invalid_argument("the value is less than zero");
|
|
}
|
|
|
|
#ifdef __APPLE__
|
|
mac_os_atomic_fetch_add(&default_lable_value_, val);
|
|
#else
|
|
default_lable_value_ += val;
|
|
#endif
|
|
}
|
|
|
|
void inc(const std::vector<std::string> &labels_value, double value = 1) {
|
|
if (value == 0) {
|
|
return;
|
|
}
|
|
|
|
validate(labels_value, value);
|
|
if (use_atomic_) {
|
|
if (labels_value != labels_value_) {
|
|
throw std::invalid_argument(
|
|
"the given labels_value is not match with origin labels_value");
|
|
}
|
|
set_value<true>(atomic_value_map_[labels_value], value, op_type_t::INC);
|
|
}
|
|
else {
|
|
std::lock_guard lock(mtx_);
|
|
set_value<false>(value_map_[labels_value], value, op_type_t::INC);
|
|
}
|
|
}
|
|
|
|
void update(double value) { default_lable_value_ = value; }
|
|
|
|
void update(const std::vector<std::string> &labels_value, double value) {
|
|
if (labels_value.empty() || labels_name_.size() != labels_value.size()) {
|
|
throw std::invalid_argument(
|
|
"the number of labels_value name and labels_value is not match");
|
|
}
|
|
if (use_atomic_) {
|
|
if (labels_value != labels_value_) {
|
|
throw std::invalid_argument(
|
|
"the given labels_value is not match with origin labels_value");
|
|
}
|
|
set_value<true>(atomic_value_map_[labels_value], value, op_type_t::SET);
|
|
}
|
|
else {
|
|
std::lock_guard lock(mtx_);
|
|
set_value<false>(value_map_[labels_value], value, op_type_t::SET);
|
|
}
|
|
}
|
|
|
|
metric_hash_map<std::atomic<double>> &atomic_value_map() {
|
|
return atomic_value_map_;
|
|
}
|
|
|
|
protected:
|
|
void serialize_default_label(std::string &str) {
|
|
str.append(name_);
|
|
if (labels_name_.empty()) {
|
|
str.append(" ");
|
|
}
|
|
|
|
if (type_ == MetricType::Counter) {
|
|
str.append(std::to_string((int64_t)default_lable_value_));
|
|
}
|
|
else {
|
|
str.append(std::to_string(default_lable_value_));
|
|
}
|
|
|
|
str.append("\n");
|
|
}
|
|
|
|
template <typename T>
|
|
void serialize_map(T &value_map, std::string &str) {
|
|
for (auto &[labels_value, value] : value_map) {
|
|
if (value == 0) {
|
|
continue;
|
|
}
|
|
str.append(name_);
|
|
str.append("{");
|
|
build_string(str, labels_name_, labels_value);
|
|
str.append("} ");
|
|
|
|
if (type_ == MetricType::Counter) {
|
|
str.append(std::to_string((int64_t)value));
|
|
}
|
|
else {
|
|
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();
|
|
}
|
|
|
|
void validate(const std::vector<std::string> &labels_value, double value) {
|
|
if (value < 0) {
|
|
throw std::invalid_argument("the value is less than zero");
|
|
}
|
|
if (labels_value.empty() || labels_name_.size() != labels_value.size()) {
|
|
throw std::invalid_argument(
|
|
"the number of labels_value name and labels_value is not match");
|
|
}
|
|
}
|
|
|
|
template <bool is_atomic = false, typename T>
|
|
void set_value(T &label_val, double value, op_type_t type) {
|
|
switch (type) {
|
|
case op_type_t::INC: {
|
|
#ifdef __APPLE__
|
|
if constexpr (is_atomic) {
|
|
mac_os_atomic_fetch_add(&label_val, value);
|
|
}
|
|
else {
|
|
label_val += value;
|
|
}
|
|
#else
|
|
if constexpr (is_atomic) {
|
|
label_val.fetch_add(value, std::memory_order_relaxed);
|
|
}
|
|
else {
|
|
label_val += value;
|
|
}
|
|
#endif
|
|
} break;
|
|
case op_type_t::DEC:
|
|
#ifdef __APPLE__
|
|
if constexpr (is_atomic) {
|
|
mac_os_atomic_fetch_sub(&label_val, value);
|
|
}
|
|
else {
|
|
label_val -= value;
|
|
}
|
|
#else
|
|
if constexpr (is_atomic) {
|
|
label_val.fetch_sub(value, std::memory_order_relaxed);
|
|
}
|
|
else {
|
|
label_val -= value;
|
|
}
|
|
#endif
|
|
break;
|
|
case op_type_t::SET:
|
|
label_val = value;
|
|
break;
|
|
}
|
|
}
|
|
|
|
metric_hash_map<std::atomic<double>> atomic_value_map_;
|
|
std::atomic<double> default_lable_value_ = 0;
|
|
|
|
std::mutex mtx_;
|
|
metric_hash_map<double> value_map_;
|
|
};
|
|
} // namespace ylt::metric
|