Skip to content

Commit

Permalink
serialize zero (#762)
Browse files Browse the repository at this point in the history
  • Loading branch information
qicosmos committed Aug 21, 2024
1 parent e21db0c commit 6017187
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 11 deletions.
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

0 comments on commit 6017187

Please sign in to comment.