]>
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 <seastar/core/metrics.hh> | |
25 | #include <unordered_map> | |
26 | #include <seastar/core/sharded.hh> | |
27 | #include <boost/functional/hash.hpp> | |
1e59de90 | 28 | |
11fdf7f2 TL |
29 | /*! |
30 | * \file metrics_api.hh | |
1e59de90 | 31 | * \brief header file for metric API layer (like prometheus or collectd) |
11fdf7f2 TL |
32 | * |
33 | * | |
34 | * | |
35 | */ | |
36 | namespace seastar { | |
37 | namespace metrics { | |
38 | namespace impl { | |
39 | ||
40 | using labels_type = std::map<sstring, sstring>; | |
41 | } | |
42 | } | |
43 | } | |
44 | ||
45 | namespace std { | |
46 | ||
47 | template<> | |
48 | struct hash<seastar::metrics::impl::labels_type> { | |
49 | using argument_type = seastar::metrics::impl::labels_type; | |
50 | using result_type = ::std::size_t; | |
51 | result_type operator()(argument_type const& s) const { | |
52 | result_type h = 0; | |
53 | for (auto&& i : s) { | |
54 | boost::hash_combine(h, std::hash<seastar::sstring>{}(i.second)); | |
55 | } | |
56 | return h; | |
57 | } | |
58 | }; | |
59 | ||
60 | } | |
61 | ||
62 | namespace seastar { | |
63 | namespace metrics { | |
1e59de90 TL |
64 | struct relabel_config; |
65 | ||
66 | /*! | |
67 | * \brief result of metric relabeling | |
68 | * | |
69 | * The result of calling set_relabel_configs. | |
70 | * | |
71 | * metrics_relabeled_due_to_collision the number of metrics that caused conflict | |
72 | * and were relabeled to avoid name collision. | |
73 | * | |
74 | * Non zero value indicates there were name collisions. | |
75 | * | |
76 | */ | |
77 | struct metric_relabeling_result { | |
78 | size_t metrics_relabeled_due_to_collision; | |
79 | }; | |
80 | ||
11fdf7f2 TL |
81 | namespace impl { |
82 | ||
83 | /** | |
84 | * Metrics are collected in groups that belongs to some logical entity. | |
85 | * For example, different measurements of the cpu, will belong to group "cpu". | |
86 | * | |
87 | * Name is the metric name like used_objects or used_bytes | |
88 | * | |
89 | * Inherit type allows customizing one of the basic types (gauge, counter, derive). | |
90 | * | |
91 | * Instance_id is used to differentiate multiple instance of the metrics. | |
92 | * In the seastar environment it is typical to have a metric per shard. | |
93 | * | |
94 | */ | |
95 | ||
96 | class metric_id { | |
97 | public: | |
98 | metric_id() = default; | |
99 | metric_id(group_name_type group, metric_name_type name, | |
100 | labels_type labels = {}) | |
101 | : _group(std::move(group)), _name( | |
102 | std::move(name)), _labels(labels) { | |
103 | } | |
104 | metric_id(metric_id &&) = default; | |
105 | metric_id(const metric_id &) = default; | |
106 | ||
107 | metric_id & operator=(metric_id &&) = default; | |
108 | metric_id & operator=(const metric_id &) = default; | |
109 | ||
110 | const group_name_type & group_name() const { | |
111 | return _group; | |
112 | } | |
113 | void group_name(const group_name_type & name) { | |
114 | _group = name; | |
115 | } | |
116 | const instance_id_type & instance_id() const { | |
117 | return _labels.at(shard_label.name()); | |
118 | } | |
119 | const metric_name_type & name() const { | |
120 | return _name; | |
121 | } | |
11fdf7f2 TL |
122 | const labels_type& labels() const { |
123 | return _labels; | |
124 | } | |
1e59de90 TL |
125 | labels_type& labels() { |
126 | return _labels; | |
127 | } | |
11fdf7f2 TL |
128 | sstring full_name() const; |
129 | ||
130 | bool operator<(const metric_id&) const; | |
131 | bool operator==(const metric_id&) const; | |
132 | private: | |
133 | auto as_tuple() const { | |
f67539c2 | 134 | return std::tie(group_name(), instance_id(), name(), labels()); |
11fdf7f2 TL |
135 | } |
136 | group_name_type _group; | |
137 | metric_name_type _name; | |
138 | labels_type _labels; | |
139 | }; | |
140 | } | |
141 | } | |
142 | } | |
143 | ||
144 | namespace std { | |
145 | ||
146 | template<> | |
147 | struct hash<seastar::metrics::impl::metric_id> | |
148 | { | |
149 | typedef seastar::metrics::impl::metric_id argument_type; | |
150 | typedef ::std::size_t result_type; | |
151 | result_type operator()(argument_type const& s) const | |
152 | { | |
153 | result_type const h1 ( std::hash<seastar::sstring>{}(s.group_name()) ); | |
154 | result_type const h2 ( std::hash<seastar::sstring>{}(s.instance_id()) ); | |
155 | return h1 ^ (h2 << 1); // or use boost::hash_combine | |
156 | } | |
157 | }; | |
158 | ||
159 | } | |
160 | ||
161 | namespace seastar { | |
162 | namespace metrics { | |
163 | namespace impl { | |
164 | ||
165 | /*! | |
166 | * \brief holds metadata information of a metric family | |
167 | * | |
168 | * Holds the information that is shared between all metrics | |
169 | * that belongs to the same metric_family | |
170 | */ | |
171 | struct metric_family_info { | |
172 | data_type type; | |
f67539c2 | 173 | metric_type_def inherit_type; |
11fdf7f2 TL |
174 | description d; |
175 | sstring name; | |
1e59de90 | 176 | std::vector<std::string> aggregate_labels; |
11fdf7f2 TL |
177 | }; |
178 | ||
179 | ||
180 | /*! | |
181 | * \brief holds metric metadata | |
182 | */ | |
183 | struct metric_info { | |
184 | metric_id id; | |
1e59de90 | 185 | labels_type original_labels; |
11fdf7f2 | 186 | bool enabled; |
1e59de90 | 187 | skip_when_empty should_skip_when_empty; |
11fdf7f2 TL |
188 | }; |
189 | ||
190 | ||
191 | using metrics_registration = std::vector<metric_id>; | |
192 | ||
193 | class metric_groups_impl : public metric_groups_def { | |
194 | metrics_registration _registration; | |
195 | public: | |
196 | metric_groups_impl() = default; | |
197 | ~metric_groups_impl(); | |
198 | metric_groups_impl(const metric_groups_impl&) = delete; | |
199 | metric_groups_impl(metric_groups_impl&&) = default; | |
200 | metric_groups_impl& add_metric(group_name_type name, const metric_definition& md); | |
201 | metric_groups_impl& add_group(group_name_type name, const std::initializer_list<metric_definition>& l); | |
202 | metric_groups_impl& add_group(group_name_type name, const std::vector<metric_definition>& l); | |
203 | }; | |
204 | ||
205 | class impl; | |
206 | ||
207 | class registered_metric { | |
208 | metric_info _info; | |
209 | metric_function _f; | |
210 | shared_ptr<impl> _impl; | |
211 | public: | |
1e59de90 | 212 | registered_metric(metric_id id, metric_function f, bool enabled=true, skip_when_empty skip=skip_when_empty::no); |
11fdf7f2 TL |
213 | virtual ~registered_metric() {} |
214 | virtual metric_value operator()() const { | |
215 | return _f(); | |
216 | } | |
217 | ||
218 | bool is_enabled() const { | |
219 | return _info.enabled; | |
220 | } | |
221 | ||
222 | void set_enabled(bool b) { | |
223 | _info.enabled = b; | |
224 | } | |
1e59de90 TL |
225 | void set_skip_when_empty(skip_when_empty skip) noexcept { |
226 | _info.should_skip_when_empty = skip; | |
227 | } | |
11fdf7f2 TL |
228 | const metric_id& get_id() const { |
229 | return _info.id; | |
230 | } | |
231 | ||
232 | const metric_info& info() const { | |
233 | return _info; | |
234 | } | |
1e59de90 TL |
235 | metric_info& info() { |
236 | return _info; | |
237 | } | |
11fdf7f2 TL |
238 | metric_function& get_function() { |
239 | return _f; | |
240 | } | |
241 | }; | |
242 | ||
243 | using register_ref = shared_ptr<registered_metric>; | |
244 | using metric_instances = std::map<labels_type, register_ref>; | |
245 | ||
246 | class metric_family { | |
247 | metric_instances _instances; | |
248 | metric_family_info _info; | |
249 | public: | |
250 | using iterator = metric_instances::iterator; | |
251 | using const_iterator = metric_instances::const_iterator; | |
252 | ||
253 | metric_family() = default; | |
254 | metric_family(const metric_family&) = default; | |
255 | metric_family(const metric_instances& instances) : _instances(instances) { | |
256 | } | |
257 | metric_family(const metric_instances& instances, const metric_family_info& info) : _instances(instances), _info(info) { | |
258 | } | |
259 | metric_family(metric_instances&& instances, metric_family_info&& info) : _instances(std::move(instances)), _info(std::move(info)) { | |
260 | } | |
261 | metric_family(metric_instances&& instances) : _instances(std::move(instances)) { | |
262 | } | |
263 | ||
264 | register_ref& operator[](const labels_type& l) { | |
265 | return _instances[l]; | |
266 | } | |
267 | ||
268 | const register_ref& at(const labels_type& l) const { | |
269 | return _instances.at(l); | |
270 | } | |
271 | ||
272 | metric_family_info& info() { | |
273 | return _info; | |
274 | } | |
275 | ||
276 | const metric_family_info& info() const { | |
277 | return _info; | |
278 | } | |
279 | ||
280 | iterator find(const labels_type& l) { | |
281 | return _instances.find(l); | |
282 | } | |
283 | ||
284 | const_iterator find(const labels_type& l) const { | |
285 | return _instances.find(l); | |
286 | } | |
287 | ||
288 | iterator begin() { | |
289 | return _instances.begin(); | |
290 | } | |
291 | ||
292 | const_iterator begin() const { | |
293 | return _instances.cbegin(); | |
294 | } | |
295 | ||
296 | iterator end() { | |
297 | return _instances.end(); | |
298 | } | |
299 | ||
300 | bool empty() const { | |
301 | return _instances.empty(); | |
302 | } | |
303 | ||
304 | iterator erase(const_iterator position) { | |
305 | return _instances.erase(position); | |
306 | } | |
307 | ||
308 | const_iterator end() const { | |
309 | return _instances.cend(); | |
310 | } | |
311 | ||
312 | uint32_t size() const { | |
313 | return _instances.size(); | |
314 | } | |
315 | ||
316 | }; | |
317 | ||
318 | using value_map = std::map<sstring, metric_family>; | |
319 | ||
320 | using metric_metadata_vector = std::vector<metric_info>; | |
321 | ||
322 | /*! | |
323 | * \brief holds a metric family metadata | |
324 | * | |
325 | * The meta data of a metric family compose of the | |
326 | * metadata of the family, and a vector of the metadata for | |
327 | * each of the metric. | |
328 | */ | |
329 | struct metric_family_metadata { | |
330 | metric_family_info mf; | |
331 | metric_metadata_vector metrics; | |
332 | }; | |
333 | ||
334 | using value_vector = std::vector<metric_value>; | |
335 | using metric_metadata = std::vector<metric_family_metadata>; | |
336 | using metric_values = std::vector<value_vector>; | |
337 | ||
338 | struct values_copy { | |
339 | shared_ptr<metric_metadata> metadata; | |
340 | metric_values values; | |
341 | }; | |
342 | ||
343 | struct config { | |
344 | sstring hostname; | |
345 | }; | |
346 | ||
347 | class impl { | |
348 | value_map _value_map; | |
349 | config _config; | |
350 | bool _dirty = true; | |
351 | shared_ptr<metric_metadata> _metadata; | |
1e59de90 | 352 | std::set<sstring> _labels; |
11fdf7f2 | 353 | std::vector<std::vector<metric_function>> _current_metrics; |
1e59de90 | 354 | std::vector<relabel_config> _relabel_configs; |
11fdf7f2 TL |
355 | public: |
356 | value_map& get_value_map() { | |
357 | return _value_map; | |
358 | } | |
359 | ||
360 | const value_map& get_value_map() const { | |
361 | return _value_map; | |
362 | } | |
363 | ||
1e59de90 | 364 | void add_registration(const metric_id& id, const metric_type& type, metric_function f, const description& d, bool enabled, skip_when_empty skip, const std::vector<std::string>& aggregate_labels); |
11fdf7f2 TL |
365 | void remove_registration(const metric_id& id); |
366 | future<> stop() { | |
367 | return make_ready_future<>(); | |
368 | } | |
369 | const config& get_config() const { | |
370 | return _config; | |
371 | } | |
372 | void set_config(const config& c) { | |
373 | _config = c; | |
374 | } | |
375 | ||
376 | shared_ptr<metric_metadata> metadata(); | |
377 | ||
378 | std::vector<std::vector<metric_function>>& functions(); | |
379 | ||
380 | void update_metrics_if_needed(); | |
381 | ||
382 | void dirty() { | |
383 | _dirty = true; | |
384 | } | |
1e59de90 TL |
385 | |
386 | const std::set<sstring>& get_labels() const noexcept { | |
387 | return _labels; | |
388 | } | |
389 | ||
390 | future<metric_relabeling_result> set_relabel_configs(const std::vector<relabel_config>& relabel_configs); | |
391 | ||
392 | const std::vector<relabel_config>& get_relabel_configs() const noexcept { | |
393 | return _relabel_configs; | |
394 | } | |
11fdf7f2 TL |
395 | }; |
396 | ||
397 | const value_map& get_value_map(); | |
398 | using values_reference = shared_ptr<values_copy>; | |
399 | ||
400 | foreign_ptr<values_reference> get_values(); | |
401 | ||
402 | shared_ptr<impl> get_local_impl(); | |
403 | ||
404 | void unregister_metric(const metric_id & id); | |
405 | ||
406 | /*! | |
407 | * \brief initialize metric group | |
408 | * | |
409 | * Create a metric_group_def. | |
410 | * No need to use it directly. | |
411 | */ | |
412 | std::unique_ptr<metric_groups_def> create_metric_groups(); | |
413 | ||
414 | } | |
20effc67 TL |
415 | |
416 | /// Metrics configuration options. | |
417 | struct options : public program_options::option_group { | |
418 | /// \brief The hostname used by the metrics. | |
419 | /// | |
420 | /// If not set, the local hostname will be used. | |
421 | program_options::value<std::string> metrics_hostname; | |
422 | ||
423 | options(program_options::option_group* parent_group); | |
424 | }; | |
11fdf7f2 TL |
425 | |
426 | /*! | |
20effc67 | 427 | * \brief set the metrics configuration |
11fdf7f2 | 428 | */ |
20effc67 | 429 | future<> configure(const options& opts); |
11fdf7f2 | 430 | |
1e59de90 TL |
431 | /*! |
432 | * \brief Perform relabeling and operation on metrics dynamically. | |
433 | * | |
434 | * The function would return true if the changes were applied with no conflict | |
435 | * or false, if there was a conflict in the registration. | |
436 | * | |
437 | * The general logic follows Prometheus metrics_relabel_config configuration. | |
438 | * The relabel rules are applied one after the other. | |
439 | * You can add or change a label. you can enable or disable a metric, | |
440 | * in that case the metrics will not be reported at all. | |
441 | * You can turn on and off the skip_when_empty flag. | |
442 | * | |
443 | * Using the Prometheus convention, the metric name is __name__. | |
444 | * Names cannot be changed. | |
445 | * | |
446 | * Import notes: | |
447 | * - The relabeling always starts from the original set of labels the metric | |
448 | * was created with. | |
449 | * - calling with an empty set will remove the relabel config and will | |
450 | * return all metrics to their original labels | |
451 | * - To prevent a situation that calling this function would crash the system. | |
452 | * in a situation where a conflicting metrics name are entered, an additional label | |
453 | * will be added to the labels with a unique ID. | |
454 | * | |
455 | * A few examples: | |
456 | * To add a level label with a value 1, to the reactor_utilization metric: | |
457 | * std::vector<sm::relabel_config> rl(1); | |
458 | rl[0].source_labels = {"__name__"}; | |
459 | rl[0].target_label = "level"; | |
460 | rl[0].replacement = "1"; | |
461 | rl[0].expr = "reactor_utilization"; | |
462 | set_relabel_configs(rl); | |
463 | * | |
464 | * To report only the metrics with the level label equals 1 | |
465 | * | |
466 | std::vector<sm::relabel_config> rl(2); | |
467 | rl[0].source_labels = {"__name__"}; | |
468 | rl[0].action = sm::relabel_config::relabel_action::drop; | |
469 | ||
470 | rl[1].source_labels = {"level"}; | |
471 | rl[1].expr = "1"; | |
472 | rl[1].action = sm::relabel_config::relabel_action::keep; | |
473 | set_relabel_configs(rl); | |
474 | ||
475 | */ | |
476 | future<metric_relabeling_result> set_relabel_configs(const std::vector<relabel_config>& relabel_configs); | |
477 | /* | |
478 | * \brief return the current relabel_configs | |
479 | * This function returns a vector of the current relabel configs | |
480 | */ | |
481 | const std::vector<relabel_config>& get_relabel_configs(); | |
482 | ||
11fdf7f2 TL |
483 | } |
484 | } |