]> git.proxmox.com Git - ceph.git/blame - ceph/src/seastar/include/seastar/core/metrics.hh
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / seastar / include / seastar / core / metrics.hh
CommitLineData
11fdf7f2
TL
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#pragma once
23
24#include <functional>
20effc67 25#include <limits>
1e59de90
TL
26#include <map>
27#include <type_traits>
11fdf7f2
TL
28#include <seastar/core/sstring.hh>
29#include <seastar/core/shared_ptr.hh>
30#include <seastar/core/metrics_registration.hh>
31#include <boost/lexical_cast.hpp>
11fdf7f2
TL
32#include <seastar/core/metrics_types.hh>
33#include <seastar/util/std-compat.hh>
1e59de90 34#include <seastar/util/bool_class.hh>
11fdf7f2
TL
35
36/*! \file metrics.hh
37 * \brief header for metrics creation.
38 *
39 * This header file contains the metrics creation method with their helper function.
40 * Include this file when need to create metrics.
41 * Typically this will be in your source file.
42 *
43 * Code that is under the impl namespace should not be used directly.
44 *
45 */
46
47namespace seastar {
48
49/*!
f67539c2
TL
50 * \addtogroup metrics
51 * @{
52 *
11fdf7f2
TL
53 * \namespace seastar::metrics
54 * \brief metrics creation and registration
55 *
56 * the metrics namespace holds the relevant method and classes to generate metrics.
57 *
58 * The metrics layer support registering metrics, that later will be
59 * exported via different API protocols.
60 *
61 * To be able to support multiple protocols the following simplifications where made:
62 * 1. The id of the metrics is based on the collectd id
63 * 2. A metric could be a single value either a reference or a function
64 *
65 * To add metrics definition to class A do the following:
66 * * Add a metrics_group memeber to A
67 * * Add a a set_metrics() method that would be called in the constructor.
68 *
69 *
70 * In A header file
71 * \code
72 * #include "core/metrics_registration.hh"
73 * class A {
74 * metric_groups _metrics
75 *
76 * void setup_metrics();
77 *
78 * };
79 * \endcode
80 *
81 * In A source file:
82 *
83 * \code
84 * include "core/metrics.hh"
85 *
86 * void A::setup_metrics() {
87 * namespace sm = seastar::metrics;
88 * _metrics = sm::create_metric_group();
89 * _metrics->add_group("cache", {sm::make_gauge("bytes", "used", [this] { return _region.occupancy().used_space(); })});
90 * }
91 * \endcode
92 */
93
94namespace metrics {
95
9f95a23c
TL
96class double_registration : public std::runtime_error {
97public:
98 double_registration(std::string what);
99};
11fdf7f2
TL
100
101/*!
102 * \defgroup metrics_types metrics type definitions
103 * The following are for the metric layer use, do not use them directly
1e59de90 104 * Instead use the make_counter, make_gauge
11fdf7f2
TL
105 *
106 */
107using metric_type_def = sstring; /*!< Used to hold an inherit type (like bytes)*/
108using metric_name_type = sstring; /*!< The metric name'*/
109using instance_id_type = sstring; /*!< typically used for the shard id*/
1e59de90 110using skip_when_empty = bool_class<class skip_when_empty_tag>;
11fdf7f2
TL
111
112/*!
113 * \brief Human-readable description of a metric/group.
114 *
115 *
116 * Uses a separate class to deal with type resolution
117 *
118 * Add this to metric creation:
119 *
120 * \code
121 * _metrics->add_group("groupname", {
122 * sm::make_gauge("metric_name", value, description("A documentation about the return value"))
123 * });
124 * \endcode
125 *
126 */
127class description {
128public:
129 description(sstring s = sstring()) : _s(std::move(s))
130 {}
131 const sstring& str() const {
132 return _s;
133 }
134private:
135 sstring _s;
136};
137
138/*!
139 * \brief Label a metrics
140 *
141 * Label are useful for adding information about a metric that
142 * later you would need to aggregate by.
143 * For example, if you have multiple queues on a shard.
144 * Adding the queue id as a Label will allow you to use the same name
145 * of the metrics with multiple id instances.
146 *
147 * label_instance holds an instance of label consist of a key and value.
148 *
149 * Typically you will not generate a label_instance yourself, but use a label
150 * object for that.
151 * @see label for more information
152 *
153 *
154 */
155class label_instance {
156 sstring _key;
157 sstring _value;
158public:
159 /*!
160 * \brief create a label_instance
161 * label instance consists of key and value.
162 * The key is an sstring.
163 * T - the value type can be any type that can be lexical_cast to string
164 * (ie. if it support the redirection operator for stringstream).
165 *
166 * All primitive types are supported so all the following examples are valid:
167 * label_instance a("smp_queue", 1)
168 * label_instance a("my_key", "my_value")
169 * label_instance a("internal_id", -1)
170 */
171 template<typename T>
172 label_instance(const sstring& key, T v) : _key(key), _value(boost::lexical_cast<std::string>(v)){}
173
174 /*!
175 * \brief returns the label key
176 */
177 const sstring key() const {
178 return _key;
179 }
180
181 /*!
182 * \brief returns the label value
183 */
184 const sstring value() const {
185 return _value;
186 }
187 bool operator<(const label_instance&) const;
188 bool operator==(const label_instance&) const;
189 bool operator!=(const label_instance&) const;
190};
191
192
193/*!
194 * \brief Class that creates label instances
195 *
196 * A factory class to create label instance
197 * Typically, the same Label name is used in multiple places.
198 * label is a label factory, you create it once, and use it to create the label_instance.
199 *
200 * In the example we would like to label the smp_queue with with the queue owner
201 *
202 * seastar::metrics::label smp_owner("smp_owner");
203 *
204 * now, when creating a new smp metric we can add a label to it:
205 *
206 * sm::make_queue_length("send_batch_queue_length", _last_snt_batch, {smp_owner(cpuid)})
207 *
208 * where cpuid in this case is unsiged.
209 */
210class label {
211 sstring key;
212public:
213 using instance = label_instance;
214 /*!
215 * \brief creating a label
216 * key is the label name, it will be the key for all label_instance
217 * that will be created from this label.
218 */
219 explicit label(const sstring& key) : key(key) {
220 }
221
222 /*!
223 * \brief creating a label instance
224 *
225 * Use the function operator to create a new label instance.
226 * T - the value type can be any type that can be lexical_cast to string
227 * (ie. if it support the redirection operator for stringstream).
228 *
229 * All primitive types are supported so if lab is a label, all the following examples are valid:
230 * lab(1)
231 * lab("my_value")
232 * lab(-1)
233 */
234 template<typename T>
235 instance operator()(T value) const {
236 return label_instance(key, std::forward<T>(value));
237 }
238
239 /*!
240 * \brief returns the label name
241 */
242 const sstring& name() const {
243 return key;
244 }
245};
246
247/*!
f67539c2 248 * \namespace impl
11fdf7f2
TL
249 * \brief holds the implementation parts of the metrics layer, do not use directly.
250 *
251 * The metrics layer define a thin API for adding metrics.
252 * Some of the implementation details need to be in the header file, they should not be use directly.
253 */
254namespace impl {
255
256// The value binding data types
257enum class data_type : uint8_t {
1e59de90
TL
258 COUNTER,
259 REAL_COUNTER,
260 GAUGE,
11fdf7f2 261 HISTOGRAM,
1e59de90
TL
262 SUMMARY,
263};
264
265template <bool callable, typename T>
266struct real_counter_type_traits {
267 using type = T;
268};
269
270template <typename T>
271struct real_counter_type_traits<true, T> {
272 using type = typename std::invoke_result<T>::type;
273};
274
275template <typename T>
276struct counter_type_traits {
277 using real_traits = real_counter_type_traits<std::is_invocable<T>::value, T>;
278 static constexpr bool is_integral = std::is_integral<typename real_traits::type>::value;
279 static constexpr data_type type = is_integral ? data_type::COUNTER : data_type::REAL_COUNTER;
11fdf7f2
TL
280};
281
282/*!
f67539c2 283 * \brief A helper class that used to return metrics value.
11fdf7f2
TL
284 *
285 * Do not use directly @see metrics_creation
286 */
20effc67
TL
287class metric_value {
288public:
f67539c2 289 std::variant<double, histogram> u;
11fdf7f2
TL
290 data_type _type;
291 data_type type() const {
292 return _type;
293 }
294
295 double d() const {
f67539c2 296 return std::get<double>(u);
11fdf7f2
TL
297 }
298
299 uint64_t ui() const {
20effc67
TL
300 auto d = std::get<double>(u);
301 if (d >= 0 && d <= double(std::numeric_limits<long>::max())) {
302 return lround(d);
303 } else {
304 // double value is out of range or NaN or Inf
305 ulong_conversion_error(d);
306 return 0;
307 }
11fdf7f2
TL
308 }
309
310 int64_t i() const {
20effc67
TL
311 auto d = std::get<double>(u);
312 if (d >= double(std::numeric_limits<long>::min()) && d <= double(std::numeric_limits<long>::max())) {
313 return lround(d);
314 } else {
315 // double value is out of range or NaN or Inf
316 ulong_conversion_error(d);
317 return 0;
318 }
11fdf7f2
TL
319 }
320
321 metric_value()
322 : _type(data_type::GAUGE) {
323 }
324
325 metric_value(histogram&& h, data_type t = data_type::HISTOGRAM) :
326 u(std::move(h)), _type(t) {
327 }
328 metric_value(const histogram& h, data_type t = data_type::HISTOGRAM) :
329 u(h), _type(t) {
330 }
331
332 metric_value(double d, data_type t)
333 : u(d), _type(t) {
334 }
335
336 metric_value& operator=(const metric_value& c) = default;
337
338 metric_value& operator+=(const metric_value& c) {
339 *this = *this + c;
340 return *this;
341 }
342
343 metric_value operator+(const metric_value& c);
344 const histogram& get_histogram() const {
f67539c2 345 return std::get<histogram>(u);
11fdf7f2 346 }
20effc67 347
1e59de90
TL
348 /*!
349 * \brief return true if this metric was never used
350 *
351 * Histograms, Summaries and counters are ever growing by nature, so
352 * it is possible to check if they have been used or not.
353 */
354 bool is_empty() const noexcept {
355 return ((_type == data_type::HISTOGRAM || _type == data_type::SUMMARY) && get_histogram().sample_count == 0) ||
356 ((_type == data_type::COUNTER || _type == data_type::REAL_COUNTER) && d() == 0);
357 }
20effc67
TL
358private:
359 static void ulong_conversion_error(double d);
11fdf7f2
TL
360};
361
362using metric_function = std::function<metric_value()>;
363
364struct metric_type {
365 data_type base_type;
366 metric_type_def type_name;
367};
368
369struct metric_definition_impl {
370 metric_name_type name;
371 metric_type type;
372 metric_function f;
373 description d;
374 bool enabled = true;
1e59de90
TL
375 skip_when_empty _skip_when_empty = skip_when_empty::no;
376 std::vector<std::string> aggregate_labels;
11fdf7f2
TL
377 std::map<sstring, sstring> labels;
378 metric_definition_impl& operator ()(bool enabled);
379 metric_definition_impl& operator ()(const label_instance& label);
1e59de90
TL
380 metric_definition_impl& operator ()(skip_when_empty skip) noexcept;
381 metric_definition_impl& aggregate(const std::vector<label>& labels) noexcept;
382 metric_definition_impl& set_skip_when_empty(bool skip=true) noexcept;
f67539c2 383 metric_definition_impl& set_type(const sstring& type_name);
11fdf7f2
TL
384 metric_definition_impl(
385 metric_name_type name,
386 metric_type type,
387 metric_function f,
388 description d,
1e59de90
TL
389 std::vector<label_instance> labels,
390 std::vector<label> aggregate_labels = {});
11fdf7f2
TL
391};
392
393class metric_groups_def {
394public:
395 metric_groups_def() = default;
396 virtual ~metric_groups_def() = default;
397 metric_groups_def(const metric_groups_def&) = delete;
398 metric_groups_def(metric_groups_def&&) = default;
399 virtual metric_groups_def& add_metric(group_name_type name, const metric_definition& md) = 0;
400 virtual metric_groups_def& add_group(group_name_type name, const std::initializer_list<metric_definition>& l) = 0;
401 virtual metric_groups_def& add_group(group_name_type name, const std::vector<metric_definition>& l) = 0;
402};
403
404instance_id_type shard();
405
1e59de90 406template<typename T, typename = std::enable_if_t<std::is_invocable_v<T>>>
11fdf7f2 407metric_function make_function(T val, data_type dt) {
1e59de90 408 return [dt, val = std::move(val)] {
11fdf7f2
TL
409 return metric_value(val(), dt);
410 };
411}
412
1e59de90 413template<typename T, typename = std::enable_if_t<!std::is_invocable_v<T>>>
11fdf7f2
TL
414metric_function make_function(T& val, data_type dt) {
415 return [dt, &val] {
416 return metric_value(val, dt);
417 };
418}
419}
420
421extern const bool metric_disabled;
422
423extern label shard_label;
11fdf7f2
TL
424
425/*
426 * The metrics definition are defined to be compatible with collectd metrics defintion.
427 * Typically you should used gauge or derived.
428 */
429
430
431/*!
432 * \brief Gauge are a general purpose metric.
433 *
434 * They can support floating point and can increase or decrease
435 */
436template<typename T>
437impl::metric_definition_impl make_gauge(metric_name_type name,
1e59de90 438 T&& val, description d = description(), std::vector<label_instance> labels = {}) {
11fdf7f2
TL
439 return {name, {impl::data_type::GAUGE, "gauge"}, make_function(std::forward<T>(val), impl::data_type::GAUGE), d, labels};
440}
441
442/*!
443 * \brief Gauge are a general purpose metric.
444 *
445 * They can support floating point and can increase or decrease
446 */
447template<typename T>
448impl::metric_definition_impl make_gauge(metric_name_type name,
449 description d, T&& val) {
450 return {name, {impl::data_type::GAUGE, "gauge"}, make_function(std::forward<T>(val), impl::data_type::GAUGE), d, {}};
451}
452
453/*!
454 * \brief Gauge are a general purpose metric.
455 *
456 * They can support floating point and can increase or decrease
457 */
458template<typename T>
459impl::metric_definition_impl make_gauge(metric_name_type name,
460 description d, std::vector<label_instance> labels, T&& val) {
461 return {name, {impl::data_type::GAUGE, "gauge"}, make_function(std::forward<T>(val), impl::data_type::GAUGE), d, labels};
462}
463
464
465/*!
466 * \brief Derive are used when a rate is more interesting than the value.
467 *
468 * Derive is an integer value that can increase or decrease, typically it is used when looking at the
469 * derivation of the value.
470 *
471 * It is OK to use it when counting things and if no wrap-around is expected (it shouldn't) it's prefer over counter metric.
472 */
473template<typename T>
1e59de90 474[[deprecated("Use make_counter()")]]
11fdf7f2 475impl::metric_definition_impl make_derive(metric_name_type name,
1e59de90
TL
476 T&& val, description d = description(), std::vector<label_instance> labels = {}) {
477 return make_counter(std::move(name), std::forward<T>(val), std::move(d), std::move(labels));
11fdf7f2
TL
478}
479
480
481/*!
482 * \brief Derive are used when a rate is more interesting than the value.
483 *
484 * Derive is an integer value that can increase or decrease, typically it is used when looking at the
485 * derivation of the value.
486 *
487 * It is OK to use it when counting things and if no wrap-around is expected (it shouldn't) it's prefer over counter metric.
488 */
489template<typename T>
1e59de90 490[[deprecated("Use make_counter()")]]
11fdf7f2
TL
491impl::metric_definition_impl make_derive(metric_name_type name, description d,
492 T&& val) {
1e59de90 493 return make_counter(std::move(name), std::forward<T>(val), std::move(d), {});
11fdf7f2
TL
494}
495
496
497/*!
498 * \brief Derive are used when a rate is more interesting than the value.
499 *
500 * Derive is an integer value that can increase or decrease, typically it is used when looking at the
501 * derivation of the value.
502 *
503 * It is OK to use it when counting things and if no wrap-around is expected (it shouldn't) it's prefer over counter metric.
504 */
505template<typename T>
1e59de90 506[[deprecated("Use make_counter()")]]
11fdf7f2
TL
507impl::metric_definition_impl make_derive(metric_name_type name, description d, std::vector<label_instance> labels,
508 T&& val) {
1e59de90 509 return make_counter(std::move(name), std::forward<T>(val), std::move(d), std::move(labels));
11fdf7f2
TL
510}
511
512
513/*!
514 * \brief create a counter metric
515 *
1e59de90
TL
516 * Counters are used when a rate is more interesting than the value, monitoring systems take
517 * derivation from it to display.
518 *
519 * It's an integer or floating point value that can increase or decrease.
11fdf7f2
TL
520 *
521 */
522template<typename T>
523impl::metric_definition_impl make_counter(metric_name_type name,
1e59de90
TL
524 T&& val, description d = description(), std::vector<label_instance> labels = {}) {
525 auto type = impl::counter_type_traits<std::remove_reference_t<T>>::type;
526 return {name, {type, "counter"}, make_function(std::forward<T>(val), type), d, labels};
527}
528
529/*!
530 * \brief create a counter metric
531 *
532 * Counters are used when a rate is more interesting than the value, monitoring systems take
533 * derivation from it to display.
534 *
535 * It's an integer or floating point value that can increase or decrease.
536 *
537 */
538template<typename T>
539impl::metric_definition_impl make_counter(metric_name_type name, description d, T&& val) {
540 return make_counter(std::move(name), std::forward<T>(val), std::move(d), {});
541}
542
543/*!
544 * \brief create a counter metric
545 *
546 * Counters are used when a rate is more interesting than the value, monitoring systems take
547 * derivation from it to display.
548 *
549 * It's an integer or floating point value that can increase or decrease.
550 *
551 */
552template<typename T>
553impl::metric_definition_impl make_counter(metric_name_type name, description d, std::vector<label_instance> labels, T&& val) {
554 return make_counter(std::move(name), std::forward<T>(val), std::move(d), std::move(labels));
11fdf7f2
TL
555}
556
557/*!
558 * \brief create an absolute metric.
559 *
560 * Absolute are used for metric that are being erased after each time they are read.
561 * They are here for compatibility reasons and should general be avoided in most applications.
562 */
563template<typename T>
1e59de90 564[[deprecated("Use make_counter()")]]
11fdf7f2 565impl::metric_definition_impl make_absolute(metric_name_type name,
1e59de90
TL
566 T&& val, description d = description(), std::vector<label_instance> labels = {}) {
567 return make_counter(std::move(name), std::forward<T>(val), std::move(d), std::move(labels));
11fdf7f2
TL
568}
569
570/*!
571 * \brief create a histogram metric.
572 *
573 * Histograms are a list o buckets with upper values and counter for the number
574 * of entries in each bucket.
575 */
576template<typename T>
577impl::metric_definition_impl make_histogram(metric_name_type name,
1e59de90 578 T&& val, description d = description(), std::vector<label_instance> labels = {}) {
11fdf7f2
TL
579 return {name, {impl::data_type::HISTOGRAM, "histogram"}, make_function(std::forward<T>(val), impl::data_type::HISTOGRAM), d, labels};
580}
581
582/*!
583 * \brief create a histogram metric.
584 *
585 * Histograms are a list o buckets with upper values and counter for the number
586 * of entries in each bucket.
587 */
588template<typename T>
589impl::metric_definition_impl make_histogram(metric_name_type name,
590 description d, std::vector<label_instance> labels, T&& val) {
591 return {name, {impl::data_type::HISTOGRAM, "histogram"}, make_function(std::forward<T>(val), impl::data_type::HISTOGRAM), d, labels};
592}
593
594
595/*!
596 * \brief create a histogram metric.
597 *
598 * Histograms are a list o buckets with upper values and counter for the number
599 * of entries in each bucket.
600 */
601template<typename T>
602impl::metric_definition_impl make_histogram(metric_name_type name,
603 description d, T&& val) {
604 return {name, {impl::data_type::HISTOGRAM, "histogram"}, make_function(std::forward<T>(val), impl::data_type::HISTOGRAM), d, {}};
605}
606
1e59de90
TL
607/*!
608 * \brief create a summary metric.
609 *
610 * Summaries are a different kind of histograms. It reports in quantiles.
611 * For example, the p99 and p95 latencies.
612 */
613template<typename T>
614impl::metric_definition_impl make_summary(metric_name_type name,
615 description d, T&& val) {
616 return {name, {impl::data_type::SUMMARY, "summary"}, make_function(std::forward<T>(val), impl::data_type::SUMMARY), d, {}};
617}
618
11fdf7f2
TL
619
620/*!
621 * \brief create a total_bytes metric.
622 *
623 * total_bytes are used for an ever growing counters, like the total bytes
624 * passed on a network.
625 */
626
627template<typename T>
628impl::metric_definition_impl make_total_bytes(metric_name_type name,
1e59de90
TL
629 T&& val, description d = description(), std::vector<label_instance> labels = {},
630 instance_id_type = impl::shard()) {
631 return make_counter(name, std::forward<T>(val), d, labels).set_type("total_bytes");
11fdf7f2
TL
632}
633
634/*!
635 * \brief create a current_bytes metric.
636 *
637 * current_bytes are used to report on current status in bytes.
638 * For example the current free memory.
639 */
640
641template<typename T>
642impl::metric_definition_impl make_current_bytes(metric_name_type name,
1e59de90
TL
643 T&& val, description d = description(), std::vector<label_instance> labels = {},
644 instance_id_type = impl::shard()) {
20effc67 645 return make_gauge(name, std::forward<T>(val), d, labels).set_type("bytes");
11fdf7f2
TL
646}
647
648
649/*!
650 * \brief create a queue_length metric.
651 *
652 * queue_length are used to report on queue length
653 */
654
655template<typename T>
656impl::metric_definition_impl make_queue_length(metric_name_type name,
1e59de90
TL
657 T&& val, description d = description(), std::vector<label_instance> labels = {},
658 instance_id_type = impl::shard()) {
f67539c2 659 return make_gauge(name, std::forward<T>(val), d, labels).set_type("queue_length");
11fdf7f2
TL
660}
661
662
663/*!
664 * \brief create a total operation metric.
665 *
666 * total_operations are used for ever growing operation counter.
667 */
668
669template<typename T>
670impl::metric_definition_impl make_total_operations(metric_name_type name,
1e59de90
TL
671 T&& val, description d = description(), std::vector<label_instance> labels = {},
672 instance_id_type = impl::shard()) {
673 return make_counter(name, std::forward<T>(val), d, labels).set_type("total_operations");
11fdf7f2
TL
674}
675
676/*! @} */
677}
678}