Skip to content

Commit

Permalink
Removed 'Key' concept, added 'MetricGroup' concept
Browse files Browse the repository at this point in the history
  • Loading branch information
DarkWanderer committed Sep 23, 2023
1 parent cc29590 commit f8d0ad3
Show file tree
Hide file tree
Showing 8 changed files with 308 additions and 216 deletions.
20 changes: 15 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,30 @@ cout << c1.value(); // 1

### Registering an existing metric

`Registry`

```cpp
auto registry = createRegistry();
auto gauge = registry->getGauge("my_gauge", {{"some", "label"}});
gauge = 10.0;
```
Registry also allows adding previously existing metrics:
```cpp
Gauge gauge;
gauge = 5;
auto registry = createRegistry();
registry->add({ "my_gauge", {{"some", "label"}} }, gauge);
cout << registry->getGauge({ "my_gauge", {{"some", "label"}} }).value(); // 5
registry->add("my_gauge", {{"some", "label"}}, gauge);
cout << registry->getGauge("my_gauge", {{"some", "label"}}).value(); // 5
```

### Prometheus
### Serialization

```cpp
auto registry = createRegistry();
auto gauge = registry->getGauge({ "my_gauge", {{"some", "label"}} });
auto result = serializePrometheus(*registry);
auto gauge = registry->getGauge("my_gauge", {{"some", "label"}});
auto p = serializePrometheus(*registry);
auto j = serializeJson(*registry);
auto s = serializeStatsd(*registry);
```
### Timers
Expand Down
54 changes: 14 additions & 40 deletions include/metrics/registry.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,72 +7,46 @@

namespace Metrics
{
struct Key
{
Key() = default;
Key(Key&&) = default;
Key(const Key&) = default;
~Key() = default;

const std::string name;
const Labels labels;

bool operator==(const Metrics::Key& other) const { return name == other.name && labels == other.labels; }

bool operator<(const Metrics::Key& other) const
{
auto c1 = name.compare(other.name);
if (c1 != 0)
return c1 < 0;
return labels < other.labels;
}
class IMetricGroup {
public:
virtual std::string description() const = 0;
virtual TypeCode type() const = 0;
virtual std::vector<std::pair<Labels, std::shared_ptr<IMetric>>> metrics() const = 0;
};

class IRegistry
{
public:
/// <summary>
/// Get untyped IMetric or nullptr
/// </summary>
/// <param name="key">metric key</param>
/// <returns>metric</returns>
virtual std::shared_ptr<IMetric> get(const Key& key) const = 0;
virtual std::vector<std::string> metricNames() const = 0;
virtual const IMetricGroup& getGroup(const std::string& name) const = 0;

/// <summary>
/// Get or create a gauge with provided key
/// </summary>
/// <param name="key">metric key</param>
/// <returns>new or existing metric object</returns>
virtual Gauge getGauge(const Key& key) = 0;

template <typename ...Params> Gauge getGauge(Params&&... params) {
return getGauge(Key(std::forward<Params>(params)...));
}
virtual Gauge getGauge(const std::string name, const Labels& labels = {}) = 0;

/// <summary>
/// Get or create a counter with provided key
/// </summary>
/// <param name="key">metric key</param>
/// <returns>new or existing metric object</returns>
virtual Counter getCounter(const Key& key) = 0;

template <typename ...Params> Counter getCounter(Params&&... params) {
return getCounter(Key(std::forward<Params>(params)...));
}
virtual Counter getCounter(const std::string name, const Labels& labels = {}) = 0;

/// <summary>
/// Get or create a summary with provided key
/// </summary>
/// <param name="key">metric key</param>
/// <returns>new or existing metric object</returns>
virtual Summary getSummary(const Key& key, const std::vector<double>& quantiles = { 0.50, 0.90, 0.99, 0.999 }, double error = 0.01) = 0;
virtual Summary getSummary(const std::string name, const Labels& labels = {}, const std::vector<double>& quantiles = { 0.50, 0.90, 0.99, 0.999 }, double error = 0.01) = 0;

/// <summary>
/// Get or create a histogram with provided key
/// </summary>
/// <param name="key">metric key</param>
/// <returns>new or existing metric object</returns>
virtual Histogram getHistogram(const Key& key, const std::vector<double>& bounds = { 100., 200., 300., 400., 500. }) = 0;
virtual Histogram getHistogram(const std::string name, const Labels& labels = {}, const std::vector<double>& bounds = { 100., 200., 300., 400., 500. }) = 0;

/// <summary>
/// Register an existing metric wrapper object with the registry
Expand All @@ -81,7 +55,7 @@ namespace Metrics
/// <param name="key"></param>
/// <param name="metric"></param>
/// <returns></returns>
template<class TMetric> bool add(const Key& key, TMetric metric) { return add(key, metric.raw()); }
template<class TMetric> bool add(TMetric metric, const std::string name, const Labels& labels = {}) { return add(key, metric.raw()); }

/// <summary>
/// Register an existing low-level metric object with the registry
Expand All @@ -90,9 +64,9 @@ namespace Metrics
/// <param name="key"></param>
/// <param name="metric"></param>
/// <returns></returns>
virtual bool add(const Key& key, std::shared_ptr<IMetric> metric) = 0;
virtual bool add(std::shared_ptr<IMetric> metric, const std::string name, const Labels& labels = {}) = 0;

virtual std::vector<Key> keys() const = 0;
virtual void setDescription(std::string name, std::string description) = 0;

virtual ~IRegistry() = 0;
};
Expand Down
8 changes: 4 additions & 4 deletions samples/cmake/sample.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ int main()
{
cout << "This is a sample app demonstrating usage of simple metrics" << endl << endl;
auto birdwatching_metrics = createRegistry();
birdwatching_metrics->getCounter( { "birds", {{ "kind", "pigeon" }} } )++;
birdwatching_metrics->getCounter( { "birds", {{ "kind", "sparrow" }} } )+=10;
birdwatching_metrics->getGauge( { "tiredness" } ) += 1.5;
birdwatching_metrics->getCounter( "birds", {{ "kind", "pigeon" }} )++;
birdwatching_metrics->getCounter( "birds", {{ "kind", "sparrow" }} )+=10;
birdwatching_metrics->getGauge( "tiredness" ) += 1.5;

cout << "The library supports outputting metrics in Prometheus format:" << endl << serializePrometheus(*birdwatching_metrics) << endl;
cout << "And in JSON format:" << endl << serializeJsonl(*birdwatching_metrics) << endl;
return 0;
}
}
41 changes: 20 additions & 21 deletions src/json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@
#include <sstream>

using json = nlohmann::json;

using namespace std;

namespace Metrics {
json serialize(const Key& key, const std::shared_ptr<IMetric> metric)
json serialize(const string& name, const Labels& labels, const std::shared_ptr<IMetric> metric)
{
json serialized;
serialized["name"] = key.name;
json labels;
for (auto kv = key.labels.cbegin(); kv != key.labels.cend(); kv++)
serialized["name"] = name;
json jlabels;
for (auto kv = labels.cbegin(); kv != labels.cend(); kv++)
{
labels[kv->first] = kv->second;
jlabels[kv->first] = kv->second;
}
if (!labels.empty())
serialized["labels"] = labels;
if (!jlabels.empty())
serialized["labels"] = jlabels;

switch (metric->type())
{
Expand Down Expand Up @@ -76,14 +76,14 @@ namespace Metrics {
METRICS_EXPORT std::string serializeJson(const IRegistry& registry)
{
auto result = json::array();
auto keys = registry.keys();

for (const auto& key : keys)
auto names = registry.metricNames();
for (const auto& name : names)
{
auto metric = registry.get(key);
if (!metric)
continue;
result.emplace_back(serialize(key, metric));
auto& group = registry.getGroup(name);
auto metrics = group.metrics();
for (const auto& metric : metrics)
result.emplace_back(serialize(name, metric.first, metric.second));
}

std::stringstream out;
Expand All @@ -94,14 +94,13 @@ namespace Metrics {
METRICS_EXPORT std::string serializeJsonl(const IRegistry& registry)
{
std::stringstream out;
auto keys = registry.keys();

for (const auto& key : keys)
auto names = registry.metricNames();
for (const auto& name : names)
{
auto metric = registry.get(key);
if (!metric)
continue;
out << serialize(key, metric) << std::endl;
auto& group = registry.getGroup(name);
auto metrics = group.metrics();
for (const auto& metric : metrics)
out << serialize(name, metric.first, metric.second) << std::endl;
}

return out.str();
Expand Down
118 changes: 66 additions & 52 deletions src/prometheus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,31 @@
#include <iostream>
#include <sstream>

using std::ostream;
using std::stringstream;
using std::endl;
using namespace std;

namespace Metrics
{
const char* typeString(TypeCode type) {
switch (type)
{
case Metrics::TypeCode::Gauge:
return "gauge";
break;
case Metrics::TypeCode::Counter:
return "counter";
break;
case Metrics::TypeCode::Summary:
return "summary";
break;
case Metrics::TypeCode::Histogram:
return "histogram";
break;
default:
break;
}
return "unknown";
}

ostream& operator<<(ostream& os, const Labels& labels)
{
bool opened = false;
Expand All @@ -22,78 +41,73 @@ namespace Metrics
return os;
}

ostream& operator<<(ostream& os, const Key& key)
void serialize(ostream& os, const string& name, const Labels& labels, const ISummary& summary)
{
os << key.name;
os << key.labels;
return os;
}

void write(ostream& os, const Key& key, const ISummary& histogram)
{
for (auto value : histogram.values())
for (auto& value : summary.values())
{
os << key.name << '{';
for (auto kv = key.labels.cbegin(); kv != key.labels.cend(); kv++)
os << name << '{';
for (auto kv = labels.cbegin(); kv != labels.cend(); kv++)
{
os << kv->first << "=\"" << kv->second << '"' << ',';
}
os << "quantile=\"" << value.first << "\"} " << value.second << endl;
}
os << key.name << "_sum" << key.labels << ' ' << histogram.sum() << endl;
os << key.name << "_count" << key.labels << ' ' << histogram.count() << endl;
os << name << "_sum" << labels << ' ' << summary.sum() << endl;
os << name << "_count" << labels << ' ' << summary.count() << endl;
}

void write(ostream& os, const Key& key, const IHistogram& histogram)
void serialize(ostream& os, const string& name, const Labels& labels, const IHistogram& histogram)
{
for (auto value : histogram.values())
for (auto& value : histogram.values())
{
os << key.name << '{';
for (auto kv = key.labels.cbegin(); kv != key.labels.cend(); kv++)
os << name << '{';
for (auto kv = labels.cbegin(); kv != labels.cend(); kv++)
{
os << kv->first << "=\"" << kv->second << '"' << ',';
}
os << "le=\"" << value.first << "\"} " << value.second << endl;
}
os << key.name << "_sum" << key.labels << ' ' << histogram.sum() << endl;
os << key.name << "_count" << key.labels << ' ' << histogram.count() << endl;
os << name << "_sum" << labels << ' ' << histogram.sum() << endl;
os << name << "_count" << labels << ' ' << histogram.count() << endl;
}

ostream& operator<<(ostream& os, const IRegistry& registry)
void serialize(ostream& os, const string& name, const Labels& labels, const std::shared_ptr<IMetric> metric)
{
auto keys = registry.keys();
for (const auto& key : keys)
if (!metric)
return ;
switch (metric->type())
{
auto metric = registry.get(key);
if (!metric)
continue;
switch (metric->type())
{
case TypeCode::Counter:
// os << "# TYPE " << key.name << " counter" << endl;
os << key << ' ' << std::static_pointer_cast<ICounterValue>(metric)->value() << endl;
break;
case TypeCode::Gauge:
// os << "# TYPE " << key.name << " gauge" << endl;
os << key << ' ' << std::static_pointer_cast<IGaugeValue>(metric)->value() << endl;
break;
case TypeCode::Summary:
os << "# TYPE " << key.name << " summary" << endl;
write(os, key, *std::static_pointer_cast<ISummary>(metric));
break;
case TypeCode::Histogram:
os << "# TYPE " << key.name << " histogram" << endl;
write(os, key, *std::static_pointer_cast<IHistogram>(metric));
break;
}
case TypeCode::Counter:
// os << "# TYPE " << name << " counter" << endl;
os << name << labels << ' ' << static_pointer_cast<ICounterValue>(metric)->value() << endl;
break;
case TypeCode::Gauge:
// os << "# TYPE " << name << " gauge" << endl;
os << name << labels << ' ' << static_pointer_cast<IGaugeValue>(metric)->value() << endl;
break;
case TypeCode::Summary:
serialize(os, name, labels, *static_pointer_cast<ISummary>(metric));
break;
case TypeCode::Histogram:
serialize(os, name, labels, *static_pointer_cast<IHistogram>(metric));
break;
}
return os;
}

std::string serializePrometheus(const IRegistry& registry)
string serializePrometheus(const IRegistry& registry)
{
stringstream ss;
ss << registry;
return ss.str();
stringstream out;
auto names = registry.metricNames();
for (const auto& name : names)
{
auto& group = registry.getGroup(name);
if (!group.description().empty())
out << "# HELP " << name << " " << group.description() << endl;
out << "# TYPE " << name << " " << typeString(group.type()) << endl;
auto metrics = group.metrics();
for (const auto& metric : metrics)
serialize(out, name, metric.first, metric.second);
}
return out.str();
}
}
Loading

0 comments on commit f8d0ad3

Please sign in to comment.