/root/doris/be/src/util/metrics.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. 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 | | #include "util/metrics.h" |
19 | | |
20 | | #include <glog/logging.h> |
21 | | #include <rapidjson/encodings.h> |
22 | | #include <rapidjson/stringbuffer.h> |
23 | | #include <rapidjson/writer.h> |
24 | | |
25 | | #include <initializer_list> |
26 | | |
27 | | #include "common/config.h" |
28 | | |
29 | | namespace doris { |
30 | | |
31 | 7 | std::ostream& operator<<(std::ostream& os, MetricType type) { |
32 | 7 | switch (type) { |
33 | 0 | case MetricType::COUNTER: |
34 | 0 | os << "counter"; |
35 | 0 | break; |
36 | 5 | case MetricType::GAUGE: |
37 | 5 | os << "gauge"; |
38 | 5 | break; |
39 | 2 | case MetricType::HISTOGRAM: |
40 | 2 | os << "histogram"; |
41 | 2 | break; |
42 | 0 | case MetricType::SUMMARY: |
43 | 0 | os << "summary"; |
44 | 0 | break; |
45 | 0 | case MetricType::UNTYPED: |
46 | 0 | os << "untyped"; |
47 | 0 | break; |
48 | 0 | default: |
49 | 0 | os << "unknown"; |
50 | 0 | break; |
51 | 7 | } |
52 | 7 | return os; |
53 | 7 | } |
54 | | |
55 | 8 | const char* unit_name(MetricUnit unit) { |
56 | 8 | switch (unit) { |
57 | 0 | case MetricUnit::NANOSECONDS: |
58 | 0 | return "nanoseconds"; |
59 | 0 | case MetricUnit::MICROSECONDS: |
60 | 0 | return "microseconds"; |
61 | 2 | case MetricUnit::MILLISECONDS: |
62 | 2 | return "milliseconds"; |
63 | 0 | case MetricUnit::SECONDS: |
64 | 0 | return "seconds"; |
65 | 0 | case MetricUnit::BYTES: |
66 | 0 | return "bytes"; |
67 | 0 | case MetricUnit::ROWS: |
68 | 0 | return "rows"; |
69 | 6 | case MetricUnit::PERCENT: |
70 | 6 | return "percent"; |
71 | 0 | case MetricUnit::REQUESTS: |
72 | 0 | return "requests"; |
73 | 0 | case MetricUnit::OPERATIONS: |
74 | 0 | return "operations"; |
75 | 0 | case MetricUnit::BLOCKS: |
76 | 0 | return "blocks"; |
77 | 0 | case MetricUnit::ROWSETS: |
78 | 0 | return "rowsets"; |
79 | 0 | case MetricUnit::CONNECTIONS: |
80 | 0 | return "rowsets"; |
81 | 0 | default: |
82 | 0 | return "nounit"; |
83 | 8 | } |
84 | 8 | } |
85 | | |
86 | 30 | std::string labels_to_string(std::initializer_list<const Labels*> multi_labels) { |
87 | 30 | bool all_empty = true; |
88 | 51 | for (const auto& labels : multi_labels) { |
89 | 51 | if (!labels->empty()) { |
90 | 22 | all_empty = false; |
91 | 22 | break; |
92 | 22 | } |
93 | 51 | } |
94 | 30 | if (all_empty) { |
95 | 8 | return std::string(); |
96 | 8 | } |
97 | | |
98 | 22 | std::stringstream ss; |
99 | 22 | ss << "{"; |
100 | 22 | int i = 0; |
101 | 54 | for (auto labels : multi_labels) { |
102 | 54 | for (const auto& label : *labels) { |
103 | 40 | if (i++ > 0) { |
104 | 18 | ss << ","; |
105 | 18 | } |
106 | 40 | ss << label.first << "=\"" << label.second << "\""; |
107 | 40 | } |
108 | 54 | } |
109 | 22 | ss << "}"; |
110 | | |
111 | 22 | return ss.str(); |
112 | 30 | } |
113 | | |
114 | | std::string Metric::to_prometheus(const std::string& display_name, const Labels& entity_labels, |
115 | 6 | const Labels& metric_labels) const { |
116 | 6 | std::stringstream ss; |
117 | 6 | ss << display_name // metric name |
118 | 6 | << labels_to_string({&entity_labels, &metric_labels}) // metric labels |
119 | 6 | << " " << to_string() << "\n"; // metric value |
120 | 6 | return ss.str(); |
121 | 6 | } |
122 | | |
123 | | std::map<std::string, double> HistogramMetric::_s_output_percentiles = { |
124 | | {"0.50", 50.0}, {"0.75", 75.0}, {"0.90", 90.0}, {"0.95", 95.0}, {"0.99", 99.0}}; |
125 | 0 | void HistogramMetric::clear() { |
126 | 0 | std::lock_guard<std::mutex> l(_lock); |
127 | 0 | _stats.clear(); |
128 | 0 | } |
129 | | |
130 | 0 | bool HistogramMetric::is_empty() const { |
131 | 0 | return _stats.is_empty(); |
132 | 0 | } |
133 | | |
134 | 200 | void HistogramMetric::add(const uint64_t& value) { |
135 | 200 | _stats.add(value); |
136 | 200 | } |
137 | | |
138 | 0 | void HistogramMetric::merge(const HistogramMetric& other) { |
139 | 0 | std::lock_guard<std::mutex> l(_lock); |
140 | 0 | _stats.merge(other._stats); |
141 | 0 | } |
142 | | |
143 | 0 | void HistogramMetric::set_histogram(const HistogramStat& stats) { |
144 | 0 | std::lock_guard<std::mutex> l(_lock); |
145 | 0 | _stats.clear(); |
146 | 0 | _stats.merge(stats); |
147 | 0 | } |
148 | | |
149 | 0 | double HistogramMetric::median() const { |
150 | 0 | return _stats.median(); |
151 | 0 | } |
152 | | |
153 | 0 | double HistogramMetric::percentile(double p) const { |
154 | 0 | return _stats.percentile(p); |
155 | 0 | } |
156 | | |
157 | 0 | double HistogramMetric::average() const { |
158 | 0 | return _stats.average(); |
159 | 0 | } |
160 | | |
161 | 0 | double HistogramMetric::standard_deviation() const { |
162 | 0 | return _stats.standard_deviation(); |
163 | 0 | } |
164 | | |
165 | 0 | std::string HistogramMetric::to_string() const { |
166 | 0 | return _stats.to_string(); |
167 | 0 | } |
168 | | |
169 | | std::string HistogramMetric::to_prometheus(const std::string& display_name, |
170 | | const Labels& entity_labels, |
171 | 2 | const Labels& metric_labels) const { |
172 | 2 | std::stringstream ss; |
173 | 10 | for (const auto& percentile : _s_output_percentiles) { |
174 | 10 | auto quantile_lable = Labels({{"quantile", percentile.first}}); |
175 | 10 | ss << display_name << labels_to_string({&entity_labels, &metric_labels, &quantile_lable}) |
176 | 10 | << " " << _stats.percentile(percentile.second) << "\n"; |
177 | 10 | } |
178 | 2 | ss << display_name << "_sum" << labels_to_string({&entity_labels, &metric_labels}) << " " |
179 | 2 | << _stats.sum() << "\n"; |
180 | 2 | ss << display_name << "_count" << labels_to_string({&entity_labels, &metric_labels}) << " " |
181 | 2 | << _stats.num() << "\n"; |
182 | 2 | ss << display_name << "_max" << labels_to_string({&entity_labels, &metric_labels}) << " " |
183 | 2 | << _stats.max() << "\n"; |
184 | 2 | ss << display_name << "_min" << labels_to_string({&entity_labels, &metric_labels}) << " " |
185 | 2 | << _stats.min() << "\n"; |
186 | 2 | ss << display_name << "_average" << labels_to_string({&entity_labels, &metric_labels}) << " " |
187 | 2 | << _stats.average() << "\n"; |
188 | 2 | ss << display_name << "_median" << labels_to_string({&entity_labels, &metric_labels}) << " " |
189 | 2 | << _stats.median() << "\n"; |
190 | 2 | ss << display_name << "_standard_deviation" |
191 | 2 | << labels_to_string({&entity_labels, &metric_labels}) << " " << _stats.standard_deviation() |
192 | 2 | << "\n"; |
193 | | |
194 | 2 | return ss.str(); |
195 | 2 | } |
196 | | |
197 | 2 | rj::Value HistogramMetric::to_json_value(rj::Document::AllocatorType& allocator) const { |
198 | 2 | rj::Value json_value(rj::kObjectType); |
199 | 2 | json_value.AddMember("total_count", rj::Value(_stats.num()), allocator); |
200 | 2 | json_value.AddMember("min", rj::Value(_stats.min()), allocator); |
201 | 2 | json_value.AddMember("average", rj::Value(_stats.average()), allocator); |
202 | 2 | json_value.AddMember("median", rj::Value(_stats.median()), allocator); |
203 | 10 | for (const auto& percentile : _s_output_percentiles) { |
204 | 10 | json_value.AddMember( |
205 | 10 | rj::Value(std::string("percentile_").append(percentile.first.substr(2)).c_str(), |
206 | 10 | allocator), |
207 | 10 | rj::Value(_stats.percentile(percentile.second)), allocator); |
208 | 10 | } |
209 | 2 | json_value.AddMember("standard_deviation", rj::Value(_stats.standard_deviation()), allocator); |
210 | 2 | json_value.AddMember("max", rj::Value(_stats.max()), allocator); |
211 | 2 | json_value.AddMember("total_sum", rj::Value(_stats.sum()), allocator); |
212 | | |
213 | 2 | return json_value; |
214 | 2 | } |
215 | | |
216 | 30 | std::string MetricPrototype::simple_name() const { |
217 | 30 | return group_name.empty() ? name : group_name; |
218 | 30 | } |
219 | | |
220 | 20 | std::string MetricPrototype::combine_name(const std::string& registry_name) const { |
221 | 20 | return (registry_name.empty() ? std::string() : registry_name + "_") + simple_name(); |
222 | 20 | } |
223 | | |
224 | 7 | std::string MetricPrototype::to_prometheus(const std::string& registry_name) const { |
225 | 7 | std::stringstream ss; |
226 | 7 | ss << "# TYPE " << combine_name(registry_name) << " " << type << "\n"; |
227 | 7 | return ss.str(); |
228 | 7 | } |
229 | | |
230 | 238 | void MetricEntity::deregister_metric(const MetricPrototype* metric_type) { |
231 | 238 | std::lock_guard<std::mutex> l(_lock); |
232 | 238 | auto metric = _metrics.find(metric_type); |
233 | 238 | if (metric != _metrics.end()) { |
234 | 212 | delete metric->second; |
235 | 212 | _metrics.erase(metric); |
236 | 212 | } |
237 | 238 | } |
238 | | |
239 | 74 | Metric* MetricEntity::get_metric(const std::string& name, const std::string& group_name) const { |
240 | 74 | MetricPrototype dummy(MetricType::UNTYPED, MetricUnit::NOUNIT, name, "", group_name); |
241 | 74 | std::lock_guard<std::mutex> l(_lock); |
242 | 74 | auto it = _metrics.find(&dummy); |
243 | 74 | if (it == _metrics.end()) { |
244 | 2 | return nullptr; |
245 | 2 | } |
246 | 72 | return it->second; |
247 | 74 | } |
248 | | |
249 | 644 | void MetricEntity::register_hook(const std::string& name, const std::function<void()>& hook) { |
250 | 644 | std::lock_guard<std::mutex> l(_lock); |
251 | | #ifndef BE_TEST |
252 | | DCHECK(_hooks.find(name) == _hooks.end()) << "hook is already exist! " << _name << ":" << name; |
253 | | #endif |
254 | 644 | _hooks.emplace(name, hook); |
255 | 644 | } |
256 | | |
257 | 508 | void MetricEntity::deregister_hook(const std::string& name) { |
258 | 508 | std::lock_guard<std::mutex> l(_lock); |
259 | 508 | _hooks.erase(name); |
260 | 508 | } |
261 | | |
262 | 22 | void MetricEntity::trigger_hook_unlocked(bool force) const { |
263 | | // When 'enable_metric_calculator' is true, hooks will be triggered by a background thread, |
264 | | // see 'calculate_metrics' in daemon.cpp for more details. |
265 | 22 | if (!force && config::enable_metric_calculator) { |
266 | 19 | return; |
267 | 19 | } |
268 | 3 | for (const auto& hook : _hooks) { |
269 | 2 | hook.second(); |
270 | 2 | } |
271 | 3 | } |
272 | | |
273 | 6 | MetricRegistry::~MetricRegistry() {} |
274 | | |
275 | | std::shared_ptr<MetricEntity> MetricRegistry::register_entity(const std::string& name, |
276 | | const Labels& labels, |
277 | 728 | MetricEntityType type) { |
278 | 728 | std::shared_ptr<MetricEntity> entity = std::make_shared<MetricEntity>(type, name, labels); |
279 | 728 | std::lock_guard<std::mutex> l(_lock); |
280 | 728 | auto inserted_entity = _entities.insert(std::make_pair(entity, 1)); |
281 | 728 | if (!inserted_entity.second) { |
282 | | // If exist, increase the registered count |
283 | 304 | inserted_entity.first->second++; |
284 | 304 | } |
285 | 728 | return inserted_entity.first->first; |
286 | 728 | } |
287 | | |
288 | 656 | void MetricRegistry::deregister_entity(const std::shared_ptr<MetricEntity>& entity) { |
289 | 656 | std::lock_guard<std::mutex> l(_lock); |
290 | 656 | auto found_entity = _entities.find(entity); |
291 | 656 | if (found_entity != _entities.end()) { |
292 | | // Decrease the registered count |
293 | 656 | --found_entity->second; |
294 | 656 | if (found_entity->second == 0) { |
295 | | // Only erase it when registered count is zero |
296 | 372 | _entities.erase(found_entity); |
297 | 372 | } |
298 | 656 | } |
299 | 656 | } |
300 | | |
301 | | std::shared_ptr<MetricEntity> MetricRegistry::get_entity(const std::string& name, |
302 | | const Labels& labels, |
303 | 10 | MetricEntityType type) { |
304 | 10 | std::shared_ptr<MetricEntity> dummy = std::make_shared<MetricEntity>(type, name, labels); |
305 | | |
306 | 10 | std::lock_guard<std::mutex> l(_lock); |
307 | 10 | auto entity = _entities.find(dummy); |
308 | 10 | if (entity == _entities.end()) { |
309 | 2 | return std::shared_ptr<MetricEntity>(); |
310 | 2 | } |
311 | 8 | return entity->first; |
312 | 10 | } |
313 | | |
314 | 0 | void MetricRegistry::trigger_all_hooks(bool force) const { |
315 | 0 | std::lock_guard<std::mutex> l(_lock); |
316 | 0 | for (const auto& entity : _entities) { |
317 | 0 | std::lock_guard<std::mutex> l(entity.first->_lock); |
318 | 0 | entity.first->trigger_hook_unlocked(force); |
319 | 0 | } |
320 | 0 | } |
321 | | |
322 | 10 | std::string MetricRegistry::to_prometheus(bool with_tablet_metrics) const { |
323 | | // Reorder by MetricPrototype |
324 | 10 | EntityMetricsByType entity_metrics_by_types; |
325 | 10 | std::lock_guard<std::mutex> l(_lock); |
326 | 10 | for (const auto& entity : _entities) { |
327 | 7 | if (entity.first->_type == MetricEntityType::kTablet && !with_tablet_metrics) { |
328 | 0 | continue; |
329 | 0 | } |
330 | 7 | std::lock_guard<std::mutex> l(entity.first->_lock); |
331 | 7 | entity.first->trigger_hook_unlocked(false); |
332 | 8 | for (const auto& metric : entity.first->_metrics) { |
333 | 8 | std::pair<MetricEntity*, Metric*> new_elem = |
334 | 8 | std::make_pair(entity.first.get(), metric.second); |
335 | 8 | auto found = entity_metrics_by_types.find(metric.first); |
336 | 8 | if (found == entity_metrics_by_types.end()) { |
337 | 8 | entity_metrics_by_types.emplace( |
338 | 8 | metric.first, std::vector<std::pair<MetricEntity*, Metric*>>({new_elem})); |
339 | 8 | } else { |
340 | 0 | found->second.emplace_back(new_elem); |
341 | 0 | } |
342 | 8 | } |
343 | 7 | } |
344 | | |
345 | | // Output |
346 | 10 | std::stringstream ss; |
347 | 10 | std::string last_group_name; |
348 | 10 | for (const auto& entity_metrics_by_type : entity_metrics_by_types) { |
349 | 8 | if (last_group_name.empty() || |
350 | 8 | last_group_name != entity_metrics_by_type.first->group_name) { |
351 | 7 | ss << entity_metrics_by_type.first->to_prometheus(_name); // metric TYPE line |
352 | 7 | } |
353 | 8 | last_group_name = entity_metrics_by_type.first->group_name; |
354 | 8 | std::string display_name = entity_metrics_by_type.first->combine_name(_name); |
355 | 8 | for (const auto& entity_metric : entity_metrics_by_type.second) { |
356 | 8 | ss << entity_metric.second->to_prometheus(display_name, // metric key-value line |
357 | 8 | entity_metric.first->_labels, |
358 | 8 | entity_metrics_by_type.first->labels); |
359 | 8 | } |
360 | 8 | } |
361 | | |
362 | 10 | return ss.str(); |
363 | 10 | } |
364 | | |
365 | 9 | std::string MetricRegistry::to_json(bool with_tablet_metrics) const { |
366 | 9 | rj::Document doc {rj::kArrayType}; |
367 | 9 | rj::Document::AllocatorType& allocator = doc.GetAllocator(); |
368 | 9 | std::lock_guard<std::mutex> l(_lock); |
369 | 9 | for (const auto& entity : _entities) { |
370 | 7 | if (entity.first->_type == MetricEntityType::kTablet && !with_tablet_metrics) { |
371 | 0 | continue; |
372 | 0 | } |
373 | 7 | std::lock_guard<std::mutex> l(entity.first->_lock); |
374 | 7 | entity.first->trigger_hook_unlocked(false); |
375 | 8 | for (const auto& metric : entity.first->_metrics) { |
376 | 8 | rj::Value metric_obj(rj::kObjectType); |
377 | | // tags |
378 | 8 | rj::Value tag_obj(rj::kObjectType); |
379 | 8 | tag_obj.AddMember("metric", rj::Value(metric.first->simple_name().c_str(), allocator), |
380 | 8 | allocator); |
381 | | // MetricPrototype's labels |
382 | 8 | for (auto& label : metric.first->labels) { |
383 | 5 | tag_obj.AddMember(rj::Value(label.first.c_str(), allocator), |
384 | 5 | rj::Value(label.second.c_str(), allocator), allocator); |
385 | 5 | } |
386 | | // MetricEntity's labels |
387 | 8 | for (auto& label : entity.first->_labels) { |
388 | 3 | tag_obj.AddMember(rj::Value(label.first.c_str(), allocator), |
389 | 3 | rj::Value(label.second.c_str(), allocator), allocator); |
390 | 3 | } |
391 | 8 | metric_obj.AddMember("tags", tag_obj, allocator); |
392 | | // unit |
393 | 8 | rj::Value unit_val(unit_name(metric.first->unit), allocator); |
394 | 8 | metric_obj.AddMember("unit", unit_val, allocator); |
395 | | // value |
396 | 8 | metric_obj.AddMember("value", metric.second->to_json_value(allocator), allocator); |
397 | 8 | doc.PushBack(metric_obj, allocator); |
398 | 8 | } |
399 | 7 | } |
400 | | |
401 | 9 | rj::StringBuffer strBuf; |
402 | 9 | rj::Writer<rj::StringBuffer> writer(strBuf); |
403 | 9 | doc.Accept(writer); |
404 | 9 | return strBuf.GetString(); |
405 | 9 | } |
406 | | |
407 | 7 | std::string MetricRegistry::to_core_string() const { |
408 | 7 | std::stringstream ss; |
409 | 7 | std::lock_guard<std::mutex> l(_lock); |
410 | 7 | for (const auto& entity : _entities) { |
411 | 5 | std::lock_guard<std::mutex> l(entity.first->_lock); |
412 | 5 | entity.first->trigger_hook_unlocked(false); |
413 | 6 | for (const auto& metric : entity.first->_metrics) { |
414 | 6 | if (metric.first->is_core_metric) { |
415 | 1 | ss << metric.first->combine_name(_name) << " LONG " << metric.second->to_string() |
416 | 1 | << "\n"; |
417 | 1 | } |
418 | 6 | } |
419 | 5 | } |
420 | | |
421 | 7 | return ss.str(); |
422 | 7 | } |
423 | | |
424 | | } // namespace doris |