/proc/self/cwd/source/server/admin/prometheus_stats.cc
Line | Count | Source (jump to first uncovered line) |
1 | | #include "source/server/admin/prometheus_stats.h" |
2 | | |
3 | | #include "source/common/common/empty_string.h" |
4 | | #include "source/common/common/macros.h" |
5 | | #include "source/common/common/regex.h" |
6 | | #include "source/common/stats/histogram_impl.h" |
7 | | #include "source/common/upstream/host_utility.h" |
8 | | |
9 | | #include "absl/strings/str_cat.h" |
10 | | #include "absl/strings/str_replace.h" |
11 | | |
12 | | namespace Envoy { |
13 | | namespace Server { |
14 | | |
15 | | namespace { |
16 | | |
17 | 0 | const Regex::CompiledGoogleReMatcher& promRegex() { |
18 | 0 | CONSTRUCT_ON_FIRST_USE(Regex::CompiledGoogleReMatcher, "[^a-zA-Z0-9_]", false); |
19 | 0 | } |
20 | | |
21 | | /** |
22 | | * Take a string and sanitize it according to Prometheus conventions. |
23 | | */ |
24 | 0 | std::string sanitizeName(const absl::string_view name) { |
25 | | // The name must match the regex [a-zA-Z_][a-zA-Z0-9_]* as required by |
26 | | // prometheus. Refer to https://prometheus.io/docs/concepts/data_model/. |
27 | | // The initial [a-zA-Z_] constraint is always satisfied by the namespace prefix. |
28 | 0 | return promRegex().replaceAll(name, "_"); |
29 | 0 | } |
30 | | |
31 | | /** |
32 | | * Take tag values and sanitize it for text serialization, according to |
33 | | * Prometheus conventions. |
34 | | */ |
35 | 0 | std::string sanitizeValue(const absl::string_view value) { |
36 | | // Removes problematic characters from Prometheus tag values to prevent |
37 | | // text serialization issues. This matches the prometheus text formatting code: |
38 | | // https://github.com/prometheus/common/blob/88f1636b699ae4fb949d292ffb904c205bf542c9/expfmt/text_create.go#L419-L420. |
39 | | // The goal is to replace '\' with "\\", newline with "\n", and '"' with "\"". |
40 | 0 | return absl::StrReplaceAll(value, { |
41 | 0 | {R"(\)", R"(\\)"}, |
42 | 0 | {"\n", R"(\n)"}, |
43 | 0 | {R"(")", R"(\")"}, |
44 | 0 | }); |
45 | 0 | } |
46 | | |
47 | | /* |
48 | | * Comparator for Stats::Metric that does not require a string representation |
49 | | * to make the comparison, for memory efficiency. |
50 | | */ |
51 | | struct MetricLessThan { |
52 | 0 | bool operator()(const Stats::Metric* a, const Stats::Metric* b) const { |
53 | 0 | ASSERT(&a->constSymbolTable() == &b->constSymbolTable()); |
54 | 0 | return a->constSymbolTable().lessThan(a->statName(), b->statName()); |
55 | 0 | } |
56 | | }; |
57 | | |
58 | | struct PrimitiveMetricSnapshotLessThan { |
59 | | bool operator()(const Stats::PrimitiveMetricMetadata* a, |
60 | 0 | const Stats::PrimitiveMetricMetadata* b) { |
61 | 0 | return a->name() < b->name(); |
62 | 0 | } |
63 | | }; |
64 | | |
65 | | std::string generateNumericOutput(uint64_t value, const Stats::TagVector& tags, |
66 | 0 | const std::string& prefixed_tag_extracted_name) { |
67 | 0 | const std::string formatted_tags = PrometheusStatsFormatter::formattedTags(tags); |
68 | 0 | return fmt::format("{0}{{{1}}} {2}\n", prefixed_tag_extracted_name, formatted_tags, value); |
69 | 0 | } |
70 | | |
71 | | /* |
72 | | * Return the prometheus output for a numeric Stat (Counter or Gauge). |
73 | | */ |
74 | | template <class StatType> |
75 | | std::string generateStatNumericOutput(const StatType& metric, |
76 | 0 | const std::string& prefixed_tag_extracted_name) { |
77 | 0 | return generateNumericOutput(metric.value(), metric.tags(), prefixed_tag_extracted_name); |
78 | 0 | } Unexecuted instantiation: prometheus_stats.cc:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > Envoy::Server::(anonymous namespace)::generateStatNumericOutput<Envoy::Stats::Counter>(Envoy::Stats::Counter const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Unexecuted instantiation: prometheus_stats.cc:std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > Envoy::Server::(anonymous namespace)::generateStatNumericOutput<Envoy::Stats::Gauge>(Envoy::Stats::Gauge const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) |
79 | | |
80 | | /* |
81 | | * Returns the prometheus output for a TextReadout in gauge format. |
82 | | * It is a workaround of a limitation of prometheus which stores only numeric metrics. |
83 | | * The output is a gauge named the same as a given text-readout. The value of returned gauge is |
84 | | * always equal to 0. Returned gauge contains all tags of a given text-readout and one additional |
85 | | * tag {"text_value":"textReadout.value"}. |
86 | | */ |
87 | | std::string generateTextReadoutOutput(const Stats::TextReadout& text_readout, |
88 | 0 | const std::string& prefixed_tag_extracted_name) { |
89 | 0 | auto tags = text_readout.tags(); |
90 | 0 | tags.push_back(Stats::Tag{"text_value", text_readout.value()}); |
91 | 0 | const std::string formattedTags = PrometheusStatsFormatter::formattedTags(tags); |
92 | 0 | return fmt::format("{0}{{{1}}} 0\n", prefixed_tag_extracted_name, formattedTags); |
93 | 0 | } |
94 | | |
95 | | /* |
96 | | * Returns the prometheus output for a histogram. The output is a multi-line string (with embedded |
97 | | * newlines) that contains all the individual bucket counts and sum/count for a single histogram |
98 | | * (metric_name plus all tags). |
99 | | */ |
100 | | std::string generateHistogramOutput(const Stats::ParentHistogram& histogram, |
101 | 0 | const std::string& prefixed_tag_extracted_name) { |
102 | 0 | const std::string tags = PrometheusStatsFormatter::formattedTags(histogram.tags()); |
103 | 0 | const std::string hist_tags = histogram.tags().empty() ? EMPTY_STRING : (tags + ","); |
104 | |
|
105 | 0 | const Stats::HistogramStatistics& stats = histogram.cumulativeStatistics(); |
106 | 0 | Stats::ConstSupportedBuckets& supported_buckets = stats.supportedBuckets(); |
107 | 0 | const std::vector<uint64_t>& computed_buckets = stats.computedBuckets(); |
108 | 0 | std::string output; |
109 | 0 | for (size_t i = 0; i < supported_buckets.size(); ++i) { |
110 | 0 | double bucket = supported_buckets[i]; |
111 | 0 | uint64_t value = computed_buckets[i]; |
112 | | // We want to print the bucket in a fixed point (non-scientific) format. The fmt library |
113 | | // doesn't have a specific modifier to format as a fixed-point value only so we use the |
114 | | // 'g' operator which prints the number in general fixed point format or scientific format |
115 | | // with precision 50 to round the number up to 32 significant digits in fixed point format |
116 | | // which should cover pretty much all cases |
117 | 0 | output.append(fmt::format("{0}_bucket{{{1}le=\"{2:.32g}\"}} {3}\n", prefixed_tag_extracted_name, |
118 | 0 | hist_tags, bucket, value)); |
119 | 0 | } |
120 | |
|
121 | 0 | output.append(fmt::format("{0}_bucket{{{1}le=\"+Inf\"}} {2}\n", prefixed_tag_extracted_name, |
122 | 0 | hist_tags, stats.sampleCount())); |
123 | 0 | output.append(fmt::format("{0}_sum{{{1}}} {2:.32g}\n", prefixed_tag_extracted_name, tags, |
124 | 0 | stats.sampleSum())); |
125 | 0 | output.append(fmt::format("{0}_count{{{1}}} {2}\n", prefixed_tag_extracted_name, tags, |
126 | 0 | stats.sampleCount())); |
127 | |
|
128 | 0 | return output; |
129 | 0 | }; |
130 | | |
131 | | /** |
132 | | * Processes a stat type (counter, gauge, histogram) by generating all output lines, sorting |
133 | | * them by tag-extracted metric name, and then outputting them in the correct sorted order into |
134 | | * response. |
135 | | * |
136 | | * @param response The buffer to put the output into. |
137 | | * @param used_only Whether to only output stats that are used. |
138 | | * @param regex A filter on which stats to output. |
139 | | * @param metrics The metrics to output stats for. This must contain all stats of the given type |
140 | | * to be included in the same output. |
141 | | * @param generate_output A function which returns the output text for this metric. |
142 | | * @param type The name of the prometheus metric type for used in TYPE annotations. |
143 | | */ |
144 | | template <class StatType> |
145 | | uint64_t outputStatType( |
146 | | Buffer::Instance& response, const StatsParams& params, |
147 | | const std::vector<Stats::RefcountPtr<StatType>>& metrics, |
148 | | const std::function<std::string( |
149 | | const StatType& metric, const std::string& prefixed_tag_extracted_name)>& generate_output, |
150 | 0 | absl::string_view type, const Stats::CustomStatNamespaces& custom_namespaces) { |
151 | | |
152 | | /* |
153 | | * From |
154 | | * https:*github.com/prometheus/docs/blob/master/content/docs/instrumenting/exposition_formats.md#grouping-and-sorting: |
155 | | * |
156 | | * All lines for a given metric must be provided as one single group, with the optional HELP and |
157 | | * TYPE lines first (in no particular order). Beyond that, reproducible sorting in repeated |
158 | | * expositions is preferred but not required, i.e. do not sort if the computational cost is |
159 | | * prohibitive. |
160 | | */ |
161 | | |
162 | | // This is an unsorted collection of dumb-pointers (no need to increment then decrement every |
163 | | // refcount; ownership is held throughout by `metrics`). It is unsorted for efficiency, but will |
164 | | // be sorted before producing the final output to satisfy the "preferred" ordering from the |
165 | | // prometheus spec: metrics will be sorted by their tags' textual representation, which will be |
166 | | // consistent across calls. |
167 | 0 | using StatTypeUnsortedCollection = std::vector<const StatType*>; |
168 | | |
169 | | // Return early to avoid crashing when getting the symbol table from the first metric. |
170 | 0 | if (metrics.empty()) { |
171 | 0 | return 0; |
172 | 0 | } |
173 | | |
174 | | // There should only be one symbol table for all of the stats in the admin |
175 | | // interface. If this assumption changes, the name comparisons in this function |
176 | | // will have to change to compare to convert all StatNames to strings before |
177 | | // comparison. |
178 | 0 | const Stats::SymbolTable& global_symbol_table = metrics.front()->constSymbolTable(); |
179 | | |
180 | | // Sorted collection of metrics sorted by their tagExtractedName, to satisfy the requirements |
181 | | // of the exposition format. |
182 | 0 | std::map<Stats::StatName, StatTypeUnsortedCollection, Stats::StatNameLessThan> groups( |
183 | 0 | global_symbol_table); |
184 | |
|
185 | 0 | for (const auto& metric : metrics) { |
186 | 0 | ASSERT(&global_symbol_table == &metric->constSymbolTable()); |
187 | 0 | if (!params.shouldShowMetric(*metric)) { |
188 | 0 | continue; |
189 | 0 | } |
190 | 0 | groups[metric->tagExtractedStatName()].push_back(metric.get()); |
191 | 0 | } |
192 | | |
193 | 0 | auto result = groups.size(); |
194 | 0 | for (auto& group : groups) { |
195 | 0 | const absl::optional<std::string> prefixed_tag_extracted_name = |
196 | 0 | PrometheusStatsFormatter::metricName(global_symbol_table.toString(group.first), |
197 | 0 | custom_namespaces); |
198 | 0 | if (!prefixed_tag_extracted_name.has_value()) { |
199 | 0 | --result; |
200 | 0 | continue; |
201 | 0 | } |
202 | 0 | response.add(fmt::format("# TYPE {0} {1}\n", prefixed_tag_extracted_name.value(), type)); |
203 | | |
204 | | // Sort before producing the final output to satisfy the "preferred" ordering from the |
205 | | // prometheus spec: metrics will be sorted by their tags' textual representation, which will |
206 | | // be consistent across calls. |
207 | 0 | std::sort(group.second.begin(), group.second.end(), MetricLessThan()); |
208 | |
|
209 | 0 | for (const auto& metric : group.second) { |
210 | 0 | response.add(generate_output(*metric, prefixed_tag_extracted_name.value())); |
211 | 0 | } |
212 | 0 | } |
213 | 0 | return result; |
214 | 0 | } Unexecuted instantiation: prometheus_stats.cc:unsigned long Envoy::Server::(anonymous namespace)::outputStatType<Envoy::Stats::Counter>(Envoy::Buffer::Instance&, Envoy::Server::StatsParams const&, std::__1::vector<Envoy::Stats::RefcountPtr<Envoy::Stats::Counter>, std::__1::allocator<Envoy::Stats::RefcountPtr<Envoy::Stats::Counter> > > const&, std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > (Envoy::Stats::Counter const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)> const&, std::__1::basic_string_view<char, std::__1::char_traits<char> >, Envoy::Stats::CustomStatNamespaces const&) Unexecuted instantiation: prometheus_stats.cc:unsigned long Envoy::Server::(anonymous namespace)::outputStatType<Envoy::Stats::Gauge>(Envoy::Buffer::Instance&, Envoy::Server::StatsParams const&, std::__1::vector<Envoy::Stats::RefcountPtr<Envoy::Stats::Gauge>, std::__1::allocator<Envoy::Stats::RefcountPtr<Envoy::Stats::Gauge> > > const&, std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > (Envoy::Stats::Gauge const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)> const&, std::__1::basic_string_view<char, std::__1::char_traits<char> >, Envoy::Stats::CustomStatNamespaces const&) Unexecuted instantiation: prometheus_stats.cc:unsigned long Envoy::Server::(anonymous namespace)::outputStatType<Envoy::Stats::TextReadout>(Envoy::Buffer::Instance&, Envoy::Server::StatsParams const&, std::__1::vector<Envoy::Stats::RefcountPtr<Envoy::Stats::TextReadout>, std::__1::allocator<Envoy::Stats::RefcountPtr<Envoy::Stats::TextReadout> > > const&, std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > (Envoy::Stats::TextReadout const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)> const&, std::__1::basic_string_view<char, std::__1::char_traits<char> >, Envoy::Stats::CustomStatNamespaces const&) Unexecuted instantiation: prometheus_stats.cc:unsigned long Envoy::Server::(anonymous namespace)::outputStatType<Envoy::Stats::ParentHistogram>(Envoy::Buffer::Instance&, Envoy::Server::StatsParams const&, std::__1::vector<Envoy::Stats::RefcountPtr<Envoy::Stats::ParentHistogram>, std::__1::allocator<Envoy::Stats::RefcountPtr<Envoy::Stats::ParentHistogram> > > const&, std::__1::function<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > (Envoy::Stats::ParentHistogram const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)> const&, std::__1::basic_string_view<char, std::__1::char_traits<char> >, Envoy::Stats::CustomStatNamespaces const&) |
215 | | |
216 | | template <class StatType> |
217 | | uint64_t outputPrimitiveStatType(Buffer::Instance& response, const StatsParams& params, |
218 | | const std::vector<StatType>& metrics, absl::string_view type, |
219 | 0 | const Stats::CustomStatNamespaces& custom_namespaces) { |
220 | | |
221 | | /* |
222 | | * From |
223 | | * https:*github.com/prometheus/docs/blob/master/content/docs/instrumenting/exposition_formats.md#grouping-and-sorting: |
224 | | * |
225 | | * All lines for a given metric must be provided as one single group, with the optional HELP and |
226 | | * TYPE lines first (in no particular order). Beyond that, reproducible sorting in repeated |
227 | | * expositions is preferred but not required, i.e. do not sort if the computational cost is |
228 | | * prohibitive. |
229 | | */ |
230 | | |
231 | | // This is an unsorted collection of dumb-pointers (no need to increment then decrement every |
232 | | // refcount; ownership is held throughout by `metrics`). It is unsorted for efficiency, but will |
233 | | // be sorted before producing the final output to satisfy the "preferred" ordering from the |
234 | | // prometheus spec: metrics will be sorted by their tags' textual representation, which will be |
235 | | // consistent across calls. |
236 | 0 | using StatTypeUnsortedCollection = std::vector<const StatType*>; |
237 | | |
238 | | // Return early to avoid crashing when getting the symbol table from the first metric. |
239 | 0 | if (metrics.empty()) { |
240 | 0 | return 0; |
241 | 0 | } |
242 | | |
243 | | // Sorted collection of metrics sorted by their tagExtractedName, to satisfy the requirements |
244 | | // of the exposition format. |
245 | 0 | std::map<std::string, StatTypeUnsortedCollection> groups; |
246 | |
|
247 | 0 | for (const auto& metric : metrics) { |
248 | 0 | if (!params.shouldShowMetric(metric)) { |
249 | 0 | continue; |
250 | 0 | } |
251 | 0 | groups[metric.tagExtractedName()].push_back(&metric); |
252 | 0 | } |
253 | |
|
254 | 0 | auto result = groups.size(); |
255 | 0 | for (auto& group : groups) { |
256 | 0 | const absl::optional<std::string> prefixed_tag_extracted_name = |
257 | 0 | PrometheusStatsFormatter::metricName(group.first, custom_namespaces); |
258 | 0 | if (!prefixed_tag_extracted_name.has_value()) { |
259 | 0 | --result; |
260 | 0 | continue; |
261 | 0 | } |
262 | 0 | response.add(fmt::format("# TYPE {0} {1}\n", prefixed_tag_extracted_name.value(), type)); |
263 | | |
264 | | // Sort before producing the final output to satisfy the "preferred" ordering from the |
265 | | // prometheus spec: metrics will be sorted by their tags' textual representation, which will |
266 | | // be consistent across calls. |
267 | 0 | std::sort(group.second.begin(), group.second.end(), PrimitiveMetricSnapshotLessThan()); |
268 | |
|
269 | 0 | for (const auto& metric : group.second) { |
270 | 0 | response.add(generateNumericOutput(metric->value(), metric->tags(), |
271 | 0 | prefixed_tag_extracted_name.value())); |
272 | 0 | } |
273 | 0 | } |
274 | 0 | return result; |
275 | 0 | } Unexecuted instantiation: prometheus_stats.cc:unsigned long Envoy::Server::(anonymous namespace)::outputPrimitiveStatType<Envoy::Stats::PrimitiveCounterSnapshot>(Envoy::Buffer::Instance&, Envoy::Server::StatsParams const&, std::__1::vector<Envoy::Stats::PrimitiveCounterSnapshot, std::__1::allocator<Envoy::Stats::PrimitiveCounterSnapshot> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> >, Envoy::Stats::CustomStatNamespaces const&) Unexecuted instantiation: prometheus_stats.cc:unsigned long Envoy::Server::(anonymous namespace)::outputPrimitiveStatType<Envoy::Stats::PrimitiveGaugeSnapshot>(Envoy::Buffer::Instance&, Envoy::Server::StatsParams const&, std::__1::vector<Envoy::Stats::PrimitiveGaugeSnapshot, std::__1::allocator<Envoy::Stats::PrimitiveGaugeSnapshot> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> >, Envoy::Stats::CustomStatNamespaces const&) |
276 | | |
277 | | } // namespace |
278 | | |
279 | 0 | std::string PrometheusStatsFormatter::formattedTags(const std::vector<Stats::Tag>& tags) { |
280 | 0 | std::vector<std::string> buf; |
281 | 0 | buf.reserve(tags.size()); |
282 | 0 | for (const Stats::Tag& tag : tags) { |
283 | 0 | buf.push_back(fmt::format("{}=\"{}\"", sanitizeName(tag.name_), sanitizeValue(tag.value_))); |
284 | 0 | } |
285 | 0 | return absl::StrJoin(buf, ","); |
286 | 0 | } |
287 | | |
288 | | absl::optional<std::string> |
289 | | PrometheusStatsFormatter::metricName(const std::string& extracted_name, |
290 | 0 | const Stats::CustomStatNamespaces& custom_namespaces) { |
291 | 0 | const absl::optional<absl::string_view> custom_namespace_stripped = |
292 | 0 | custom_namespaces.stripRegisteredPrefix(extracted_name); |
293 | 0 | if (custom_namespace_stripped.has_value()) { |
294 | | // This case the name has a custom namespace, and it is a custom metric. |
295 | 0 | const std::string sanitized_name = sanitizeName(custom_namespace_stripped.value()); |
296 | | // We expose these metrics without modifying (e.g. without "envoy_"), |
297 | | // so we have to check the "user-defined" stat name complies with the Prometheus naming |
298 | | // convention. Specifically the name must start with the "[a-zA-Z_]" pattern. |
299 | | // All the characters in sanitized_name are already in "[a-zA-Z0-9_]" pattern |
300 | | // thanks to sanitizeName above, so the only thing we have to do is check |
301 | | // if it does not start with digits. |
302 | 0 | if (sanitized_name.empty() || absl::ascii_isdigit(sanitized_name.front())) { |
303 | 0 | return absl::nullopt; |
304 | 0 | } |
305 | 0 | return sanitized_name; |
306 | 0 | } |
307 | | |
308 | | // If it does not have a custom namespace, add namespacing prefix to avoid conflicts, as per best |
309 | | // practice: https://prometheus.io/docs/practices/naming/#metric-names Also, naming conventions on |
310 | | // https://prometheus.io/docs/concepts/data_model/ |
311 | 0 | return absl::StrCat("envoy_", sanitizeName(extracted_name)); |
312 | 0 | } |
313 | | |
314 | | uint64_t PrometheusStatsFormatter::statsAsPrometheus( |
315 | | const std::vector<Stats::CounterSharedPtr>& counters, |
316 | | const std::vector<Stats::GaugeSharedPtr>& gauges, |
317 | | const std::vector<Stats::ParentHistogramSharedPtr>& histograms, |
318 | | const std::vector<Stats::TextReadoutSharedPtr>& text_readouts, |
319 | | const Upstream::ClusterManager& cluster_manager, Buffer::Instance& response, |
320 | 0 | const StatsParams& params, const Stats::CustomStatNamespaces& custom_namespaces) { |
321 | |
|
322 | 0 | uint64_t metric_name_count = 0; |
323 | 0 | metric_name_count += outputStatType<Stats::Counter>(response, params, counters, |
324 | 0 | generateStatNumericOutput<Stats::Counter>, |
325 | 0 | "counter", custom_namespaces); |
326 | |
|
327 | 0 | metric_name_count += outputStatType<Stats::Gauge>(response, params, gauges, |
328 | 0 | generateStatNumericOutput<Stats::Gauge>, |
329 | 0 | "gauge", custom_namespaces); |
330 | | |
331 | | // TextReadout stats are returned in gauge format, so "gauge" type is set intentionally. |
332 | 0 | metric_name_count += outputStatType<Stats::TextReadout>( |
333 | 0 | response, params, text_readouts, generateTextReadoutOutput, "gauge", custom_namespaces); |
334 | |
|
335 | 0 | metric_name_count += outputStatType<Stats::ParentHistogram>( |
336 | 0 | response, params, histograms, generateHistogramOutput, "histogram", custom_namespaces); |
337 | | |
338 | | // Note: This assumes that there is no overlap in stat name between per-endpoint stats and all |
339 | | // other stats. If this is not true, then the counters/gauges for per-endpoint need to be combined |
340 | | // with the above counter/gauge calls so that stats can be properly grouped. |
341 | 0 | std::vector<Stats::PrimitiveCounterSnapshot> host_counters; |
342 | 0 | std::vector<Stats::PrimitiveGaugeSnapshot> host_gauges; |
343 | 0 | Upstream::HostUtility::forEachHostMetric( |
344 | 0 | cluster_manager, |
345 | 0 | [&](Stats::PrimitiveCounterSnapshot&& metric) { |
346 | 0 | host_counters.emplace_back(std::move(metric)); |
347 | 0 | }, |
348 | 0 | [&](Stats::PrimitiveGaugeSnapshot&& metric) { host_gauges.emplace_back(std::move(metric)); }); |
349 | |
|
350 | 0 | metric_name_count += |
351 | 0 | outputPrimitiveStatType(response, params, host_counters, "counter", custom_namespaces); |
352 | |
|
353 | 0 | metric_name_count += |
354 | 0 | outputPrimitiveStatType(response, params, host_gauges, "gauge", custom_namespaces); |
355 | |
|
356 | 0 | return metric_name_count; |
357 | 0 | } |
358 | | |
359 | | } // namespace Server |
360 | | } // namespace Envoy |