Skip to content

Commit

Permalink
Add http exposer with examples
Browse files Browse the repository at this point in the history
  • Loading branch information
jupp0r committed Oct 13, 2016
1 parent b3cf25c commit 4784ca2
Show file tree
Hide file tree
Showing 13 changed files with 249 additions and 56 deletions.
6 changes: 6 additions & 0 deletions .bazelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
build:asan --strip=never
build:asan --copt -fsanitize=address
build:asan --copt -O0
build:asan --copt -fno-omit-frame-pointer
build:asan --copt -g
build:asan --linkopt -fsanitize=address
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ offer the possibility for metrics to collected by Prometheus, but
other push/pull collections can be added as plugins.

## Project Status
Pre-alpha
Alpha

* parts of the library are instrumented by itself (bytes scraped, number of scrapes)
* there is a working [example](tests/sample_server.cc) that prometheus successfully scrapes
* gauge and counter metrics are implemented, histograms and summaries aren't
* thread safety is missing in registries and metric families (you'd have to lock access yourself for now)

## License
MIT
7 changes: 5 additions & 2 deletions lib/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@ cc_library(
name = "prometheus-cpp",
srcs = ["counter.cc",
"gauge.cc",
"exposer.cc"],
"exposer.cc",
"registry.cc"],
hdrs = ["counter.h",
"gauge.h",
"exposer.h",
"metric.h",
"collectable.h",
"family.h"],
"family.h",
"registry.h"],
visibility = ["//visibility:public"],
deps = ["@protobuf//:protobuf",
"@prometheus_client_model//:prometheus_client_model",
"@civetweb//:civetweb",
],
linkstatic = 1,
)
96 changes: 58 additions & 38 deletions lib/exposer.cc
Original file line number Diff line number Diff line change
@@ -1,61 +1,81 @@
#include <chrono>
#include <sstream>
#include <string>
#include <thread>
#include <sstream>

#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>

#include "exposer.h"

#include "cpp/metrics.pb.h"

namespace prometheus {
MetricsHandler::MetricsHandler(
const std::vector<std::weak_ptr<Collectable>>& collectables,
Registry& registry)
: collectables_(collectables),
bytesTransferedFamily_(registry.add_counter(
"exposer_bytes_transfered", "bytesTransferred to metrics services",
{{"component", "exposer"}})),
bytesTransfered_(bytesTransferedFamily_->add({})),
numScrapesFamily_(registry.add_counter(
"exposer_total_scrapes", "Number of times metrics were scraped",
{{"component", "exposer"}})),
numScrapes_(numScrapesFamily_->add({})) {}

bool MetricsHandler::handleGet(CivetServer* server,
struct mg_connection* conn) {
using namespace io::prometheus::client;

std::ostringstream ss;
for (auto&& wcollectable : collectables_) {
auto collectable = wcollectable.lock();
if (!collectable) {
continue;
}

class MetricsHandler : public CivetHandler {
public:
bool handleGet(CivetServer* server, struct mg_connection* conn) {
using namespace io::prometheus::client;

MetricFamily message;
message.set_name("Foo");
message.set_help("Foo help");
message.set_type(MetricType::COUNTER);
auto metric1 = message.add_metric();
auto counter = metric1->mutable_counter();
counter->set_value(1337.0);

std::ostringstream ss;
{
for (auto&& metricFamily : collectable->collect()) {
{
google::protobuf::io::OstreamOutputStream rawOutput{&ss};
google::protobuf::io::CodedOutputStream output(&rawOutput);

// Write the size.
const int size = message.ByteSize();
const int size = metricFamily.ByteSize();
output.WriteVarint32(size);
}
}

auto buf = ss.str();
message.AppendToString(&buf);
mg_printf(conn,
"HTTP/1.1 200 OK\r\n"
"Content-Type: "
"application/vnd.google.protobuf; "
"proto=io.prometheus.client.MetricFamily; "
"encoding=delimited\r\n"
"Content-Length: ");
mg_printf(conn, "%lu\r\n\r\n", buf.size());
mg_write(conn, buf.data(), buf.size());
return true;
auto buffer = std::string{};
metricFamily.SerializeToString(&buffer);
ss << buffer;
}
}
};

auto body = ss.str();
mg_printf(conn,
"HTTP/1.1 200 OK\r\n"
"Content-Type: "
"application/vnd.google.protobuf; "
"proto=io.prometheus.client.MetricFamily; "
"encoding=delimited\r\n"
"Content-Length: ");
mg_printf(conn, "%lu\r\n\r\n", body.size());
mg_write(conn, body.data(), body.size());
bytesTransfered_->inc(body.size());
numScrapes_->inc();
return true;
}

Exposer::Exposer(std::uint16_t port)
: server_({"listening_ports", std::to_string(port)}) {
MetricsHandler handler;
server_.addHandler("/metrics", &handler);
std::this_thread::sleep_for(std::chrono::seconds(60000));
: server_({"listening_ports", std::to_string(port)}),
exposerRegistry_(
std::make_shared<Registry>(std::map<std::string, std::string>{})),
metricsHandler_(collectables_, *exposerRegistry_) {
registerCollectable(exposerRegistry_);
server_.addHandler("/metrics", &metricsHandler_);
}

void Exposer::run() {}
void Exposer::registerCollectable(
const std::weak_ptr<Collectable>& collectable) {
collectables_.push_back(collectable);
}
}
30 changes: 25 additions & 5 deletions lib/exposer.h
Original file line number Diff line number Diff line change
@@ -1,17 +1,37 @@
#pragma once

#include <atomic>
#include <cstdint>
#include <memory>

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

namespace prometheus {

class Exposer {
class MetricsHandler : public CivetHandler {
public:
Exposer(std::uint16_t port);
void run();
private:
CivetServer server_;
MetricsHandler(const std::vector<std::weak_ptr<Collectable>>& collectables,
Registry& registry);

bool handleGet(CivetServer* server, struct mg_connection* conn);

const std::vector<std::weak_ptr<Collectable>>& collectables_;
Family<Counter>* bytesTransferedFamily_;
Counter* bytesTransfered_;
Family<Counter>* numScrapesFamily_;
Counter* numScrapes_;
};

class Exposer {
public:
Exposer(std::uint16_t port);
void registerCollectable(const std::weak_ptr<Collectable>& collectable);

private:
CivetServer server_;
std::vector<std::weak_ptr<Collectable>> collectables_;
std::shared_ptr<Registry> exposerRegistry_;
MetricsHandler metricsHandler_;
};
}
2 changes: 2 additions & 0 deletions lib/family.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
#include <string>
#include <unordered_map>

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

namespace prometheus {
Expand Down
13 changes: 10 additions & 3 deletions lib/gauge.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ void Gauge::dec(double value) {
void Gauge::set(double value) { value_.store(value); }

void Gauge::change(double value) {
auto current = value_.load();
while (!value_.compare_exchange_weak(current, current + value))
;
auto current = value_.load();
while (!value_.compare_exchange_weak(current, current + value))
;
}

void Gauge::set_to_current_time() {
Expand All @@ -38,4 +38,11 @@ void Gauge::set_to_current_time() {
}

double Gauge::value() const { return value_; }

io::prometheus::client::Metric Gauge::collect() {
io::prometheus::client::Metric metric;
auto gauge = metric.mutable_gauge();
gauge->set_value(value());
return metric;
}
}
10 changes: 9 additions & 1 deletion lib/gauge.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@

#include <atomic>

#include "cpp/metrics.pb.h"
#include "metric.h"

namespace prometheus {

class Gauge {
class Gauge : public Metric {
public:
static const io::prometheus::client::MetricType metric_type =
io::prometheus::client::GAUGE;

Gauge();
Gauge(double);
void inc();
Expand All @@ -16,6 +22,8 @@ class Gauge {
void set_to_current_time();
double value() const;

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

private:
void change(double);
std::atomic<double> value_;
Expand Down
43 changes: 43 additions & 0 deletions lib/registry.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#include "registry.h"

namespace prometheus {

Registry::Registry(const std::map<std::string, std::string>& constLabels)
: constLabels_(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;
}

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;
}

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());
}

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;
}
}
31 changes: 31 additions & 0 deletions lib/registry.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#pragma once

#include <map>

#include "collectable.h"
#include "cpp/metrics.pb.h"
#include "family.h"

namespace prometheus {

class Counter;
class Gauge;

class Registry : public Collectable {
public:
Registry() = default;
Registry(const std::map<std::string, std::string>& constLabels);
Family<Counter>* add_counter(
const std::string& name, const std::string& help,
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);

// collectable
std::vector<io::prometheus::client::MetricFamily> collect() override;

private:
std::vector<std::unique_ptr<Collectable>> collectables_;
std::map<std::string, std::string> constLabels_;
};
}
3 changes: 2 additions & 1 deletion tests/BUILD
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
cc_test(
name = "prometheus_test",
srcs = ["counter_test.cc", "gauge_test.cc", "mock_metric.h", "family_test.cc"],
srcs = ["counter_test.cc", "gauge_test.cc", "mock_metric.h", "family_test.cc", "registry_test.cc"],
copts = ["-Iexternal/googletest/include"],
deps = ["@googletest//:main",
"//lib:prometheus-cpp"],
linkstatic = 1,
)

cc_binary(
Expand Down
32 changes: 32 additions & 0 deletions tests/registry_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#include <vector>

#include <gmock/gmock.h>

#include "lib/registry.h"
#include "lib/collectable.h"

using namespace testing;
using namespace prometheus;

class MockCollectable : public Collectable {
public:
MOCK_METHOD0(collect, std::vector<io::prometheus::client::MetricFamily>());
};

class RegistryTest : public Test {};

TEST_F(RegistryTest, collectsSingleMetricFamily) {
auto registry = Registry{};
auto counterFamily = registry.add_counter("test", "a test", {});
counterFamily->add({{"name", "counter1"}});
counterFamily->add({{"name", "counter2"}});
auto collected = registry.collect();
ASSERT_EQ(collected.size(), 1);
EXPECT_EQ(collected[0].name(), "test");
EXPECT_EQ(collected[0].help(), "a test");
ASSERT_EQ(collected[0].metric_size(), 2);
ASSERT_EQ(collected[0].metric(0).label_size(), 1);
EXPECT_EQ(collected[0].metric(0).label(0).name(), "name");
ASSERT_EQ(collected[0].metric(1).label_size(), 1);
EXPECT_EQ(collected[0].metric(1).label(0).name(), "name");
}
Loading

0 comments on commit 4784ca2

Please sign in to comment.