Coverage Report

Created: 2024-09-19 09:45

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