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

init #570

Merged
merged 20 commits into from
May 10, 2024
89 changes: 89 additions & 0 deletions example/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
#include <vector>

#include "../include/cinatra.hpp"
#include "cinatra/ylt/metric/gauge.hpp"
#include "cinatra/ylt/metric/histogram.hpp"
#include "cinatra/ylt/metric/summary.hpp"

using namespace cinatra;
using namespace std::chrono_literals;
Expand Down Expand Up @@ -382,7 +385,93 @@ async_simple::coro::Lazy<void> basic_usage() {
#endif
}

void use_metric() {
auto c = std::make_shared<counter_t>("request_count", "request count",
std::vector{"method", "url"});
auto failed = std::make_shared<gauge_t>("not_found_request_count",
"not found request count",
std::vector{"method", "code", "url"});
auto total =
std::make_shared<counter_t>("total_request_count", "total request count");

auto h =
std::make_shared<histogram_t>(std::string("test"), std::string("help"),
std::vector{5.0, 10.0, 20.0, 50.0, 100.0});

auto summary = std::make_shared<summary_t>(
std::string("test_summary"), std::string("summary help"),
summary_t::Quantiles{
{0.5, 0.05}, {0.9, 0.01}, {0.95, 0.005}, {0.99, 0.001}});

metric_t::regiter_metric(c);
metric_t::regiter_metric(total);
metric_t::regiter_metric(failed);
metric_t::regiter_metric(h);
metric_t::regiter_metric(summary);

std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> distr(1, 100);

std::thread thd([&] {
while (true) {
c->inc({"GET", "/test"});
total->inc();
h->observe(distr(gen));
summary->observe(distr(gen));
std::this_thread::sleep_for(1s);
}
});
thd.detach();

coro_http_server server(1, 9001);
server.set_default_handler(
[&](coro_http_request &req,
coro_http_response &resp) -> async_simple::coro::Lazy<void> {
failed->inc({std::string(req.get_method()),
std::to_string((int)status_type::not_found),
std::string(req.get_url())});
total->inc();
resp.set_status_and_content(status_type::not_found, "not found");
co_return;
});

server.set_http_handler<GET>(
"/get", [&](coro_http_request &req, coro_http_response &resp) {
resp.set_status_and_content(status_type::ok, "ok");
c->inc({std::string(req.get_method()), std::string(req.get_url())});
total->inc();
});

server.set_http_handler<GET>(
"/test", [&](coro_http_request &req, coro_http_response &resp) {
resp.set_status_and_content(status_type::ok, "ok");
c->inc({std::string(req.get_method()), std::string(req.get_url())});
total->inc();
});

server.set_http_handler<GET, POST>(
"/", [&](coro_http_request &req, coro_http_response &resp) {
resp.set_status_and_content(status_type::ok, "ok");
total->inc();
});

server.set_http_handler<GET, POST>(
"/metrics", [](coro_http_request &req, coro_http_response &resp) {
std::string str;
auto metrics = metric_t::collect();
for (auto &m : metrics) {
m->serialize(str);
}
std::cout << str;
resp.need_date_head(false);
resp.set_status_and_content(status_type::ok, std::move(str));
});
server.sync_start();
}

int main() {
// use_metric();
async_simple::coro::syncAwait(basic_usage());
async_simple::coro::syncAwait(use_aspects());
async_simple::coro::syncAwait(static_file_server());
Expand Down
131 changes: 131 additions & 0 deletions include/cinatra/ylt/metric/counter.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#pragma once
#include "metric.hpp"

namespace cinatra {
class counter_t : public metric_t {
public:
counter_t() = default;
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)) {}

counter_t(const char *name, const char *help,
std::vector<const char *> labels_name = {})
: counter_t(
std::string(name), std::string(help),
std::vector<std::string>(labels_name.begin(), labels_name.end())) {}

void inc() {
std::lock_guard guard(mtx_);
set_value(value_map_[{}], 1, op_type_t::INC);
}

void inc(const std::vector<std::string> &labels_value, double value = 1) {
if (value == 0) {
return;
}
validate(labels_value, value);
std::lock_guard guard(mtx_);
set_value(value_map_[labels_value], value, op_type_t::INC);
}

void update(const std::vector<std::string> &labels_value, double value) {
if (labels_name_.size() != labels_value.size()) {
throw std::invalid_argument(
"the number of labels_value name and labels_value is not match");
}
std::lock_guard guard(mtx_);
set_value(value_map_[labels_value], value, op_type_t::SET);
}

void reset() {
std::lock_guard guard(mtx_);
for (auto &pair : value_map_) {
pair.second = {};
}
}

std::map<std::vector<std::string>, sample_t,
std::less<std::vector<std::string>>>
values(bool need_lock = true) override {
if (need_lock) {
return value_map_;
}
std::lock_guard guard(mtx_);
return value_map_;
}

void serialize(std::string &str) override {
if (value_map_.empty()) {
return;
}
str.append("# HELP ").append(name_).append(" ").append(help_).append("\n");
str.append("# TYPE ")
.append(name_)
.append(" ")
.append(metric_name())
.append("\n");
for (auto &[labels_value, sample] : value_map_) {
str.append(name_);
if (labels_name_.empty()) {
str.append(" ");
}
else {
str.append("{");
build_string(str, labels_name_, labels_value);
str.append("} ");
}

str.append(std::to_string((int64_t)sample.value));
if (enable_timestamp_) {
str.append(" ");
str.append(std::to_string(sample.timestamp));
}
str.append("\n");
}
}

protected:
enum class op_type_t { INC, DEC, SET };
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_name_.size() != labels_value.size()) {
throw std::invalid_argument(
"the number of labels_value name and labels_value is not match");
}
}

void set_value(sample_t &sample, double value, op_type_t type) {
sample.timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();
switch (type) {
case op_type_t::INC:
sample.value += value;
break;
case op_type_t::DEC:
sample.value -= value;
break;
case op_type_t::SET:
sample.value = value;
break;
}
}

std::mutex mtx_;
std::map<std::vector<std::string>, sample_t,
std::less<std::vector<std::string>>>
value_map_;
};
} // namespace cinatra
Loading
Loading