Coverage Report

Created: 2025-03-27 13:57

/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
292
std::ostream& operator<<(std::ostream& os, MetricType type) {
32
292
    switch (type) {
33
171
    case MetricType::COUNTER:
34
171
        os << "counter";
35
171
        break;
36
116
    case MetricType::GAUGE:
37
116
        os << "gauge";
38
116
        break;
39
5
    case MetricType::HISTOGRAM:
40
5
        os << "histogram";
41
5
        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
292
    }
52
292
    return os;
53
292
}
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
618
std::string labels_to_string(std::initializer_list<const Labels*> multi_labels) {
87
618
    bool all_empty = true;
88
1.02k
    for (const auto& labels : multi_labels) {
89
1.02k
        if (!labels->empty()) {
90
382
            all_empty = false;
91
382
            break;
92
382
        }
93
1.02k
    }
94
618
    if (all_empty) {
95
236
        return std::string();
96
236
    }
97
98
382
    std::stringstream ss;
99
382
    ss << "{";
100
382
    int i = 0;
101
789
    for (auto labels : multi_labels) {
102
789
        for (const auto& label : *labels) {
103
646
            if (i++ > 0) {
104
264
                ss << ",";
105
264
            }
106
646
            ss << label.first << "=\"" << label.second << "\"";
107
646
        }
108
789
    }
109
382
    ss << "}";
110
111
382
    return ss.str();
112
618
}
113
114
std::string Metric::to_prometheus(const std::string& display_name, const Labels& entity_labels,
115
558
                                  const Labels& metric_labels) const {
116
558
    std::stringstream ss;
117
558
    ss << display_name                                       // metric name
118
558
       << labels_to_string({&entity_labels, &metric_labels}) // metric labels
119
558
       << " " << to_string() << "\n";                        // metric value
120
558
    return ss.str();
121
558
}
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
5
                                           const Labels& metric_labels) const {
172
    // TODO: Use std::string concate for better performance.
173
5
    std::stringstream ss;
174
25
    for (const auto& percentile : _s_output_percentiles) {
175
25
        auto quantile_lable = Labels({{"quantile", percentile.first}});
176
25
        ss << display_name << labels_to_string({&entity_labels, &metric_labels, &quantile_lable})
177
25
           << " " << _stats.percentile(percentile.second) << "\n";
178
25
    }
179
5
    ss << display_name << "_sum" << labels_to_string({&entity_labels, &metric_labels}) << " "
180
5
       << _stats.sum() << "\n";
181
5
    ss << display_name << "_count" << labels_to_string({&entity_labels, &metric_labels}) << " "
182
5
       << _stats.num() << "\n";
183
5
    ss << display_name << "_max" << labels_to_string({&entity_labels, &metric_labels}) << " "
184
5
       << _stats.max() << "\n";
185
5
    ss << display_name << "_min" << labels_to_string({&entity_labels, &metric_labels}) << " "
186
5
       << _stats.min() << "\n";
187
5
    ss << display_name << "_average" << labels_to_string({&entity_labels, &metric_labels}) << " "
188
5
       << _stats.average() << "\n";
189
5
    ss << display_name << "_median" << labels_to_string({&entity_labels, &metric_labels}) << " "
190
5
       << _stats.median() << "\n";
191
5
    ss << display_name << "_standard_deviation"
192
5
       << labels_to_string({&entity_labels, &metric_labels}) << " " << _stats.standard_deviation()
193
5
       << "\n";
194
195
5
    return ss.str();
196
5
}
197
198
2
rj::Value HistogramMetric::to_json_value(rj::Document::AllocatorType& allocator) const {
199
2
    rj::Value json_value(rj::kObjectType);
200
2
    json_value.AddMember("total_count", rj::Value(_stats.num()), allocator);
201
2
    json_value.AddMember("min", rj::Value(_stats.min()), allocator);
202
2
    json_value.AddMember("average", rj::Value(_stats.average()), allocator);
203
2
    json_value.AddMember("median", rj::Value(_stats.median()), allocator);
204
10
    for (const auto& percentile : _s_output_percentiles) {
205
10
        json_value.AddMember(
206
10
                rj::Value(std::string("percentile_").append(percentile.first.substr(2)).c_str(),
207
10
                          allocator),
208
10
                rj::Value(_stats.percentile(percentile.second)), allocator);
209
10
    }
210
2
    json_value.AddMember("standard_deviation", rj::Value(_stats.standard_deviation()), allocator);
211
2
    json_value.AddMember("max", rj::Value(_stats.max()), allocator);
212
2
    json_value.AddMember("total_sum", rj::Value(_stats.sum()), allocator);
213
214
2
    return json_value;
215
2
}
216
217
708
std::string MetricPrototype::simple_name() const {
218
708
    return group_name.empty() ? name : group_name;
219
708
}
220
221
698
std::string MetricPrototype::combine_name(const std::string& registry_name) const {
222
698
    return (registry_name.empty() ? std::string() : registry_name + "_") + simple_name();
223
698
}
224
225
292
std::string MetricPrototype::to_prometheus(const std::string& registry_name) const {
226
292
    std::stringstream ss;
227
292
    ss << "# TYPE " << combine_name(registry_name) << " " << type << "\n";
228
292
    return ss.str();
229
292
}
230
231
43
void MetricEntity::deregister_metric(const MetricPrototype* metric_type) {
232
43
    std::lock_guard<std::mutex> l(_lock);
233
43
    auto metric = _metrics.find(metric_type);
234
43
    if (metric != _metrics.end()) {
235
17
        delete metric->second;
236
17
        _metrics.erase(metric);
237
17
    }
238
43
}
239
240
74
Metric* MetricEntity::get_metric(const std::string& name, const std::string& group_name) const {
241
74
    MetricPrototype dummy(MetricType::UNTYPED, MetricUnit::NOUNIT, name, "", group_name);
242
74
    std::lock_guard<std::mutex> l(_lock);
243
74
    auto it = _metrics.find(&dummy);
244
74
    if (it == _metrics.end()) {
245
2
        return nullptr;
246
2
    }
247
72
    return it->second;
248
74
}
249
250
1.00k
void MetricEntity::register_hook(const std::string& name, const std::function<void()>& hook) {
251
1.00k
    std::lock_guard<std::mutex> l(_lock);
252
#ifndef BE_TEST
253
    DCHECK(_hooks.find(name) == _hooks.end()) << "hook is already exist! " << _name << ":" << name;
254
#endif
255
1.00k
    _hooks.emplace(name, hook);
256
1.00k
}
257
258
498
void MetricEntity::deregister_hook(const std::string& name) {
259
498
    std::lock_guard<std::mutex> l(_lock);
260
498
    _hooks.erase(name);
261
498
}
262
263
55
void MetricEntity::trigger_hook_unlocked(bool force) const {
264
    // When 'enable_metric_calculator' is true, hooks will be triggered by a background thread,
265
    // see 'calculate_metrics' in daemon.cpp for more details.
266
55
    if (!force && config::enable_metric_calculator) {
267
52
        return;
268
52
    }
269
3
    for (const auto& hook : _hooks) {
270
2
        hook.second();
271
2
    }
272
3
}
273
274
6
MetricRegistry::~MetricRegistry() {}
275
276
std::shared_ptr<MetricEntity> MetricRegistry::register_entity(const std::string& name,
277
                                                              const Labels& labels,
278
1.68k
                                                              MetricEntityType type) {
279
1.68k
    std::shared_ptr<MetricEntity> entity = std::make_shared<MetricEntity>(type, name, labels);
280
1.68k
    std::lock_guard<std::mutex> l(_lock);
281
1.68k
    auto inserted_entity = _entities.insert(std::make_pair(entity, 1));
282
1.68k
    if (!inserted_entity.second) {
283
        // If exist, increase the registered count
284
185
        inserted_entity.first->second++;
285
185
    }
286
1.68k
    return inserted_entity.first->first;
287
1.68k
}
288
289
1.67k
void MetricRegistry::deregister_entity(const std::shared_ptr<MetricEntity>& entity) {
290
1.67k
    std::lock_guard<std::mutex> l(_lock);
291
1.67k
    auto found_entity = _entities.find(entity);
292
1.67k
    if (found_entity != _entities.end()) {
293
        // Decrease the registered count
294
1.51k
        --found_entity->second;
295
1.51k
        if (found_entity->second == 0) {
296
            // Only erase it when registered count is zero
297
1.43k
            _entities.erase(found_entity);
298
1.43k
        }
299
1.51k
    }
300
1.67k
}
301
302
std::shared_ptr<MetricEntity> MetricRegistry::get_entity(const std::string& name,
303
                                                         const Labels& labels,
304
10
                                                         MetricEntityType type) {
305
10
    std::shared_ptr<MetricEntity> dummy = std::make_shared<MetricEntity>(type, name, labels);
306
307
10
    std::lock_guard<std::mutex> l(_lock);
308
10
    auto entity = _entities.find(dummy);
309
10
    if (entity == _entities.end()) {
310
2
        return std::shared_ptr<MetricEntity>();
311
2
    }
312
8
    return entity->first;
313
10
}
314
315
0
void MetricRegistry::trigger_all_hooks(bool force) const {
316
0
    std::lock_guard<std::mutex> l(_lock);
317
0
    for (const auto& entity : _entities) {
318
0
        std::lock_guard<std::mutex> l(entity.first->_lock);
319
0
        entity.first->trigger_hook_unlocked(force);
320
0
    }
321
0
}
322
323
13
std::string MetricRegistry::to_prometheus(bool with_tablet_metrics) const {
324
    // Reorder by MetricPrototype
325
13
    EntityMetricsByType entity_metrics_by_types;
326
13
    std::lock_guard<std::mutex> l1(_lock);
327
40
    for (const auto& entity : _entities) {
328
40
        if (entity.first->_type == MetricEntityType::kTablet && !with_tablet_metrics) {
329
0
            continue;
330
0
        }
331
40
        std::lock_guard<std::mutex> l2(entity.first->_lock);
332
40
        entity.first->trigger_hook_unlocked(false);
333
563
        for (const auto& metric : entity.first->_metrics) {
334
563
            std::pair<MetricEntity*, Metric*> new_elem =
335
563
                    std::make_pair(entity.first.get(), metric.second);
336
563
            auto found = entity_metrics_by_types.find(metric.first);
337
563
            if (found == entity_metrics_by_types.end()) {
338
401
                entity_metrics_by_types.emplace(
339
401
                        metric.first, std::vector<std::pair<MetricEntity*, Metric*>>({new_elem}));
340
401
            } else {
341
162
                found->second.emplace_back(new_elem);
342
162
            }
343
563
        }
344
40
    }
345
346
    // Output
347
13
    std::stringstream ss;
348
13
    std::string last_group_name;
349
401
    for (const auto& entity_metrics_by_type : entity_metrics_by_types) {
350
401
        if (last_group_name.empty() ||
351
401
            last_group_name != entity_metrics_by_type.first->group_name) {
352
292
            ss << entity_metrics_by_type.first->to_prometheus(_name); // metric TYPE line
353
292
        }
354
401
        last_group_name = entity_metrics_by_type.first->group_name;
355
401
        std::string display_name = entity_metrics_by_type.first->combine_name(_name);
356
563
        for (const auto& entity_metric : entity_metrics_by_type.second) {
357
563
            ss << entity_metric.second->to_prometheus(display_name, // metric key-value line
358
563
                                                      entity_metric.first->_labels,
359
563
                                                      entity_metrics_by_type.first->labels);
360
563
        }
361
401
    }
362
363
13
    return ss.str();
364
13
}
365
366
9
std::string MetricRegistry::to_json(bool with_tablet_metrics) const {
367
9
    rj::Document doc {rj::kArrayType};
368
9
    rj::Document::AllocatorType& allocator = doc.GetAllocator();
369
9
    std::lock_guard<std::mutex> l(_lock);
370
9
    for (const auto& entity : _entities) {
371
7
        if (entity.first->_type == MetricEntityType::kTablet && !with_tablet_metrics) {
372
0
            continue;
373
0
        }
374
7
        std::lock_guard<std::mutex> l(entity.first->_lock);
375
7
        entity.first->trigger_hook_unlocked(false);
376
8
        for (const auto& metric : entity.first->_metrics) {
377
8
            rj::Value metric_obj(rj::kObjectType);
378
            // tags
379
8
            rj::Value tag_obj(rj::kObjectType);
380
8
            tag_obj.AddMember("metric", rj::Value(metric.first->simple_name().c_str(), allocator),
381
8
                              allocator);
382
            // MetricPrototype's labels
383
8
            for (auto& label : metric.first->labels) {
384
5
                tag_obj.AddMember(rj::Value(label.first.c_str(), allocator),
385
5
                                  rj::Value(label.second.c_str(), allocator), allocator);
386
5
            }
387
            // MetricEntity's labels
388
8
            for (auto& label : entity.first->_labels) {
389
3
                tag_obj.AddMember(rj::Value(label.first.c_str(), allocator),
390
3
                                  rj::Value(label.second.c_str(), allocator), allocator);
391
3
            }
392
8
            metric_obj.AddMember("tags", tag_obj, allocator);
393
            // unit
394
8
            rj::Value unit_val(unit_name(metric.first->unit), allocator);
395
8
            metric_obj.AddMember("unit", unit_val, allocator);
396
            // value
397
8
            metric_obj.AddMember("value", metric.second->to_json_value(allocator), allocator);
398
8
            doc.PushBack(metric_obj, allocator);
399
8
        }
400
7
    }
401
402
9
    rj::StringBuffer strBuf;
403
9
    rj::Writer<rj::StringBuffer> writer(strBuf);
404
9
    doc.Accept(writer);
405
9
    return strBuf.GetString();
406
9
}
407
408
7
std::string MetricRegistry::to_core_string() const {
409
7
    std::stringstream ss;
410
7
    std::lock_guard<std::mutex> l(_lock);
411
7
    for (const auto& entity : _entities) {
412
5
        std::lock_guard<std::mutex> l(entity.first->_lock);
413
5
        entity.first->trigger_hook_unlocked(false);
414
6
        for (const auto& metric : entity.first->_metrics) {
415
6
            if (metric.first->is_core_metric) {
416
1
                ss << metric.first->combine_name(_name) << " LONG " << metric.second->to_string()
417
1
                   << "\n";
418
1
            }
419
6
        }
420
5
    }
421
422
7
    return ss.str();
423
7
}
424
425
} // namespace doris