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