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