|
1 | 1 | #include "prometheus/text_serializer.h"
|
2 | 2 |
|
| 3 | +#include <chrono> |
3 | 4 | #include <cmath>
|
4 | 5 | #include <limits>
|
5 | 6 | #include <locale>
|
@@ -75,127 +76,207 @@ void WriteHead(std::ostream& out, const MetricFamily& family,
|
75 | 76 | }
|
76 | 77 |
|
77 | 78 | // 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 | + } |
81 | 89 | }
|
82 | 90 | out << "\n";
|
83 | 91 | }
|
84 | 92 |
|
85 | 93 | 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"); |
88 | 96 | WriteValue(out, metric.counter.value);
|
89 |
| - WriteTail(out, metric); |
| 97 | + WriteTail(out, metric, open_metrics); |
90 | 98 | }
|
91 | 99 |
|
92 | 100 | void SerializeGauge(std::ostream& out, const MetricFamily& family,
|
93 |
| - const ClientMetric& metric) { |
| 101 | + const ClientMetric& metric, const bool open_metrics) { |
94 | 102 | WriteHead(out, family, metric);
|
95 | 103 | WriteValue(out, metric.gauge.value);
|
96 |
| - WriteTail(out, metric); |
| 104 | + WriteTail(out, metric, open_metrics); |
97 | 105 | }
|
98 | 106 |
|
99 | 107 | void SerializeSummary(std::ostream& out, const MetricFamily& family,
|
100 |
| - const ClientMetric& metric) { |
| 108 | + const ClientMetric& metric, const bool open_metrics) { |
101 | 109 | auto& sum = metric.summary;
|
102 | 110 | WriteHead(out, family, metric, "_count");
|
103 | 111 | out << sum.sample_count;
|
104 |
| - WriteTail(out, metric); |
| 112 | + WriteTail(out, metric, open_metrics); |
105 | 113 |
|
106 | 114 | WriteHead(out, family, metric, "_sum");
|
107 | 115 | WriteValue(out, sum.sample_sum);
|
108 |
| - WriteTail(out, metric); |
| 116 | + WriteTail(out, metric, open_metrics); |
109 | 117 |
|
110 | 118 | for (auto& q : sum.quantile) {
|
111 | 119 | WriteHead(out, family, metric, "", "quantile", q.quantile);
|
112 | 120 | WriteValue(out, q.value);
|
113 |
| - WriteTail(out, metric); |
| 121 | + WriteTail(out, metric, open_metrics); |
114 | 122 | }
|
115 | 123 | }
|
116 | 124 |
|
117 | 125 | void SerializeUntyped(std::ostream& out, const MetricFamily& family,
|
118 |
| - const ClientMetric& metric) { |
| 126 | + const ClientMetric& metric, const bool open_metrics) { |
119 | 127 | WriteHead(out, family, metric);
|
120 | 128 | WriteValue(out, metric.untyped.value);
|
121 |
| - WriteTail(out, metric); |
| 129 | + WriteTail(out, metric, open_metrics); |
122 | 130 | }
|
123 | 131 |
|
124 | 132 | void SerializeHistogram(std::ostream& out, const MetricFamily& family,
|
125 |
| - const ClientMetric& metric) { |
| 133 | + const ClientMetric& metric, const bool open_metrics) { |
126 | 134 | auto& hist = metric.histogram;
|
127 | 135 | WriteHead(out, family, metric, "_count");
|
128 | 136 | out << hist.sample_count;
|
129 |
| - WriteTail(out, metric); |
| 137 | + WriteTail(out, metric, open_metrics); |
130 | 138 |
|
131 | 139 | WriteHead(out, family, metric, "_sum");
|
132 | 140 | WriteValue(out, hist.sample_sum);
|
133 |
| - WriteTail(out, metric); |
| 141 | + WriteTail(out, metric, open_metrics); |
134 | 142 |
|
135 | 143 | double last = -std::numeric_limits<double>::infinity();
|
136 | 144 | for (auto& b : hist.bucket) {
|
137 | 145 | WriteHead(out, family, metric, "_bucket", "le", b.upper_bound);
|
138 | 146 | last = b.upper_bound;
|
139 | 147 | out << b.cumulative_count;
|
140 |
| - WriteTail(out, metric); |
| 148 | + WriteTail(out, metric, open_metrics); |
141 | 149 | }
|
142 | 150 |
|
143 | 151 | if (last != std::numeric_limits<double>::infinity()) {
|
144 | 152 | WriteHead(out, family, metric, "_bucket", "le", "+Inf");
|
145 | 153 | out << hist.sample_count;
|
146 |
| - WriteTail(out, metric); |
| 154 | + WriteTail(out, metric, open_metrics); |
147 | 155 | }
|
148 | 156 | }
|
149 | 157 |
|
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); |
159 | 201 | }
|
160 | 202 | break;
|
| 203 | + } |
161 | 204 | 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); |
165 | 212 | }
|
166 | 213 | break;
|
167 | 214 | 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); |
171 | 222 | }
|
172 | 223 | break;
|
173 | 224 | 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); |
177 | 237 | }
|
178 | 238 | break;
|
179 | 239 | 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); |
183 | 247 | }
|
184 | 248 | break;
|
185 | 249 | }
|
186 | 250 | }
|
187 | 251 | } // namespace
|
188 | 252 |
|
| 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 | + |
189 | 260 | void TextSerializer::Serialize(std::ostream& out,
|
190 | 261 | 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 { |
191 | 268 | auto saved_locale = out.getloc();
|
192 | 269 | auto saved_precision = out.precision();
|
193 | 270 |
|
194 | 271 | out.imbue(std::locale::classic());
|
195 | 272 | out.precision(std::numeric_limits<double>::max_digits10 - 1);
|
196 | 273 |
|
197 | 274 | for (auto& family : metrics) {
|
198 |
| - SerializeFamily(out, family); |
| 275 | + SerializeFamily(out, family, open_metrics); |
| 276 | + } |
| 277 | + |
| 278 | + if (open_metrics) { |
| 279 | + out << "# EOF\n"; |
199 | 280 | }
|
200 | 281 |
|
201 | 282 | out.imbue(saved_locale);
|
|
0 commit comments