Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[metric][feat]serialize zero #762

Merged
merged 1 commit into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 31 additions & 11 deletions include/ylt/metric/counter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ class basic_static_counter : public static_metric {
}

value_type update(value_type value) {
if (!has_change_) [[unlikely]] {
has_change_ = true;
}
return default_label_value_.update(value);
}

Expand All @@ -116,7 +119,7 @@ class basic_static_counter : public static_metric {

void serialize(std::string &str) override {
auto value = default_label_value_.value();
if (value == 0) {
if (value == 0 && !has_change_) {
return;
}

Expand Down Expand Up @@ -164,6 +167,7 @@ class basic_static_counter : public static_metric {

thread_local_value<value_type> default_label_value_;
uint32_t dupli_count_ = 2;
bool has_change_ = false;
};

template <typename Key>
Expand Down Expand Up @@ -231,6 +235,8 @@ class basic_dynamic_counter : public dynamic_metric {
if (value_map_.size() > ylt_label_capacity) {
return value_type{};
}
if (!has_change_) [[unlikely]]
has_change_ = true;
auto [it, r] = value_map_.try_emplace(
labels_value, thread_local_value<value_type>(dupli_count_));
lock.unlock();
Expand Down Expand Up @@ -266,12 +272,20 @@ class basic_dynamic_counter : public dynamic_metric {
dynamic_metric_hash_map<std::array<std::string, N>,
thread_local_value<value_type>>
value_map() {
[[maybe_unused]] bool has_change = false;
return value_map(has_change);
}

dynamic_metric_hash_map<std::array<std::string, N>,
thread_local_value<value_type>>
value_map(bool &has_change) {
dynamic_metric_hash_map<std::array<std::string, N>,
thread_local_value<value_type>>
map;
{
std::lock_guard lock(mtx_);
map = value_map_;
has_change = has_change_;
}

return map;
Expand Down Expand Up @@ -353,7 +367,8 @@ class basic_dynamic_counter : public dynamic_metric {
}

bool has_label_value(const std::string &value) override {
auto map = value_map();
[[maybe_unused]] bool has_change = false;
auto map = value_map(has_change);
for (auto &[label_value, _] : map) {
if (auto it = std::find(label_value.begin(), label_value.end(), value);
it != label_value.end()) {
Expand All @@ -365,7 +380,8 @@ class basic_dynamic_counter : public dynamic_metric {
}

bool has_label_value(const std::regex &regex) override {
auto map = value_map();
[[maybe_unused]] bool has_change = false;
auto map = value_map(has_change);
for (auto &[label_value, _] : map) {
if (auto it = std::find_if(label_value.begin(), label_value.end(),
[&](auto &val) {
Expand All @@ -390,13 +406,14 @@ class basic_dynamic_counter : public dynamic_metric {
}

void serialize(std::string &str) override {
auto map = value_map();
bool has_change = false;
auto map = value_map(has_change);
if (map.empty()) {
return;
}

std::string value_str;
serialize_map(map, value_str);
serialize_map(map, value_str, has_change);
if (!value_str.empty()) {
serialize_head(str);
str.append(value_str);
Expand All @@ -406,16 +423,18 @@ class basic_dynamic_counter : public dynamic_metric {
#ifdef CINATRA_ENABLE_METRIC_JSON
void serialize_to_json(std::string &str) override {
std::string s;
auto map = value_map();
bool has_change = false;
auto map = value_map(has_change);
json_counter_t counter{name_, help_, std::string(metric_name())};
to_json(counter, map, str);
to_json(counter, map, str, has_change);
}

template <typename T>
void to_json(json_counter_t &counter, T &map, std::string &str) {
void to_json(json_counter_t &counter, T &map, std::string &str,
bool has_change) {
for (auto &[k, v] : map) {
auto val = v.value();
if (val == 0) {
if (val == 0 && !has_change) {
continue;
}
json_counter_metric_t metric;
Expand All @@ -434,10 +453,10 @@ class basic_dynamic_counter : public dynamic_metric {

protected:
template <typename T>
void serialize_map(T &value_map, std::string &str) {
void serialize_map(T &value_map, std::string &str, bool has_change) {
for (auto &[labels_value, value] : value_map) {
auto val = value.value();
if (val == 0) {
if (val == 0 && !has_change) {
continue;
}
str.append(name_);
Expand Down Expand Up @@ -477,6 +496,7 @@ class basic_dynamic_counter : public dynamic_metric {
thread_local_value<value_type>>
value_map_;
size_t dupli_count_ = 2;
bool has_change_ = false;
};

using dynamic_counter_1t = basic_dynamic_counter<int64_t, 1>;
Expand Down
7 changes: 7 additions & 0 deletions include/ylt/metric/gauge.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class basic_static_gauge : public basic_static_counter<value_type> {
using basic_static_counter<value_type>::default_label_value_;
using metric_t::labels_value_;
using basic_static_counter<value_type>::dupli_count_;
using basic_static_counter<value_type>::has_change_;

public:
basic_static_gauge(std::string name, std::string help, size_t dupli_count = 2)
Expand All @@ -28,6 +29,9 @@ class basic_static_gauge : public basic_static_counter<value_type> {
}

void dec(value_type value = 1) {
if (!has_change_) [[unlikely]] {
has_change_ = true;
}
#ifdef __APPLE__
if constexpr (std::is_floating_point_v<value_type>) {
mac_os_atomic_fetch_sub(&default_label_value_.local_value(), value);
Expand All @@ -49,6 +53,7 @@ class basic_dynamic_gauge : public basic_dynamic_counter<value_type, N> {
using basic_dynamic_counter<value_type, N>::value_map_;
using basic_dynamic_counter<value_type, N>::mtx_;
using basic_dynamic_counter<value_type, N>::dupli_count_;
using basic_dynamic_counter<value_type, N>::has_change_;

public:
basic_dynamic_gauge(std::string name, std::string help,
Expand All @@ -70,6 +75,8 @@ class basic_dynamic_gauge : public basic_dynamic_counter<value_type, N> {
if (value_map_.size() > ylt_label_capacity) {
return;
}
if (!has_change_) [[unlikely]]
has_change_ = true;
auto [it, r] = value_map_.try_emplace(
labels_value, thread_local_value<value_type>(dupli_count_));
lock.unlock();
Expand Down
24 changes: 24 additions & 0 deletions include/ylt/metric/summary.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ class summary_t : public static_metric {
}

void observe(double value) {
if (!has_observe_) [[unlikely]] {
has_observe_ = true;
}
int64_t max_limit = (std::min)(ylt_label_capacity, (int64_t)1000000);
if (block_->sample_queue_.size_approx() >= max_limit) {
g_summary_failed_count++;
Expand Down Expand Up @@ -123,6 +126,10 @@ class summary_t : public static_metric {
co_return;
}

if (!has_observe_) {
co_return;
}

serialize_head(str);

double sum = 0;
Expand Down Expand Up @@ -156,6 +163,10 @@ class summary_t : public static_metric {
co_return;
}

if (!has_observe_) {
co_return;
}

json_summary_t summary{name_, help_, std::string(metric_name())};
double sum = 0;
uint64_t count = 0;
Expand Down Expand Up @@ -233,6 +244,7 @@ class summary_t : public static_metric {
static inline std::shared_ptr<coro_io::io_context_pool> excutor_ =
coro_io::create_io_context_pool(1);
std::atomic<bool> is_coro_started_ = false;
bool has_observe_ = false;
};

template <uint8_t N>
Expand Down Expand Up @@ -284,6 +296,9 @@ class basic_dynamic_summary : public dynamic_metric {
}

void observe(std::array<std::string, N> labels_value, double value) {
if (!has_observe_) [[unlikely]] {
has_observe_ = true;
}
int64_t max_limit = (std::min)(ylt_label_capacity, (int64_t)1000000);
if (labels_block_->sample_queue_.size_approx() >= max_limit) {
g_summary_failed_count++;
Expand Down Expand Up @@ -401,6 +416,10 @@ class basic_dynamic_summary : public dynamic_metric {
co_return;
}

if (!has_observe_) {
co_return;
}

serialize_head(str);

auto sum_map = co_await coro_io::post(
Expand Down Expand Up @@ -444,6 +463,10 @@ class basic_dynamic_summary : public dynamic_metric {
co_return;
}

if (!has_observe_) {
co_return;
}

auto sum_map = co_await coro_io::post(
[this] {
return labels_block_->sum_and_count_;
Expand Down Expand Up @@ -479,6 +502,7 @@ class basic_dynamic_summary : public dynamic_metric {
std::chrono::milliseconds max_age_;
uint16_t age_buckets_;
std::atomic<bool> is_coro_started_ = false;
bool has_observe_ = false;
};

using dynamic_summary_1 = basic_dynamic_summary<1>;
Expand Down
87 changes: 87 additions & 0 deletions src/metric/tests/test_metric.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#define DOCTEST_CONFIG_IMPLEMENT
#include <chrono>
#include <random>
using namespace std::chrono_literals;

#include "doctest.h"
#include "ylt/metric.hpp"
Expand All @@ -11,6 +13,91 @@ struct metrc_tag {};

struct test_tag {};

TEST_CASE("serialize zero") {
counter_t c("test", "");
gauge_t g("test1", "");
std::string str;
c.serialize(str);
CHECK(str.empty());
g.serialize(str);
CHECK(str.empty());
c.inc();
c.serialize(str);
CHECK(!str.empty());
str.clear();
g.inc();
g.serialize(str);
CHECK(!str.empty());
c.update(0);
c.serialize(str);
CHECK(!str.empty());
str.clear();
g.dec();
g.serialize(str);
CHECK(!str.empty());
str.clear();

dynamic_counter_1t c1("test", "", {"url"});
c1.serialize(str);
CHECK(str.empty());
dynamic_gauge_1t g1("test", "", {"url"});
g1.serialize(str);
CHECK(str.empty());
c1.inc({"/test"});
c1.serialize(str);
CHECK(!str.empty());
str.clear();
g1.inc({"/test"});
g1.serialize(str);
CHECK(!str.empty());
str.clear();

c1.update({"/test"}, 0);
c1.serialize(str);
CHECK(!str.empty());
str.clear();

g1.dec({"/test"});
g1.serialize(str);
CHECK(!str.empty());
str.clear();

c1.serialize_to_json(str);
CHECK(!str.empty());
str.clear();
g1.serialize_to_json(str);
CHECK(!str.empty());
str.clear();

histogram_t h("test", "help", {5.23, 10.54, 20.0, 50.0, 100.0});
h.serialize(str);
CHECK(str.empty());
h.serialize_to_json(str);
CHECK(str.empty());
h.observe(23);
h.serialize(str);
CHECK(!str.empty());
str.clear();

std::map<std::string, std::string> customMap = {};
auto summary = std::make_shared<summary_t>(
"test", "help",
summary_t::Quantiles{
{0.5, 0.05}, {0.9, 0.01}, {0.95, 0.005}, {0.99, 0.001}},
customMap);
async_simple::coro::syncAwait(summary->serialize_async(str));
CHECK(str.empty());
async_simple::coro::syncAwait(summary->serialize_to_json_async(str));
CHECK(str.empty());
summary->observe(0);
async_simple::coro::syncAwait(summary->serialize_async(str));
CHECK(!str.empty());
str.clear();
async_simple::coro::syncAwait(summary->serialize_to_json_async(str));
CHECK(!str.empty());
str.clear();
}

TEST_CASE("test metric manager") {
auto c = std::make_shared<counter_t>("test1", "");
auto g = std::make_shared<gauge_t>("test2", "");
Expand Down
Loading