]> git.proxmox.com Git - ceph.git/blob - ceph/src/seastar/src/core/prometheus.cc
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / seastar / src / core / prometheus.cc
1 /*
2 * This file is open source software, licensed to you under the terms
3 * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4 * distributed with this work for additional information regarding copyright
5 * ownership. You may not use this file except in compliance with the License.
6 *
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing,
12 * software distributed under the License is distributed on an
13 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 * KIND, either express or implied. See the License for the
15 * specific language governing permissions and limitations
16 * under the License.
17 */
18 /*
19 * Copyright (C) 2016 ScyllaDB
20 */
21
22 #include <seastar/core/prometheus.hh>
23 #include <sstream>
24
25 #include <seastar/core/scollectd_api.hh>
26 #include "core/scollectd-impl.hh"
27 #include <seastar/core/metrics_api.hh>
28 #include <seastar/http/function_handlers.hh>
29 #include <boost/algorithm/string/replace.hpp>
30 #include <boost/range/algorithm_ext/erase.hpp>
31 #include <boost/algorithm/string.hpp>
32 #include <boost/range/algorithm.hpp>
33 #include <boost/range/combine.hpp>
34 #include <seastar/core/thread.hh>
35 #include <seastar/core/loop.hh>
36 #include <regex>
37
38 namespace seastar {
39
40 extern seastar::logger seastar_logger;
41
42 namespace prometheus {
43
44 namespace mi = metrics::impl;
45
46 static std::string to_str(seastar::metrics::impl::data_type dt) {
47 switch (dt) {
48 case seastar::metrics::impl::data_type::GAUGE:
49 return "gauge";
50 case seastar::metrics::impl::data_type::COUNTER:
51 case seastar::metrics::impl::data_type::REAL_COUNTER:
52 return "counter";
53 case seastar::metrics::impl::data_type::HISTOGRAM:
54 return "histogram";
55 case seastar::metrics::impl::data_type::SUMMARY:
56 return "summary";
57 }
58 return "untyped";
59 }
60
61 static std::string to_str(const seastar::metrics::impl::metric_value& v) {
62 switch (v.type()) {
63 case seastar::metrics::impl::data_type::GAUGE:
64 case seastar::metrics::impl::data_type::REAL_COUNTER:
65 return std::to_string(v.d());
66 case seastar::metrics::impl::data_type::COUNTER:
67 return std::to_string(v.i());
68 case seastar::metrics::impl::data_type::HISTOGRAM:
69 case seastar::metrics::impl::data_type::SUMMARY:
70 break;
71 }
72 return ""; // we should never get here but it makes the compiler happy
73 }
74
75 static void add_name(std::ostream& s, const sstring& name, const std::map<sstring, sstring>& labels, const config& ctx) {
76 s << name << "{";
77 const char* delimiter = "";
78 if (ctx.label) {
79 s << ctx.label->key() << "=\"" << ctx.label->value() << '"';
80 delimiter = ",";
81 }
82
83 if (!labels.empty()) {
84 for (auto l : labels) {
85 s << delimiter;
86 s << l.first << "=\"" << l.second << '"';
87 delimiter = ",";
88 }
89 }
90 s << "} ";
91
92 }
93
94 /*!
95 * \brief iterator for metric family
96 *
97 * In prometheus, a single shard collecct all the data from the other
98 * shards and report it.
99 *
100 * Each shard returns a value_copy struct that has a vector of vector values (a vector per metric family)
101 * and a vector of metadata (and insdie it a vector of metric metadata)
102 *
103 * The metrics are sorted by the metric family name.
104 *
105 * In prometheus, all the metrics that belongs to the same metric family are reported together.
106 *
107 * For efficiency the results from the metrics layer are kept in a vector.
108 *
109 * So we have a vector of shards of a vector of metric families of a vector of values.
110 *
111 * To produce the result, we use the metric_family_iterator that is created by metric_family_range.
112 *
113 * When iterating over the metrics we use two helper structure.
114 *
115 * 1. A map between metric family name and the total number of values (combine on all shards) and
116 * pointer to the metric family metadata.
117 * 2. A vector of positions to the current metric family for each shard.
118 *
119 * The metric_family_range returns a metric_family_iterator that goes over all the families.
120 *
121 * The iterator returns a metric_family object, that can report the metric_family name, the size (how many
122 * metrics in total belongs to the metric family) and a a foreach_metric method.
123 *
124 * The foreach_metric method can be used to perform an action on each of the metric that belongs to
125 * that metric family
126 *
127 * Iterating over the metrics is done:
128 * - go over each of the shard and each of the entry in the position vector:
129 * - if the current family (the metric family that we get from the shard and position) has the current name:
130 * - iterate over each of the metrics belong to that metric family:
131 *
132 * for example, if m is a metric_family_range
133 *
134 * for (auto&& i : m) {
135 * std::cout << i.name() << std::endl;
136 * i.foreach_metric([](const mi::metric_value& value, const mi::metric_info& value_info) {
137 * std::cout << value_info.id.labels().size() <<std::cout;
138 * });
139 * }
140 *
141 * Will print all the metric family names followed by the number of labels each metric has.
142 */
143 class metric_family_iterator;
144
145 class metric_family_range;
146
147 class metrics_families_per_shard {
148 using metrics_family_per_shard_data_container = std::vector<foreign_ptr<mi::values_reference>>;
149 metrics_family_per_shard_data_container _data;
150 using comp_function = std::function<bool(const sstring&, const mi::metric_family_metadata&)>;
151 /*!
152 * \brief find the last item in a range of metric family based on a comparator function
153 *
154 */
155 metric_family_iterator find_bound(const sstring& family_name, comp_function comp) const;
156
157 public:
158
159 using const_iterator = metrics_family_per_shard_data_container::const_iterator;
160 using iterator = metrics_family_per_shard_data_container::iterator;
161 using reference = metrics_family_per_shard_data_container::reference;
162 using const_reference = metrics_family_per_shard_data_container::const_reference;
163
164 /*!
165 * \brief find the first item following a metric family range.
166 * metric family are sorted, this will return the first item that is outside
167 * of the range
168 */
169 metric_family_iterator upper_bound(const sstring& family_name) const;
170
171 /*!
172 * \brief find the first item in a range of metric family.
173 * metric family are sorted, the first item, is the first to match the
174 * criteria.
175 */
176 metric_family_iterator lower_bound(const sstring& family_name) const;
177
178 /**
179 * \defgroup Variables Global variables
180 */
181
182 /*
183 * @defgroup Vector properties
184 * The following methods making metrics_families_per_shard act as
185 * a vector of foreign_ptr<mi::values_reference>
186 * @{
187 *
188 *
189 */
190 iterator begin() {
191 return _data.begin();
192 }
193
194 iterator end() {
195 return _data.end();
196 }
197
198 const_iterator begin() const {
199 return _data.begin();
200 }
201
202 const_iterator end() const {
203 return _data.end();
204 }
205
206 void resize(size_t new_size) {
207 _data.resize(new_size);
208 }
209
210 reference& operator[](size_t n) {
211 return _data[n];
212 }
213
214 const_reference& operator[](size_t n) const {
215 return _data[n];
216 }
217 /** @} */
218 };
219
220 static future<> get_map_value(metrics_families_per_shard& vec) {
221 vec.resize(smp::count);
222 return parallel_for_each(boost::irange(0u, smp::count), [&vec] (auto cpu) {
223 return smp::submit_to(cpu, [] {
224 return mi::get_values();
225 }).then([&vec, cpu] (auto res) {
226 vec[cpu] = std::move(res);
227 });
228 });
229 }
230
231
232 /*!
233 * \brief a facade class for metric family
234 */
235 class metric_family {
236 const sstring* _name = nullptr;
237 uint32_t _size = 0;
238 const mi::metric_family_info* _family_info = nullptr;
239 metric_family_iterator& _iterator_state;
240 metric_family(metric_family_iterator& state) : _iterator_state(state) {
241 }
242 metric_family(const sstring* name , uint32_t size, const mi::metric_family_info* family_info, metric_family_iterator& state) :
243 _name(name), _size(size), _family_info(family_info), _iterator_state(state) {
244 }
245 metric_family(const metric_family& info, metric_family_iterator& state) :
246 metric_family(info._name, info._size, info._family_info, state) {
247 }
248 public:
249 metric_family(const metric_family&) = delete;
250 metric_family(metric_family&&) = delete;
251
252 const sstring& name() const {
253 return *_name;
254 }
255
256 const uint32_t size() const {
257 return _size;
258 }
259
260 const mi::metric_family_info& metadata() const {
261 return *_family_info;
262 }
263
264 void foreach_metric(std::function<void(const mi::metric_value&, const mi::metric_info&)>&& f);
265
266 bool end() const {
267 return !_name || !_family_info;
268 }
269 friend class metric_family_iterator;
270 };
271
272 class metric_family_iterator {
273 const metrics_families_per_shard& _families;
274 std::vector<size_t> _positions;
275 metric_family _info;
276
277 void next() {
278 if (_positions.empty()) {
279 return;
280 }
281 const sstring *new_name = nullptr;
282 const mi::metric_family_info* new_family_info = nullptr;
283 _info._size = 0;
284 for (auto&& i : boost::combine(_positions, _families)) {
285 auto& pos_in_metric_per_shard = boost::get<0>(i);
286 auto& metric_family = boost::get<1>(i);
287 if (_info._name && pos_in_metric_per_shard < metric_family->metadata->size() &&
288 metric_family->metadata->at(pos_in_metric_per_shard).mf.name.compare(*_info._name) <= 0) {
289 pos_in_metric_per_shard++;
290 }
291 if (pos_in_metric_per_shard >= metric_family->metadata->size()) {
292 // no more metric family in this shard
293 continue;
294 }
295 auto& metadata = metric_family->metadata->at(pos_in_metric_per_shard);
296 int cmp = (!new_name) ? -1 : metadata.mf.name.compare(*new_name);
297 if (cmp < 0) {
298 new_name = &metadata.mf.name;
299 new_family_info = &metadata.mf;
300 _info._size = 0;
301 }
302 if (cmp <= 0) {
303 _info._size += metadata.metrics.size();
304 }
305 }
306 _info._name = new_name;
307 _info._family_info = new_family_info;
308 }
309
310 public:
311 metric_family_iterator() = delete;
312 metric_family_iterator(const metric_family_iterator& o) : _families(o._families), _positions(o._positions), _info(*this) {
313 next();
314 }
315
316 metric_family_iterator(metric_family_iterator&& o) : _families(o._families), _positions(std::move(o._positions)),
317 _info(*this) {
318 next();
319 }
320
321 metric_family_iterator(const metrics_families_per_shard& families,
322 unsigned shards)
323 : _families(families), _positions(shards, 0), _info(*this) {
324 next();
325 }
326
327 metric_family_iterator(const metrics_families_per_shard& families,
328 std::vector<size_t>&& positions)
329 : _families(families), _positions(std::move(positions)), _info(*this) {
330 next();
331 }
332
333 metric_family_iterator& operator++() {
334 next();
335 return *this;
336 }
337
338 metric_family_iterator operator++(int) {
339 metric_family_iterator previous(*this);
340 next();
341 return previous;
342 }
343
344 bool operator!=(const metric_family_iterator& o) const {
345 return !(*this == o);
346 }
347
348 bool operator==(const metric_family_iterator& o) const {
349 if (end()) {
350 return o.end();
351 }
352 if (o.end()) {
353 return false;
354 }
355 return name() == o.name();
356 }
357
358 metric_family& operator*() {
359 return _info;
360 }
361
362 metric_family* operator->() {
363 return &_info;
364 }
365 const sstring& name() const {
366 return *_info._name;
367 }
368
369 const uint32_t size() const {
370 return _info._size;
371 }
372
373 const mi::metric_family_info& metadata() const {
374 return *_info._family_info;
375 }
376
377 bool end() const {
378 return _positions.empty() || _info.end();
379 }
380
381 void foreach_metric(std::function<void(const mi::metric_value&, const mi::metric_info&)>&& f) {
382 // iterating over the shard vector and the position vector
383 for (auto&& i : boost::combine(_positions, _families)) {
384 auto& pos_in_metric_per_shard = boost::get<0>(i);
385 auto& metric_family = boost::get<1>(i);
386 if (pos_in_metric_per_shard >= metric_family->metadata->size()) {
387 // no more metric family in this shard
388 continue;
389 }
390 auto& metadata = metric_family->metadata->at(pos_in_metric_per_shard);
391 // the the name is different, that means that on this shard, the metric family
392 // does not exist, because everything is sorted by metric family name, this is fine.
393 if (metadata.mf.name == name()) {
394 const mi::value_vector& values = metric_family->values[pos_in_metric_per_shard];
395 const mi::metric_metadata_vector& metrics_metadata = metadata.metrics;
396 for (auto&& vm : boost::combine(values, metrics_metadata)) {
397 auto& value = boost::get<0>(vm);
398 auto& metric_metadata = boost::get<1>(vm);
399 f(value, metric_metadata);
400 }
401 }
402 }
403 }
404
405 };
406
407 void metric_family::foreach_metric(std::function<void(const mi::metric_value&, const mi::metric_info&)>&& f) {
408 _iterator_state.foreach_metric(std::move(f));
409 }
410
411 class metric_family_range {
412 metric_family_iterator _begin;
413 metric_family_iterator _end;
414 public:
415 metric_family_range(const metrics_families_per_shard& families) : _begin(families, smp::count),
416 _end(metric_family_iterator(families, 0))
417 {
418 }
419
420 metric_family_range(const metric_family_iterator& b, const metric_family_iterator& e) : _begin(b), _end(e)
421 {
422 }
423
424 metric_family_iterator begin() const {
425 return _begin;
426 }
427
428 metric_family_iterator end() const {
429 return _end;
430 }
431 };
432
433 metric_family_iterator metrics_families_per_shard::find_bound(const sstring& family_name, comp_function comp) const {
434 std::vector<size_t> positions;
435 positions.reserve(smp::count);
436
437 for (auto& shard_info : _data) {
438 std::vector<mi::metric_family_metadata>& metadata = *(shard_info->metadata);
439 std::vector<mi::metric_family_metadata>::iterator it_b = boost::range::upper_bound(metadata, family_name, comp);
440 positions.emplace_back(it_b - metadata.begin());
441 }
442 return metric_family_iterator(*this, std::move(positions));
443
444 }
445
446 metric_family_iterator metrics_families_per_shard::lower_bound(const sstring& family_name) const {
447 return find_bound(family_name, [](const sstring& a, const mi::metric_family_metadata& b) {
448 //sstring doesn't have a <= operator
449 return a < b.mf.name || a == b.mf.name;
450 });
451 }
452
453 metric_family_iterator metrics_families_per_shard::upper_bound(const sstring& family_name) const {
454 return find_bound(family_name, [](const sstring& a, const mi::metric_family_metadata& b) {
455 return a < b.mf.name;
456 });
457 }
458
459 /*!
460 * \brief a helper function to get metric family range
461 * if metric_family_name is empty will return everything, if not, it will return
462 * the range of metric family that match the metric_family_name.
463 *
464 * if prefix is true the match will be based on prefix
465 */
466 metric_family_range get_range(const metrics_families_per_shard& mf, const sstring& metric_family_name, bool prefix) {
467 if (metric_family_name == "") {
468 return metric_family_range(mf);
469 }
470 auto upper_bount_prefix = metric_family_name;
471 ++upper_bount_prefix.back();
472 if (prefix) {
473 return metric_family_range(mf.lower_bound(metric_family_name), mf.lower_bound(upper_bount_prefix));
474 }
475 auto lb = mf.lower_bound(metric_family_name);
476 if (lb.end() || lb->name() != metric_family_name) {
477 return metric_family_range(lb, lb); // just return an empty range
478 }
479 auto up = lb;
480 ++up;
481 return metric_family_range(lb, up);
482
483 }
484
485 void write_histogram(std::stringstream& s, const config& ctx, const sstring& name, const seastar::metrics::histogram& h, std::map<sstring, sstring> labels) noexcept {
486 add_name(s, name + "_sum", labels, ctx);
487 s << h.sample_sum << '\n';
488
489 add_name(s, name + "_count", labels, ctx);
490 s << h.sample_count << '\n';
491
492 auto& le = labels["le"];
493 auto bucket = name + "_bucket";
494 for (auto i : h.buckets) {
495 le = std::to_string(i.upper_bound);
496 add_name(s, bucket, labels, ctx);
497 s << i.count << '\n';
498 }
499 labels["le"] = "+Inf";
500 add_name(s, bucket, labels, ctx);
501 s << h.sample_count << '\n';
502 }
503
504 void write_summary(std::stringstream& s, const config& ctx, const sstring& name, const seastar::metrics::histogram& h, std::map<sstring, sstring> labels) noexcept {
505 if (h.sample_sum) {
506 add_name(s, name + "_sum", labels, ctx);
507 s << h.sample_sum << '\n';
508 }
509 if (h.sample_count) {
510 add_name(s, name + "_count", labels, ctx);
511 s << h.sample_count << '\n';
512 }
513 auto& le = labels["quantile"];
514 for (auto i : h.buckets) {
515 le = std::to_string(i.upper_bound);
516 add_name(s, name, labels, ctx);
517 s << i.count << '\n';
518 }
519 }
520 /*!
521 * \brief a helper class to aggregate metrics over labels
522 *
523 * This class sum multiple metrics based on a list of labels.
524 * It returns one or more metrics each aggregated by the aggregate_by labels.
525 *
526 * To use it, you define what labels it should aggregate by and then pass to
527 * it metrics with their labels.
528 * For example if a metrics has a 'shard' and 'name' labels and you aggregate by 'shard'
529 * it would return a map of metrics each with only the 'name' label
530 *
531 */
532 class metric_aggregate_by_labels {
533 std::vector<std::string> _labels_to_aggregate_by;
534 std::unordered_map<std::map<sstring, sstring>, seastar::metrics::impl::metric_value> _values;
535 public:
536 metric_aggregate_by_labels(std::vector<std::string> labels) : _labels_to_aggregate_by(std::move(labels)) {
537 }
538 /*!
539 * \brief add a metric
540 *
541 * This method gets a metric and its labels and adds it to the aggregated metric.
542 * For example, if a metric has the labels {'shard':'0', 'name':'myhist'} and we are aggregating
543 * over 'shard'
544 * The metric would be added to the aggregated metric with labels {'name':'myhist'}.
545 *
546 */
547 void add(const seastar::metrics::impl::metric_value& m, std::map<sstring, sstring> labels) noexcept {
548 for (auto&& l : _labels_to_aggregate_by) {
549 labels.erase(l);
550 }
551 std::unordered_map<std::map<sstring, sstring>, seastar::metrics::impl::metric_value>::iterator i = _values.find(labels);
552 if ( i == _values.end()) {
553 _values.emplace(std::move(labels), m);
554 } else {
555 i->second += m;
556 }
557 }
558 const std::unordered_map<std::map<sstring, sstring>, seastar::metrics::impl::metric_value>& get_values() const noexcept {
559 return _values;
560 }
561 bool empty() const noexcept {
562 return _values.empty();
563 }
564 };
565
566 std::string get_value_as_string(std::stringstream& s, const mi::metric_value& value) noexcept {
567 std::string value_str;
568 try {
569 value_str = to_str(value);
570 } catch (const std::range_error& e) {
571 seastar_logger.debug("prometheus: get_value_as_string: {}: {}", s.str(), e.what());
572 value_str = "NaN";
573 } catch (...) {
574 auto ex = std::current_exception();
575 // print this error as it's ignored later on by `connection::start_response`
576 seastar_logger.error("prometheus: get_value_as_string: {}: {}", s.str(), ex);
577 std::rethrow_exception(std::move(ex));
578 }
579 return value_str;
580 }
581
582 future<> write_text_representation(output_stream<char>& out, const config& ctx, const metric_family_range& m, bool show_help, std::function<bool(const mi::labels_type&)> filter) {
583 return seastar::async([&ctx, &out, &m, show_help, filter] () mutable {
584 bool found = false;
585 std::stringstream s;
586 for (metric_family& metric_family : m) {
587 auto name = ctx.prefix + "_" + metric_family.name();
588 found = false;
589 metric_aggregate_by_labels aggregated_values(metric_family.metadata().aggregate_labels);
590 bool should_aggregate = !metric_family.metadata().aggregate_labels.empty();
591 metric_family.foreach_metric([&s, &out, &ctx, &found, &name, &metric_family, &aggregated_values, should_aggregate, show_help, &filter](auto value, auto value_info) mutable {
592 s.clear();
593 s.str("");
594 if ((value_info.should_skip_when_empty && value.is_empty()) || !filter(value_info.id.labels())) {
595 return;
596 }
597 if (!found) {
598 if (show_help && metric_family.metadata().d.str() != "") {
599 s << "# HELP " << name << " " << metric_family.metadata().d.str() << '\n';
600 }
601 s << "# TYPE " << name << " " << to_str(metric_family.metadata().type) << '\n';
602 found = true;
603 }
604 if (should_aggregate) {
605 aggregated_values.add(value, value_info.id.labels());
606 } else if (value.type() == mi::data_type::SUMMARY) {
607 write_summary(s, ctx, name, value.get_histogram(), value_info.id.labels());
608 } else if (value.type() == mi::data_type::HISTOGRAM) {
609 write_histogram(s, ctx, name, value.get_histogram(), value_info.id.labels());
610 } else {
611 add_name(s, name, value_info.id.labels(), ctx);
612 s << get_value_as_string(s, value) << '\n';
613 }
614 out.write(s.str()).get();
615 thread::maybe_yield();
616 });
617 if (!aggregated_values.empty()) {
618 for (auto&& h : aggregated_values.get_values()) {
619 s.clear();
620 s.str("");
621 if (h.second.type() == mi::data_type::HISTOGRAM) {
622 write_histogram(s, ctx, name, h.second.get_histogram(), h.first);
623 } else {
624 add_name(s, name, h.first, ctx);
625 s << get_value_as_string(s, h.second) << '\n';
626 }
627 out.write(s.str()).get();
628 thread::maybe_yield();
629 }
630 }
631 }
632 });
633 }
634
635 class metrics_handler : public handler_base {
636 sstring _prefix;
637 config _ctx;
638 static std::function<bool(const mi::labels_type&)> _true_function;
639
640 /*!
641 * \brief tries to trim an asterisk from the end of the string
642 * return true if an asterisk exists.
643 */
644 bool trim_asterisk(sstring& name) {
645 if (name.size() && name.back() == '*') {
646 name.resize(name.length() - 1);
647 return true;
648 }
649 // Prometheus uses url encoding for the path so '*' is encoded as '%2A'
650 if (boost::algorithm::ends_with(name, "%2A")) {
651 // This assert is obviously true. It is in here just to
652 // silence a bogus gcc warning:
653 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89337
654 assert(name.length() >= 3);
655 name.resize(name.length() - 3);
656 return true;
657 }
658 return false;
659 }
660 /*!
661 * \brief Return a filter function, based on the request
662 *
663 * A filter function filter what metrics should be included.
664 * It returns true if a metric should be included, or false otherwise.
665 * The filters are created from the request query parameters.
666 */
667 std::function<bool(const mi::labels_type&)> make_filter(const http::request& req) {
668 std::unordered_map<sstring, std::regex> matcher;
669 auto labels = mi::get_local_impl()->get_labels();
670 for (auto&& qp : req.query_parameters) {
671 if (labels.find(qp.first) != labels.end()) {
672 matcher.emplace(qp.first, std::regex(qp.second.c_str()));
673 }
674 }
675 return (matcher.empty()) ? _true_function : [matcher](const mi::labels_type& labels) {
676 for (auto&& m : matcher) {
677 auto l = labels.find(m.first);
678 if (!std::regex_match((l == labels.end())? "" : l->second.c_str(), m.second)) {
679 return false;
680 }
681 }
682 return true;
683 };
684 }
685
686 public:
687 metrics_handler(config ctx) : _ctx(ctx) {}
688
689 future<std::unique_ptr<http::reply>> handle(const sstring& path,
690 std::unique_ptr<http::request> req, std::unique_ptr<http::reply> rep) override {
691 sstring metric_family_name = req->get_query_param("__name__");
692 bool prefix = trim_asterisk(metric_family_name);
693 bool show_help = req->get_query_param("__help__") != "false";
694 std::function<bool(const mi::labels_type&)> filter = make_filter(*req);
695
696 rep->write_body("txt", [this, metric_family_name, prefix, show_help, filter] (output_stream<char>&& s) {
697 return do_with(metrics_families_per_shard(), output_stream<char>(std::move(s)),
698 [this, prefix, &metric_family_name, show_help, filter] (metrics_families_per_shard& families, output_stream<char>& s) mutable {
699 return get_map_value(families).then([&s, &families, this, prefix, &metric_family_name, show_help, filter]() mutable {
700 return do_with(get_range(families, metric_family_name, prefix),
701 [&s, this, show_help, filter](metric_family_range& m) {
702 return write_text_representation(s, _ctx, m, show_help, filter);
703 });
704 }).finally([&s] () mutable {
705 return s.close();
706 });
707 });
708 });
709 return make_ready_future<std::unique_ptr<http::reply>>(std::move(rep));
710 }
711 };
712
713 std::function<bool(const mi::labels_type&)> metrics_handler::_true_function = [](const mi::labels_type&) {
714 return true;
715 };
716
717 future<> add_prometheus_routes(http_server& server, config ctx) {
718 server._routes.put(GET, "/metrics", new metrics_handler(ctx));
719 return make_ready_future<>();
720 }
721
722 future<> add_prometheus_routes(distributed<http_server>& server, config ctx) {
723 return server.invoke_on_all([ctx](http_server& s) {
724 return add_prometheus_routes(s, ctx);
725 });
726 }
727
728 future<> start(httpd::http_server_control& http_server, config ctx) {
729 return add_prometheus_routes(http_server.server(), ctx);
730 }
731
732 }
733 }