Coverage Report

Created: 2024-11-22 12:06

/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