Skip to content

Commit

Permalink
Add Http2Client metrics
Browse files Browse the repository at this point in the history
Name must also be provided to the constructor to ease building
the metrics name.

Also, send task includes sending and reception timestamps.

Implement: #18
  • Loading branch information
Eduardo Ramos Testillano (eramedu) committed May 30, 2023
1 parent c7aed6c commit 5877ae5
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 10 deletions.
53 changes: 51 additions & 2 deletions include/ert/http2comm/Http2Client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ SOFTWARE.

#include <ert/http2comm/Http2Connection.hpp>

#include <ert/metrics/Metrics.hpp>

namespace nghttp2
{
Expand Down Expand Up @@ -91,6 +92,8 @@ class Http2Client
std::string body;
int statusCode; // -1 = connection error
nghttp2::asio_http2::header_map headers;
std::chrono::microseconds sendingUs;
std::chrono::microseconds receptionUs;
};

private:
Expand All @@ -105,20 +108,66 @@ class Http2Client
std::future<Http2Client::response> done;
std::string data; //buffer to store a possible temporary data
bool timed_out{};
std::chrono::microseconds sendingUs;
std::chrono::microseconds receptionUs;
};

//class members
std::string name_{}; // used for metrics:
// Metric names should be in lowercase and separated by underscores (_).
// Metric names should start with a letter or an underscore (_).
// Metric names should be descriptive and meaningful for their purpose.
// Metric names should not be too long or too short.

// metrics:
ert::metrics::Metrics *metrics_{};

ert::metrics::counter_t *observed_requests_post_counter_{};
ert::metrics::counter_t *observed_requests_get_counter_{};
ert::metrics::counter_t *observed_requests_put_counter_{};
ert::metrics::counter_t *observed_requests_delete_counter_{};
ert::metrics::counter_t *observed_requests_head_counter_{};
ert::metrics::counter_t *observed_requests_other_counter_{};
ert::metrics::counter_t *observed_requests_error_post_counter_{};
ert::metrics::counter_t *observed_requests_error_get_counter_{};
ert::metrics::counter_t *observed_requests_error_put_counter_{};
ert::metrics::counter_t *observed_requests_error_delete_counter_{};
ert::metrics::counter_t *observed_requests_error_head_counter_{};
ert::metrics::counter_t *observed_requests_error_other_counter_{};

ert::metrics::gauge_t *responses_delay_seconds_gauge_{};
ert::metrics::gauge_t *messages_size_bytes_rx_gauge_{};
ert::metrics::gauge_t *messages_size_bytes_tx_gauge_{};

ert::metrics::histogram_t *responses_delay_seconds_histogram_{};
ert::metrics::histogram_t *messages_size_bytes_rx_histogram_{};
ert::metrics::histogram_t *messages_size_bytes_tx_histogram_{};

std::atomic<std::uint64_t> reception_id_{};
std::atomic<std::size_t> maximum_request_body_size_{};

public:
/**
* Class constructor given host, port and secure connection indicator
*
* @param name Client name (lower case, as it is used to name prometheus metrics).
* @param host Endpoint host
* @param port Endpoint port
* @param secure Secure connection. False by default
*/
Http2Client(const std::string& host, const std::string& port, bool secure = false);
Http2Client(const std::string &name, const std::string& host, const std::string& port, bool secure = false);
virtual ~Http2Client() {};

/**
* Enable metrics
*
* @param metrics Optional metrics object to compute counters and histograms
* @param responseDelaySecondsHistogramBucketBoundaries Optional bucket boundaries for response delay seconds histogram
* @param messageSizeBytesHistogramBucketBoundaries Optional bucket boundaries for message size bytes histogram
*/
void enableMetrics(ert::metrics::Metrics *metrics,
const ert::metrics::bucket_boundaries_t &responseDelaySecondsHistogramBucketBoundaries = {},
const ert::metrics::bucket_boundaries_t &messageSizeBytesHistogramBucketBoundaries = {});

/**
* Send request to the server
*
Expand Down
8 changes: 6 additions & 2 deletions include/ert/http2comm/Http2Server.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,11 @@ namespace http2comm
class Http2Server
{
std::string server_key_password_{};
std::string name_{};
std::string name_{}; // used for metrics:
// Metric names should be in lowercase and separated by underscores (_).
// Metric names should start with a letter or an underscore (_).
// Metric names should be descriptive and meaningful for their purpose.
// Metric names should not be too long or too short.
std::string api_name_{};
std::string api_version_{};
boost::asio::io_service *timers_io_service_;
Expand Down Expand Up @@ -106,7 +110,7 @@ class Http2Server
/**
* Class constructor
*
* @param name Server name.
* @param name Server name (lower case, as it is used to name prometheus metrics).
* @param workerThreads number of worker threads.
* @param maxWorkerThreads number of maximum worker threads which internal processing could grow to. Defaults to '0' which means that maximum equals to provided worker threads.
* @param timerIoService Optional io service to manage response delays
Expand Down
121 changes: 115 additions & 6 deletions src/Http2Client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,9 @@ namespace ert
{
namespace http2comm
{
Http2Client::Http2Client(const std::string& host, const std::string& port, bool secure)
: host_(host),
Http2Client::Http2Client(const std::string& name, const std::string& host, const std::string& port, bool secure)
: name_(name),
host_(host),
port_(port),
secure_(secure),
connection_(std::make_unique<Http2Connection>(host, port, secure))
Expand All @@ -64,6 +65,43 @@ Http2Client::Http2Client(const std::string& host, const std::string& port, bool
}
}

void Http2Client::enableMetrics(ert::metrics::Metrics *metrics,
const ert::metrics::bucket_boundaries_t &responseDelaySecondsHistogramBucketBoundaries,
const ert::metrics::bucket_boundaries_t &messageSizeBytesHistogramBucketBoundaries) {

metrics_ = metrics;

if (metrics_) {
ert::metrics::counter_family_ref_t cf = metrics->addCounterFamily(name_ + std::string("_observed_requests_total"), std::string("Http2 total requests observed in ") + name_);
observed_requests_post_counter_ = &(cf.Add({{"method", "POST"}}));
observed_requests_get_counter_ = &(cf.Add({{"method", "GET"}}));
observed_requests_put_counter_ = &(cf.Add({{"method", "PUT"}}));
observed_requests_delete_counter_ = &(cf.Add({{"method", "DELETE"}}));
observed_requests_head_counter_ = &(cf.Add({{"method", "HEAD"}}));
observed_requests_other_counter_ = &(cf.Add({{"method", "other"}}));
observed_requests_error_post_counter_ = &(cf.Add({{"success", "false"}, {"method", "POST"}}));
observed_requests_error_get_counter_ = &(cf.Add({{"success", "false"}, {"method", "GET"}}));
observed_requests_error_put_counter_ = &(cf.Add({{"success", "false"}, {"method", "PUT"}}));
observed_requests_error_delete_counter_ = &(cf.Add({{"success", "false"}, {"method", "DELETE"}}));
observed_requests_error_head_counter_ = &(cf.Add({{"success", "false"}, {"method", "HEAD"}}));
observed_requests_error_other_counter_ = &(cf.Add({{"success", "false"}, {"method", "other"}}));

ert::metrics::gauge_family_ref_t gf1 = metrics->addGaugeFamily(name_ + std::string("_responses_delay_seconds_gauge"), std::string("Http2 message responses delay gauge (seconds) in ") + name_);
responses_delay_seconds_gauge_ = &(gf1.Add({}));

ert::metrics::gauge_family_ref_t gf2 = metrics->addGaugeFamily(name_ + std::string("_messages_size_bytes_gauge"), std::string("Http2 message sizes gauge (bytes) in ") + name_);
messages_size_bytes_rx_gauge_ = &(gf2.Add({{"direction", "rx"}}));
messages_size_bytes_tx_gauge_ = &(gf2.Add({{"direction", "tx"}}));

ert::metrics::histogram_family_ref_t hf1 = metrics->addHistogramFamily(name_ + std::string("_responses_delay_seconds_histogram"), std::string("Http2 message responses delay (seconds) in ") + name_);
responses_delay_seconds_histogram_ = &(hf1.Add({}, responseDelaySecondsHistogramBucketBoundaries));

ert::metrics::histogram_family_ref_t hf2 = metrics->addHistogramFamily(name_ + std::string("_messages_size_bytes_histogram"), std::string("Http2 message sizes (bytes) in ") + name_);
messages_size_bytes_rx_histogram_ = &(hf2.Add({{"direction", "rx"}}, messageSizeBytesHistogramBucketBoundaries));
messages_size_bytes_tx_histogram_ = &(hf2.Add({{"direction", "tx"}}, messageSizeBytesHistogramBucketBoundaries));
}
}

void Http2Client::reconnect()
{
if (!mutex_.try_lock())
Expand Down Expand Up @@ -100,9 +138,60 @@ Http2Client::response Http2Client::send(
);

reconnect();

// metrics
if (metrics_) {
// counters
if (method == "POST") {
observed_requests_error_post_counter_->Increment();
}
else if (method == "GET") {
observed_requests_error_get_counter_->Increment();
}
else if (method == "PUT") {
observed_requests_error_put_counter_->Increment();
}
else if (method == "DELETE") {
observed_requests_error_delete_counter_->Increment();
}
else if (method == "HEAD") {
observed_requests_error_head_counter_->Increment();
}
else {
observed_requests_error_other_counter_->Increment();
}
}

return Http2Client::response{"", -1};
}

// metrics
if (metrics_) {
// counters
if (method == "POST") {
observed_requests_post_counter_->Increment();
}
else if (method == "GET") {
observed_requests_get_counter_->Increment();
}
else if (method == "PUT") {
observed_requests_put_counter_->Increment();
}
else if (method == "DELETE") {
observed_requests_delete_counter_->Increment();
}
else if (method == "HEAD") {
observed_requests_head_counter_->Increment();
}
else {
observed_requests_other_counter_->Increment();
}

std::size_t requestBodySize = body.size();
messages_size_bytes_rx_gauge_->Set(requestBodySize);
messages_size_bytes_rx_histogram_->Observe(requestBodySize);
}

auto url = getUri(path);

LOGINFORMATIONAL(
Expand All @@ -120,7 +209,7 @@ Http2Client::response Http2Client::send(
const auto& session = connection_->getSession();
auto task = std::make_shared<Http2Client::task>();

session.io_service().post([&, task, headers]
session.io_service().post([&, task, headers, this]
{
boost::system::error_code ec;

Expand All @@ -131,6 +220,7 @@ Http2Client::response Http2Client::send(
// headers.emplace("content-length", clValue);

//perform submit
task->sendingUs = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch());
auto req = submit(session, headers, ec);
if (!req) {
ert::tracing::Logger::error("Request submit error, closing connection ...", ERT_FILE_LOCATION);
Expand All @@ -140,17 +230,30 @@ Http2Client::response Http2Client::send(
}

req->on_response(
[task](const nghttp2::asio_http2::client::response & res)
[task, this](const nghttp2::asio_http2::client::response & res)
{
if (task->timed_out) {
LOGINFORMATIONAL(
ert::tracing::Logger::informational("Answer received for discarded transaction due to timeout. Ignoring ...", ERT_FILE_LOCATION);
);
return;
}
// metrics
if (metrics_) {
// histograms
task->receptionUs = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch());
double durationUs = (task->receptionUs - task->sendingUs).count();
double durationSeconds = durationUs/1000000.0;
LOGDEBUG(
std::string msg = ert::tracing::Logger::asString("Context duration: %d us", durationUs);
ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
);
responses_delay_seconds_gauge_->Set(durationSeconds);
responses_delay_seconds_histogram_->Observe(durationSeconds);
}

res.on_data(
[task, &res](const uint8_t* data, std::size_t len)
[task, &res, this](const uint8_t* data, std::size_t len)
{
if (len > 0)
{
Expand All @@ -160,9 +263,15 @@ Http2Client::response Http2Client::send(
else
{
//setting the value on 'response' (promise) will unlock 'done' (future)
task->response.set_value(Http2Client::response {task->data, res.status_code(), res.header()});
task->response.set_value(Http2Client::response {task->data, res.status_code(), res.header(), task->sendingUs, task->receptionUs});
LOGDEBUG(ert::tracing::Logger::debug(ert::tracing::Logger::asString(
"Request has been answered with status code: %d; data: %s; headers: %s", res.status_code(), task->data.c_str(), headersAsString(res.header()).c_str()), ERT_FILE_LOCATION));
// metrics
if (metrics_) {
std::size_t responseBodySize = task->data.size();
messages_size_bytes_tx_gauge_->Set(responseBodySize);
messages_size_bytes_tx_histogram_->Observe(responseBodySize);
}
}
});
});
Expand Down

0 comments on commit 5877ae5

Please sign in to comment.