include/ylt/metric/metric.hpp (174 lines of code) (raw):
#pragma once
#include <algorithm>
#include <atomic>
#include <chrono>
#include <cstddef>
#include <optional>
#include <regex>
#include <span>
#include <string>
#include <thread>
#include <vector>
#include "async_simple/coro/Lazy.h"
#include "async_simple/coro/SyncAwait.h"
#include "cinatra/cinatra_log_wrapper.hpp"
#if __has_include("ylt/coro_io/coro_io.hpp")
#include "ylt/coro_io/coro_io.hpp"
#else
#include "cinatra/ylt/coro_io/coro_io.hpp"
#endif
#ifdef CINATRA_ENABLE_METRIC_JSON
namespace iguana {
template <typename T>
inline char* to_chars_float(T value, char* buffer) {
return buffer + snprintf(buffer, 65, "%g", value);
}
} // namespace iguana
#include <iguana/json_writer.hpp>
#endif
namespace ylt::metric {
enum class MetricType {
Counter,
Gauge,
Histogram,
Summary,
Nil,
};
struct metric_filter_options {
std::optional<std::regex> name_regex{};
std::optional<std::regex> label_regex{};
std::optional<std::regex> label_value_regex{};
bool is_white = true;
};
class metric_t {
public:
static inline std::atomic<int64_t> g_user_metric_count = 0;
metric_t() = default;
metric_t(MetricType type, std::string name, std::string help)
: type_(type),
name_(std::move(name)),
help_(std::move(help)),
metric_created_time_(std::chrono::system_clock::now()) {
g_user_metric_count.fetch_add(1, std::memory_order::relaxed);
}
template <size_t N>
metric_t(MetricType type, std::string name, std::string help,
std::array<std::string, N> labels_name)
: metric_t(type, std::move(name), std::move(help)) {
for (size_t i = 0; i < N; i++) {
labels_name_.push_back(std::move(labels_name[i]));
}
}
metric_t(MetricType type, std::string name, std::string help,
std::map<std::string, std::string> static_labels)
: metric_t(type, std::move(name), std::move(help)) {
static_labels_ = std::move(static_labels);
for (auto& [k, v] : static_labels_) {
labels_name_.push_back(k);
labels_value_.push_back(v);
}
}
virtual ~metric_t() {
g_user_metric_count.fetch_sub(1, std::memory_order::relaxed);
}
std::string_view name() { return name_; }
const std::string& str_name() { return name_; }
std::string_view help() { return help_; }
MetricType metric_type() { return type_; }
std::string_view metric_name() {
switch (type_) {
case MetricType::Counter:
return "counter";
case MetricType::Gauge:
return "gauge";
case MetricType::Histogram:
return "histogram";
case MetricType::Summary:
return "summary";
case MetricType::Nil:
default:
return "unknown";
}
}
const std::vector<std::string>& labels_name() { return labels_name_; }
const std::map<std::string, std::string>& get_static_labels() {
return static_labels_;
}
virtual bool has_label_value(const std::string& label_value) {
return std::find(labels_value_.begin(), labels_value_.end(), label_value) !=
labels_value_.end();
}
virtual void clean_expired_label() {}
virtual bool has_label_value(const std::vector<std::string>& label_value) {
return labels_value_ == label_value;
}
virtual bool has_label_value(const std::regex& regex) {
auto it = std::find_if(labels_value_.begin(), labels_value_.end(),
[&](auto& value) {
return std::regex_match(value, regex);
});
return it != labels_value_.end();
}
bool has_label_name(const std::vector<std::string>& label_name) {
return labels_name_ == label_name;
}
bool has_label_name(const std::string& label_name) {
return std::find(labels_name_.begin(), labels_name_.end(), label_name) !=
labels_name_.end();
}
virtual void remove_label_value(
const std::map<std::string, std::string>& labels) {}
virtual void serialize(std::string& str) {}
#ifdef CINATRA_ENABLE_METRIC_JSON
virtual void serialize_to_json(std::string& str) {}
#endif
template <typename T>
T* as() {
return dynamic_cast<T*>(this);
}
protected:
void set_metric_type(MetricType type) { type_ = type; }
void serialize_head(std::string& str) {
str.append("# HELP ").append(name_).append(" ").append(help_).append("\n");
str.append("# TYPE ")
.append(name_)
.append(" ")
.append(metric_name())
.append("\n");
}
void build_label_string(std::string& str,
const std::vector<std::string>& label_name,
const auto& label_value) {
for (size_t i = 0; i < label_name.size(); i++) {
str.append(label_name[i])
.append("=\"")
.append(label_value[i])
.append("\"")
.append(",");
}
str.pop_back();
}
MetricType type_ = MetricType::Nil;
std::string name_;
std::string help_;
std::map<std::string, std::string> static_labels_;
std::vector<std::string> labels_name_; // read only
std::vector<std::string> labels_value_; // read only
std::chrono::system_clock::time_point metric_created_time_{};
};
class static_metric : public metric_t {
using metric_t::metric_t;
};
inline std::chrono::seconds ylt_label_max_age{0};
inline std::chrono::seconds ylt_label_check_expire_duration{60};
inline std::atomic<int64_t> ylt_metric_capacity = 10000000;
inline void set_metric_capacity(int64_t max_count) {
ylt_metric_capacity = max_count;
}
inline void set_label_max_age(
std::chrono::seconds max_age,
std::chrono::seconds check_duration = std::chrono::seconds{60}) {
ylt_label_max_age = max_age;
ylt_label_check_expire_duration = check_duration;
}
} // namespace ylt::metric