Skip to content
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
2 changes: 2 additions & 0 deletions lib/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ cc_library(
srcs = ["counter.cc",
"gauge.cc",
"exposer.cc",
"histogram.cc",
"registry.cc"],
hdrs = ["counter.h",
"gauge.h",
"exposer.h",
"metric.h",
"collectable.h",
"family.h",
"histogram.h",
"registry.h"],
visibility = ["//visibility:public"],
deps = ["@protobuf//:protobuf",
Expand Down
17 changes: 15 additions & 2 deletions lib/exposer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,14 @@ MetricsHandler::MetricsHandler(
numScrapesFamily_(registry.add_counter(
"exposer_total_scrapes", "Number of times metrics were scraped",
{{"component", "exposer"}})),
numScrapes_(numScrapesFamily_->add({})) {}
numScrapes_(numScrapesFamily_->add({})),
requestLatenciesFamily_(registry.add_histogram(
"exposer_request_latencies",
"Latencies of serving scrape requests, in milliseconds",
{{"component", "exposer"}})),
requestLatencies_(requestLatenciesFamily_->add(
{}, Histogram::BucketBoundaries{1, 5, 10, 20, 40, 80, 160, 320, 640,
1280, 2560})) {}

static std::string serializeToDelimitedProtobuf(
const std::vector<io::prometheus::client::MetricFamily>& metrics) {
Expand Down Expand Up @@ -90,6 +97,8 @@ bool MetricsHandler::handleGet(CivetServer* server,
struct mg_connection* conn) {
using namespace io::prometheus::client;

auto startTimeOfRequest = std::chrono::steady_clock::now();

auto acceptedEncoding = getAcceptedEncoding(conn);
auto metrics = collectMetrics();

Expand Down Expand Up @@ -118,8 +127,12 @@ bool MetricsHandler::handleGet(CivetServer* server,
mg_printf(conn, "Content-Length: %lu\r\n\r\n", body.size());
mg_write(conn, body.data(), body.size());

bytesTransfered_->inc(body.size());
auto stopTimeOfRequest = std::chrono::steady_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
stopTimeOfRequest - startTimeOfRequest);
requestLatencies_->observe(duration.count());

bytesTransfered_->inc(body.size());
numScrapes_->inc();
return true;
}
Expand Down
3 changes: 3 additions & 0 deletions lib/exposer.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "CivetServer.h"
#include "registry.h"
#include "histogram.h"

namespace prometheus {

Expand All @@ -24,6 +25,8 @@ class MetricsHandler : public CivetHandler {
Counter* bytesTransfered_;
Family<Counter>* numScrapesFamily_;
Counter* numScrapes_;
Family<Histogram> *requestLatenciesFamily_;
Histogram* requestLatencies_;
};

class Exposer {
Expand Down
11 changes: 6 additions & 5 deletions lib/family.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
#include <string>
#include <unordered_map>

#include "counter.h"
#include "collectable.h"
#include "gauge.h"
#include "metric.h"

namespace prometheus {
Expand All @@ -19,7 +17,8 @@ class Family : public Collectable {
public:
Family(const std::string& name, const std::string& help,
const std::map<std::string, std::string>& constantLabels);
T* add(const std::map<std::string, std::string>& labels);
template <typename... Args>
T* add(const std::map<std::string, std::string>& labels, Args&&... args);
void remove(T* metric);

// Collectable
Expand All @@ -46,9 +45,11 @@ Family<T>::Family(const std::string& name, const std::string& help,
: name_(name), help_(help), constantLabels_(constantLabels) {}

template <typename T>
T* Family<T>::add(const std::map<std::string, std::string>& labels) {
template <typename... Args>
T* Family<T>::add(const std::map<std::string, std::string>& labels,
Args&&... args) {
auto hash = hash_labels(labels);
auto metric = new T();
auto metric = new T(std::forward<Args>(args)...);

metrics_.insert(std::make_pair(hash, std::unique_ptr<T>{metric}));
labels_.insert({hash, labels});
Expand Down
41 changes: 41 additions & 0 deletions lib/histogram.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include <algorithm>
#include <numeric>

#include "histogram.h"

namespace prometheus {

Histogram::Histogram(const BucketBoundaries& buckets)
: bucketBoundaries_{buckets}, bucketCounts_(buckets.size() + 1) {}

void Histogram::observe(double value) {
// TODO: determine bucket list size at which binary search would be faster
auto bucketIndex = std::max(
0L, std::find_if(bucketBoundaries_.begin(), bucketBoundaries_.end(),
[value](double boundary) { return boundary > value; }) -
bucketBoundaries_.begin());
sum_.inc(value);
bucketCounts_[bucketIndex].inc();
}

io::prometheus::client::Metric Histogram::collect() {
auto metric = io::prometheus::client::Metric{};
auto histogram = metric.mutable_histogram();

auto sampleCount = std::accumulate(
bucketCounts_.begin(), bucketCounts_.end(), double{0},
[](double sum, const Counter& counter) { return sum + counter.value(); });
histogram->set_sample_count(sampleCount);
histogram->set_sample_sum(sum_.value());

for (int i = 0; i < bucketCounts_.size(); i++) {
auto& count = bucketCounts_[i];
auto bucket = histogram->add_bucket();
bucket->set_cumulative_count(count.value());
bucket->set_upper_bound(i == bucketBoundaries_.size()
? std::numeric_limits<double>::infinity()
: bucketBoundaries_[i]);
}
return metric;
}
}
28 changes: 28 additions & 0 deletions lib/histogram.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#pragma once

#include <vector>

#include "cpp/metrics.pb.h"

#include "counter.h"

namespace prometheus {
class Histogram : public Metric {
public:
using BucketBoundaries = std::vector<double>;

static const io::prometheus::client::MetricType metric_type =
io::prometheus::client::HISTOGRAM;

Histogram(const BucketBoundaries& buckets);

void observe(double value);

io::prometheus::client::Metric collect();

private:
const BucketBoundaries bucketBoundaries_;
std::vector<Counter> bucketCounts_;
Counter sum_;
};
}
48 changes: 28 additions & 20 deletions lib/registry.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,44 @@ Registry::Registry(const std::map<std::string, std::string>& constLabels)
Family<Counter>* Registry::add_counter(
const std::string& name, const std::string& help,
const std::map<std::string, std::string>& labels) {
auto counterFamily = new Family<Counter>(name, help, labels);
collectables_.push_back(std::unique_ptr<Collectable>{counterFamily});
return counterFamily;
auto counterFamily = new Family<Counter>(name, help, labels);
collectables_.push_back(std::unique_ptr<Collectable>{counterFamily});
return counterFamily;
}

Family<Gauge>* Registry::add_gauge(
const std::string& name, const std::string& help,
const std::map<std::string, std::string>& labels) {
auto gaugeFamily = new Family<Gauge>(name, help, labels);
collectables_.push_back(std::unique_ptr<Collectable>{gaugeFamily});
return gaugeFamily;
auto gaugeFamily = new Family<Gauge>(name, help, labels);
collectables_.push_back(std::unique_ptr<Collectable>{gaugeFamily});
return gaugeFamily;
}

Family<Histogram>* Registry::add_histogram(
const std::string& name, const std::string& help,
const std::map<std::string, std::string>& labels) {
auto histogramFamily = new Family<Histogram>(name, help, labels);
collectables_.push_back(std::unique_ptr<Collectable>{histogramFamily});
return histogramFamily;
}

std::vector<io::prometheus::client::MetricFamily> Registry::collect() {
auto results = std::vector<io::prometheus::client::MetricFamily>{};
for(auto&& collectable : collectables_) {
auto metrics = collectable->collect();
results.insert(results.end(), metrics.begin(), metrics.end());
}
auto results = std::vector<io::prometheus::client::MetricFamily>{};
for (auto&& collectable : collectables_) {
auto metrics = collectable->collect();
results.insert(results.end(), metrics.begin(), metrics.end());
}

for (auto&& metricFamily : results) {
for (auto&& metric : *metricFamily.mutable_metric()) {
for (auto&& constLabelPair : constLabels_) {
auto label = metric.add_label();
label->set_name(constLabelPair.first);
label->set_value(constLabelPair.second);
}
}
for (auto&& metricFamily : results) {
for (auto&& metric : *metricFamily.mutable_metric()) {
for (auto&& constLabelPair : constLabels_) {
auto label = metric.add_label();
label->set_name(constLabelPair.first);
label->set_value(constLabelPair.second);
}
}
}

return results;
return results;
}
}
4 changes: 4 additions & 0 deletions lib/registry.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "collectable.h"
#include "cpp/metrics.pb.h"
#include "family.h"
#include "histogram.h"

namespace prometheus {

Expand All @@ -20,6 +21,9 @@ class Registry : public Collectable {
const std::map<std::string, std::string>& labels);
Family<Gauge>* add_gauge(const std::string& name, const std::string& help,
const std::map<std::string, std::string>& labels);
Family<Histogram>* add_histogram(
const std::string& name, const std::string& help,
const std::map<std::string, std::string>& labels);

// collectable
std::vector<io::prometheus::client::MetricFamily> collect() override;
Expand Down
2 changes: 1 addition & 1 deletion tests/BUILD
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cc_test(
name = "prometheus_test",
srcs = ["counter_test.cc", "gauge_test.cc", "mock_metric.h", "family_test.cc", "registry_test.cc"],
srcs = ["counter_test.cc", "gauge_test.cc", "mock_metric.h", "family_test.cc", "registry_test.cc", "histogram_test.cc"],
copts = ["-Iexternal/googletest/include"],
deps = ["@googletest//:main",
"//lib:prometheus-cpp"],
Expand Down
40 changes: 26 additions & 14 deletions tests/family_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "cpp/metrics.pb.h"
#include "lib/counter.h"
#include "lib/family.h"
#include "lib/histogram.h"

using namespace testing;
using namespace prometheus;
Expand Down Expand Up @@ -41,21 +42,32 @@ TEST_F(FamilyTest, labels) {
}

TEST_F(FamilyTest, counter_value) {
auto family = Family<Counter>{"total_requests", "Counts all requests", {}};
auto counter = family.add({});
counter->inc();
auto collected = family.collect();
ASSERT_GE(collected.size(), 1);
ASSERT_GE(collected[0].metric_size(), 1);
EXPECT_THAT(collected[0].metric(0).counter().value(), Eq(1));
auto family = Family<Counter>{"total_requests", "Counts all requests", {}};
auto counter = family.add({});
counter->inc();
auto collected = family.collect();
ASSERT_GE(collected.size(), 1);
ASSERT_GE(collected[0].metric_size(), 1);
EXPECT_THAT(collected[0].metric(0).counter().value(), Eq(1));
}

TEST_F(FamilyTest, remove) {
auto family = Family<Counter>{"total_requests", "Counts all requests", {}};
auto counter1 = family.add({{"name", "counter1"}});
family.add({{"name", "counter2"}});
family.remove(counter1);
auto collected = family.collect();
ASSERT_GE(collected.size(), 1);
EXPECT_EQ(collected[0].metric_size(), 1);
auto family = Family<Counter>{"total_requests", "Counts all requests", {}};
auto counter1 = family.add({{"name", "counter1"}});
family.add({{"name", "counter2"}});
family.remove(counter1);
auto collected = family.collect();
ASSERT_GE(collected.size(), 1);
EXPECT_EQ(collected[0].metric_size(), 1);
}

TEST_F(FamilyTest, histogram) {
auto family = Family<Histogram>{"request_latency", "Latency Histogram", {}};
auto histogram1 = family.add({{"name", "histogram1"}}, Histogram::BucketBoundaries{0,1,2});
histogram1->observe(0);
auto collected = family.collect();
ASSERT_EQ(collected.size(), 1);
ASSERT_GE(collected[0].metric_size(), 1);
ASSERT_TRUE(collected[0].metric(0).has_histogram());
EXPECT_THAT(collected[0].metric(0).histogram().sample_count(), Eq(1));
}
Loading