Skip to content

Commit

Permalink
serialize metric to json
Browse files Browse the repository at this point in the history
  • Loading branch information
qicosmos committed Jun 11, 2024
1 parent 314c475 commit 72c760d
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 8 deletions.
6 changes: 6 additions & 0 deletions cmake/develop.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ if(ENABLE_SANITIZER AND NOT MSVC)
endif()
endif()

option(ENABLE_METRIC_JSON "Enable serialize metric to json" OFF)
if(ENABLE_METRIC_JSON)
add_definitions(-DCINATRA_ENABLE_METRIC_JSON)
message(STATUS "Enable serialize metric to json")
endif()

SET(ENABLE_GZIP OFF)
SET(ENABLE_SSL OFF)
SET(ENABLE_CLIENT_SSL OFF)
Expand Down
56 changes: 52 additions & 4 deletions include/cinatra/ylt/metric/counter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,21 @@

namespace ylt {
enum class op_type_t { INC, DEC, SET };
struct counter_sample {
op_type_t op_type;
std::vector<std::string> labels_value;
double value;

#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:
Expand Down Expand Up @@ -97,6 +107,44 @@ class counter_t : public metric_t {
}
}

#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");
Expand Down
43 changes: 43 additions & 0 deletions include/cinatra/ylt/metric/histogram.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,22 @@
#include "metric.hpp"

namespace ylt {
#ifdef CINATRA_ENABLE_METRIC_JSON
struct json_histogram_metric_t {
std::map<double, int64_t> quantiles;
int64_t count;
double sum;
};
REFLECTION(json_histogram_metric_t, quantiles, count, sum);
struct json_histogram_t {
std::string name;
std::string help;
std::string type;
json_histogram_metric_t metric;
};
REFLECTION(json_histogram_t, name, help, type, metric);
#endif

class histogram_t : public metric_t {
public:
histogram_t(std::string name, std::string help, std::vector<double> buckets)
Expand Down Expand Up @@ -68,6 +84,33 @@ class histogram_t : public metric_t {
.append("\n");
}

#ifdef CINATRA_ENABLE_METRIC_JSON
void serialize_to_json(std::string& str) override {
json_histogram_t hist{name_, help_, std::string(metric_name())};

double count = 0;
auto bucket_counts = get_bucket_counts();
for (size_t i = 0; i < bucket_counts.size(); i++) {
auto counter = bucket_counts[i];

count += counter->value();

if (i == bucket_boundaries_.size()) {
hist.metric.quantiles.emplace(std::numeric_limits<int>::max(),
(int64_t)count);
}
else {
hist.metric.quantiles.emplace(bucket_boundaries_[i],
(int64_t)counter->value());
}
}
hist.metric.count = (int64_t)count;
hist.metric.sum = sum_->value();

iguana::to_json(hist, str);
}
#endif

private:
template <class ForwardIterator>
bool is_strict_sorted(ForwardIterator first, ForwardIterator last) {
Expand Down
22 changes: 22 additions & 0 deletions include/cinatra/ylt/metric/metric.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@
#include "async_simple/coro/Lazy.h"
#include "cinatra/cinatra_log_wrapper.hpp"

#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 {
enum class MetricType {
Counter,
Expand Down Expand Up @@ -57,11 +69,21 @@ class metric_t {

virtual void serialize(std::string& str) {}

virtual void serialize_to_json(std::string& str) {}

// only for summary
virtual async_simple::coro::Lazy<void> serialize_async(std::string& out) {
co_return;
}

#ifdef CINATRA_ENABLE_METRIC_JSON
// only for summary
virtual async_simple::coro::Lazy<void> serialize_to_json_async(
std::string& out) {
co_return;
}
#endif

bool is_atomic() const { return use_atomic_; }

template <typename T>
Expand Down
38 changes: 38 additions & 0 deletions include/cinatra/ylt/metric/summary.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@
#include "ylt/util/concurrentqueue.h"

namespace ylt {
#ifdef CINATRA_ENABLE_METRIC_JSON
struct json_summary_metric_t {
std::map<double, double> quantiles;
int64_t count;
double sum;
};
REFLECTION(json_summary_metric_t, quantiles, count, sum);
struct json_summary_t {
std::string name;
std::string help;
std::string type;
json_summary_metric_t metric;
};
REFLECTION(json_summary_t, name, help, type, metric);
#endif

class summary_t : public metric_t {
public:
using Quantiles = std::vector<CKMSQuantiles::Quantile>;
Expand Down Expand Up @@ -103,6 +119,28 @@ class summary_t : public metric_t {
.append("\n");
}

#ifdef CINATRA_ENABLE_METRIC_JSON
async_simple::coro::Lazy<void> serialize_to_json_async(
std::string &str) override {
if (quantiles_.empty()) {
co_return;
}

json_summary_t summary{name_, help_, std::string(metric_name())};
double sum = 0;
uint64_t count = 0;
auto rates = co_await get_rates(sum, count);

for (size_t i = 0; i < quantiles_.size(); i++) {
summary.metric.quantiles.emplace(quantiles_[i].quantile, rates[i]);
}

summary.metric.sum = sum;
summary.metric.count = count;

iguana::to_json(summary, str);
}
#endif
private:
async_simple::coro::Lazy<void> start_timer(std::shared_ptr<block_t> block) {
double sample;
Expand Down
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ endif()
## manual import
include_directories(${cinatra_SOURCE_DIR}/include)
include_directories(${cinatra_SOURCE_DIR}/include/cinatra)
include_directories(${cinatra_SOURCE_DIR}/iguana)

add_executable(test_corofile
test_corofile.cpp
Expand Down
36 changes: 33 additions & 3 deletions tests/test_metric.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,13 @@ TEST_CASE("test gauge") {
CHECK(g.value() == 2);
g.inc(0);

#ifdef CINATRA_ENABLE_METRIC_JSON
std::string str_json;
g.serialize_to_json(str_json);
std::cout << str_json << "\n";
CHECK(str_json.find("\"value\":2") != std::string::npos);
#endif

g.dec();
CHECK(g.value() == 1);
g.dec();
Expand All @@ -160,6 +167,15 @@ TEST_CASE("test gauge") {
values = g.value_map();
CHECK(values[{"GET", "200", "/"}] == 3);

g.inc({"POST", "200", "/"}, 4);

#ifdef CINATRA_ENABLE_METRIC_JSON
std::string str_json;
g.serialize_to_json(str_json);
std::cout << str_json << "\n";
CHECK(str_json.find("\"code\":\"200\"") != std::string::npos);
#endif

std::string str;
g.serialize(str);
std::cout << str;
Expand All @@ -179,7 +195,7 @@ TEST_CASE("test gauge") {
}

TEST_CASE("test histogram") {
histogram_t h("test", "help", {5.0, 10.0, 20.0, 50.0, 100.0});
histogram_t h("test", "help", {5.23, 10.54, 20.0, 50.0, 100.0});
h.observe(23);
auto counts = h.get_bucket_counts();
CHECK(counts[3]->value() == 1);
Expand All @@ -193,11 +209,18 @@ TEST_CASE("test histogram") {
CHECK(counts[0]->value() == 1);
std::string str;
h.serialize(str);
std::cout << str;
std::cout << str << "\n";
CHECK(str.find("test_count") != std::string::npos);
CHECK(str.find("test_sum") != std::string::npos);
CHECK(str.find("test_bucket{le=\"5") != std::string::npos);
CHECK(str.find("test_bucket{le=\"5.23") != std::string::npos);
CHECK(str.find("test_bucket{le=\"+Inf\"}") != std::string::npos);

#ifdef CINATRA_ENABLE_METRIC_JSON
std::string str_json;
h.serialize_to_json(str_json);
std::cout << str_json << "\n";
CHECK(str_json.find("\"5.23\":1") != std::string::npos);
#endif
}

TEST_CASE("test summary") {
Expand All @@ -221,6 +244,13 @@ TEST_CASE("test summary") {
CHECK(str.find("test_summary_count") != std::string::npos);
CHECK(str.find("test_summary_sum") != std::string::npos);
CHECK(str.find("test_summary{quantile=\"") != std::string::npos);

#ifdef CINATRA_ENABLE_METRIC_JSON
std::string str_json;
async_simple::coro::syncAwait(summary.serialize_to_json_async(str_json));
std::cout << str_json << "\n";
CHECK(str_json.find("\"0.9\":") != std::string::npos);
#endif
}

TEST_CASE("test register metric") {
Expand Down

0 comments on commit 72c760d

Please sign in to comment.