Skip to content

Commit cce86db

Browse files
committed
Implement OpenMetrics spec
1 parent 2997dd9 commit cce86db

File tree

5 files changed

+332
-53
lines changed

5 files changed

+332
-53
lines changed

core/include/prometheus/client_metric.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3+
#include <chrono>
34
#include <cstdint>
45
#include <string>
56
#include <tuple>
@@ -77,7 +78,7 @@ struct PROMETHEUS_CPP_CORE_EXPORT ClientMetric {
7778

7879
// Timestamp
7980

80-
std::int64_t timestamp_ms = 0;
81+
std::chrono::milliseconds timestamp{std::chrono::seconds::zero()};
8182
};
8283

8384
} // namespace prometheus

core/include/prometheus/serializer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace prometheus {
1212
class PROMETHEUS_CPP_CORE_EXPORT Serializer {
1313
public:
1414
virtual ~Serializer() = default;
15-
virtual std::string Serialize(const std::vector<MetricFamily>&) const;
15+
virtual std::string Serialize(const std::vector<MetricFamily>& metrics) const;
1616
virtual void Serialize(std::ostream& out,
1717
const std::vector<MetricFamily>& metrics) const = 0;
1818
};

core/include/prometheus/text_serializer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,12 @@ namespace prometheus {
1212
class PROMETHEUS_CPP_CORE_EXPORT TextSerializer : public Serializer {
1313
public:
1414
using Serializer::Serialize;
15+
std::string Serialize(const std::vector<MetricFamily>& metrics,
16+
bool open_metrics) const;
1517
void Serialize(std::ostream& out,
1618
const std::vector<MetricFamily>& metrics) const override;
19+
void Serialize(std::ostream& out, const std::vector<MetricFamily>& metrics,
20+
bool open_metrics) const;
1721
};
1822

1923
} // namespace prometheus

core/src/text_serializer.cc

Lines changed: 122 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "prometheus/text_serializer.h"
22

3+
#include <chrono>
34
#include <cmath>
45
#include <limits>
56
#include <locale>
@@ -75,127 +76,207 @@ void WriteHead(std::ostream& out, const MetricFamily& family,
7576
}
7677

7778
// Write a line trailer: timestamp
78-
void WriteTail(std::ostream& out, const ClientMetric& metric) {
79-
if (metric.timestamp_ms != 0) {
80-
out << " " << metric.timestamp_ms;
79+
void WriteTail(std::ostream& out, const ClientMetric& metric,
80+
const bool open_metrics) {
81+
if (metric.timestamp != std::chrono::seconds::zero()) {
82+
out << " ";
83+
if (open_metrics) {
84+
using FloatSeconds = std::chrono::duration<double>;
85+
out << std::chrono::duration_cast<FloatSeconds>(metric.timestamp).count();
86+
} else {
87+
out << metric.timestamp.count();
88+
}
8189
}
8290
out << "\n";
8391
}
8492

8593
void SerializeCounter(std::ostream& out, const MetricFamily& family,
86-
const ClientMetric& metric) {
87-
WriteHead(out, family, metric);
94+
const ClientMetric& metric, const bool open_metrics) {
95+
WriteHead(out, family, metric, "_total");
8896
WriteValue(out, metric.counter.value);
89-
WriteTail(out, metric);
97+
WriteTail(out, metric, open_metrics);
9098
}
9199

92100
void SerializeGauge(std::ostream& out, const MetricFamily& family,
93-
const ClientMetric& metric) {
101+
const ClientMetric& metric, const bool open_metrics) {
94102
WriteHead(out, family, metric);
95103
WriteValue(out, metric.gauge.value);
96-
WriteTail(out, metric);
104+
WriteTail(out, metric, open_metrics);
97105
}
98106

99107
void SerializeSummary(std::ostream& out, const MetricFamily& family,
100-
const ClientMetric& metric) {
108+
const ClientMetric& metric, const bool open_metrics) {
101109
auto& sum = metric.summary;
102110
WriteHead(out, family, metric, "_count");
103111
out << sum.sample_count;
104-
WriteTail(out, metric);
112+
WriteTail(out, metric, open_metrics);
105113

106114
WriteHead(out, family, metric, "_sum");
107115
WriteValue(out, sum.sample_sum);
108-
WriteTail(out, metric);
116+
WriteTail(out, metric, open_metrics);
109117

110118
for (auto& q : sum.quantile) {
111119
WriteHead(out, family, metric, "", "quantile", q.quantile);
112120
WriteValue(out, q.value);
113-
WriteTail(out, metric);
121+
WriteTail(out, metric, open_metrics);
114122
}
115123
}
116124

117125
void SerializeUntyped(std::ostream& out, const MetricFamily& family,
118-
const ClientMetric& metric) {
126+
const ClientMetric& metric, const bool open_metrics) {
119127
WriteHead(out, family, metric);
120128
WriteValue(out, metric.untyped.value);
121-
WriteTail(out, metric);
129+
WriteTail(out, metric, open_metrics);
122130
}
123131

124132
void SerializeHistogram(std::ostream& out, const MetricFamily& family,
125-
const ClientMetric& metric) {
133+
const ClientMetric& metric, const bool open_metrics) {
126134
auto& hist = metric.histogram;
127135
WriteHead(out, family, metric, "_count");
128136
out << hist.sample_count;
129-
WriteTail(out, metric);
137+
WriteTail(out, metric, open_metrics);
130138

131139
WriteHead(out, family, metric, "_sum");
132140
WriteValue(out, hist.sample_sum);
133-
WriteTail(out, metric);
141+
WriteTail(out, metric, open_metrics);
134142

135143
double last = -std::numeric_limits<double>::infinity();
136144
for (auto& b : hist.bucket) {
137145
WriteHead(out, family, metric, "_bucket", "le", b.upper_bound);
138146
last = b.upper_bound;
139147
out << b.cumulative_count;
140-
WriteTail(out, metric);
148+
WriteTail(out, metric, open_metrics);
141149
}
142150

143151
if (last != std::numeric_limits<double>::infinity()) {
144152
WriteHead(out, family, metric, "_bucket", "le", "+Inf");
145153
out << hist.sample_count;
146-
WriteTail(out, metric);
154+
WriteTail(out, metric, open_metrics);
147155
}
148156
}
149157

150-
void SerializeFamily(std::ostream& out, const MetricFamily& family) {
151-
if (!family.help.empty()) {
152-
out << "# HELP " << family.name << " " << family.help << "\n";
153-
}
154-
switch (family.type) {
155-
case MetricType::Counter:
156-
out << "# TYPE " << family.name << " counter\n";
157-
for (auto& metric : family.metric) {
158-
SerializeCounter(out, family, metric);
158+
void SerializeFamily(std::ostream& out, const MetricFamily& family,
159+
const bool open_metrics) {
160+
const auto ends_with = [](const std::string& value,
161+
const std::string& ending) -> bool {
162+
if (ending.size() > value.size()) {
163+
return false;
164+
}
165+
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
166+
};
167+
168+
const auto remove_suffix = [&ends_with](std::string& value,
169+
const std::string& suffix) {
170+
if (ends_with(value, suffix)) {
171+
value.erase(value.end() - suffix.size(), value.end());
172+
}
173+
};
174+
175+
const auto compare_metrics = [](const ClientMetric& a,
176+
const ClientMetric& b) {
177+
return std::tie(a.label, a.timestamp) < std::tie(b.label, b.timestamp);
178+
};
179+
180+
MetricFamily sorted_family{family};
181+
std::stable_sort(sorted_family.metric.begin(), sorted_family.metric.end(),
182+
compare_metrics);
183+
184+
switch (sorted_family.type) {
185+
case MetricType::Counter: {
186+
remove_suffix(sorted_family.name, "_total");
187+
out << "# TYPE " << sorted_family.name;
188+
if (!open_metrics) {
189+
out << "_total";
190+
}
191+
out << " counter\n";
192+
if (!sorted_family.help.empty()) {
193+
out << "# HELP " << sorted_family.name;
194+
if (!open_metrics) {
195+
out << "_total";
196+
}
197+
out << " " << sorted_family.help << "\n";
198+
}
199+
for (const auto& metric : sorted_family.metric) {
200+
SerializeCounter(out, sorted_family, metric, open_metrics);
159201
}
160202
break;
203+
}
161204
case MetricType::Gauge:
162-
out << "# TYPE " << family.name << " gauge\n";
163-
for (auto& metric : family.metric) {
164-
SerializeGauge(out, family, metric);
205+
out << "# TYPE " << sorted_family.name << " gauge\n";
206+
if (!sorted_family.help.empty()) {
207+
out << "# HELP " << sorted_family.name << " " << sorted_family.help
208+
<< "\n";
209+
}
210+
for (auto& metric : sorted_family.metric) {
211+
SerializeGauge(out, sorted_family, metric, open_metrics);
165212
}
166213
break;
167214
case MetricType::Summary:
168-
out << "# TYPE " << family.name << " summary\n";
169-
for (auto& metric : family.metric) {
170-
SerializeSummary(out, family, metric);
215+
out << "# TYPE " << sorted_family.name << " summary\n";
216+
if (!sorted_family.help.empty()) {
217+
out << "# HELP " << sorted_family.name << " " << sorted_family.help
218+
<< "\n";
219+
}
220+
for (auto& metric : sorted_family.metric) {
221+
SerializeSummary(out, sorted_family, metric, open_metrics);
171222
}
172223
break;
173224
case MetricType::Untyped:
174-
out << "# TYPE " << family.name << " untyped\n";
175-
for (auto& metric : family.metric) {
176-
SerializeUntyped(out, family, metric);
225+
out << "# TYPE " << sorted_family.name;
226+
if (open_metrics) {
227+
out << " unknown\n";
228+
} else {
229+
out << " untyped\n";
230+
}
231+
if (!sorted_family.help.empty()) {
232+
out << "# HELP " << sorted_family.name << " " << sorted_family.help
233+
<< "\n";
234+
}
235+
for (auto& metric : sorted_family.metric) {
236+
SerializeUntyped(out, sorted_family, metric, open_metrics);
177237
}
178238
break;
179239
case MetricType::Histogram:
180-
out << "# TYPE " << family.name << " histogram\n";
181-
for (auto& metric : family.metric) {
182-
SerializeHistogram(out, family, metric);
240+
out << "# TYPE " << sorted_family.name << " histogram\n";
241+
if (!sorted_family.help.empty()) {
242+
out << "# HELP " << sorted_family.name << " " << sorted_family.help
243+
<< "\n";
244+
}
245+
for (auto& metric : sorted_family.metric) {
246+
SerializeHistogram(out, sorted_family, metric, open_metrics);
183247
}
184248
break;
185249
}
186250
}
187251
} // namespace
188252

253+
std::string TextSerializer::Serialize(const std::vector<MetricFamily>& metrics,
254+
const bool open_metrics) const {
255+
std::ostringstream ss;
256+
Serialize(ss, metrics, open_metrics);
257+
return ss.str();
258+
}
259+
189260
void TextSerializer::Serialize(std::ostream& out,
190261
const std::vector<MetricFamily>& metrics) const {
262+
Serialize(out, metrics, false);
263+
}
264+
265+
void TextSerializer::Serialize(std::ostream& out,
266+
const std::vector<MetricFamily>& metrics,
267+
const bool open_metrics) const {
191268
auto saved_locale = out.getloc();
192269
auto saved_precision = out.precision();
193270

194271
out.imbue(std::locale::classic());
195272
out.precision(std::numeric_limits<double>::max_digits10 - 1);
196273

197274
for (auto& family : metrics) {
198-
SerializeFamily(out, family);
275+
SerializeFamily(out, family, open_metrics);
276+
}
277+
278+
if (open_metrics) {
279+
out << "# EOF\n";
199280
}
200281

201282
out.imbue(saved_locale);

0 commit comments

Comments
 (0)