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