Coverage Report

Created: 2026-07-25 08:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/common/metrics/metrics.cpp
Line
Count
Source
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 "common/metrics/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
866
std::ostream& operator<<(std::ostream& os, MetricType type) {
32
866
    switch (type) {
33
564
    case MetricType::COUNTER:
34
564
        os << "counter";
35
564
        break;
36
292
    case MetricType::GAUGE:
37
292
        os << "gauge";
38
292
        break;
39
10
    case MetricType::HISTOGRAM:
40
10
        os << "histogram";
41
10
        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
866
    }
52
866
    return os;
53
866
}
54
55
1.69k
const char* unit_name(MetricUnit unit) {
56
1.69k
    switch (unit) {
57
182
    case MetricUnit::NANOSECONDS:
58
182
        return "nanoseconds";
59
3
    case MetricUnit::MICROSECONDS:
60
3
        return "microseconds";
61
20
    case MetricUnit::MILLISECONDS:
62
20
        return "milliseconds";
63
10
    case MetricUnit::SECONDS:
64
10
        return "seconds";
65
179
    case MetricUnit::BYTES:
66
179
        return "bytes";
67
11
    case MetricUnit::ROWS:
68
11
        return "rows";
69
343
    case MetricUnit::PERCENT:
70
343
        return "percent";
71
35
    case MetricUnit::REQUESTS:
72
35
        return "requests";
73
90
    case MetricUnit::OPERATIONS:
74
90
        return "operations";
75
0
    case MetricUnit::BLOCKS:
76
0
        return "blocks";
77
12
    case MetricUnit::ROWSETS:
78
12
        return "rowsets";
79
4
    case MetricUnit::CONNECTIONS:
80
4
        return "rowsets";
81
804
    default:
82
804
        return "nounit";
83
1.69k
    }
84
1.69k
}
85
86
2.65k
std::string labels_to_string(std::initializer_list<const Labels*> multi_labels) {
87
2.65k
    bool all_empty = true;
88
3.76k
    for (const auto& labels : multi_labels) {
89
3.76k
        if (!labels->empty()) {
90
1.97k
            all_empty = false;
91
1.97k
            break;
92
1.97k
        }
93
3.76k
    }
94
2.65k
    if (all_empty) {
95
688
        return std::string();
96
688
    }
97
98
1.97k
    std::stringstream ss;
99
1.97k
    ss << "{";
100
1.97k
    int i = 0;
101
3.99k
    for (auto labels : multi_labels) {
102
3.99k
        for (const auto& label : *labels) {
103
2.58k
            if (i++ > 0) {
104
618
                ss << ",";
105
618
            }
106
2.58k
            ss << label.first << "=\"" << label.second << "\"";
107
2.58k
        }
108
3.99k
    }
109
1.97k
    ss << "}";
110
111
1.97k
    return ss.str();
112
2.65k
}
113
114
std::string Metric::to_prometheus(const std::string& display_name, const Labels& entity_labels,
115
2.53k
                                  const Labels& metric_labels) const {
116
2.53k
    std::stringstream ss;
117
2.53k
    ss << display_name                                       // metric name
118
2.53k
       << labels_to_string({&entity_labels, &metric_labels}) // metric labels
119
2.53k
       << " " << to_string() << "\n";                        // metric value
120
2.53k
    return ss.str();
121
2.53k
}
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
400
void HistogramMetric::add(const uint64_t& value) {
135
400
    _stats.add(value);
136
400
}
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
263
void HistogramMetric::set_histogram(const HistogramStat& stats) {
144
263
    std::lock_guard<std::mutex> l(_lock);
145
263
    _stats.clear();
146
263
    _stats.merge(stats);
147
263
}
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
10
                                           const Labels& metric_labels) const {
172
    // TODO: Use std::string concate for better performance.
173
10
    std::stringstream ss;
174
50
    for (const auto& percentile : _s_output_percentiles) {
175
50
        auto quantile_lable = Labels({{"quantile", percentile.first}});
176
50
        ss << display_name << labels_to_string({&entity_labels, &metric_labels, &quantile_lable})
177
50
           << " " << _stats.percentile(percentile.second) << "\n";
178
50
    }
179
10
    ss << display_name << "_sum" << labels_to_string({&entity_labels, &metric_labels}) << " "
180
10
       << _stats.sum() << "\n";
181
10
    ss << display_name << "_count" << labels_to_string({&entity_labels, &metric_labels}) << " "
182
10
       << _stats.num() << "\n";
183
10
    ss << display_name << "_max" << labels_to_string({&entity_labels, &metric_labels}) << " "
184
10
       << _stats.max() << "\n";
185
10
    ss << display_name << "_min" << labels_to_string({&entity_labels, &metric_labels}) << " "
186
10
       << _stats.min() << "\n";
187
10
    ss << display_name << "_average" << labels_to_string({&entity_labels, &metric_labels}) << " "
188
10
       << _stats.average() << "\n";
189
10
    ss << display_name << "_median" << labels_to_string({&entity_labels, &metric_labels}) << " "
190
10
       << _stats.median() << "\n";
191
10
    ss << display_name << "_standard_deviation"
192
10
       << labels_to_string({&entity_labels, &metric_labels}) << " " << _stats.standard_deviation()
193
10
       << "\n";
194
195
10
    return ss.str();
196
10
}
197
198
5
rj::Value HistogramMetric::to_json_value(rj::Document::AllocatorType& allocator) const {
199
5
    rj::Value json_value(rj::kObjectType);
200
5
    json_value.AddMember("total_count", rj::Value(_stats.num()), allocator);
201
5
    json_value.AddMember("min", rj::Value(_stats.min()), allocator);
202
5
    json_value.AddMember("average", rj::Value(_stats.average()), allocator);
203
5
    json_value.AddMember("median", rj::Value(_stats.median()), allocator);
204
25
    for (const auto& percentile : _s_output_percentiles) {
205
25
        json_value.AddMember(
206
25
                rj::Value(std::string("percentile_").append(percentile.first.substr(2)).c_str(),
207
25
                          allocator),
208
25
                rj::Value(_stats.percentile(percentile.second)), allocator);
209
25
    }
210
5
    json_value.AddMember("standard_deviation", rj::Value(_stats.standard_deviation()), allocator);
211
5
    json_value.AddMember("max", rj::Value(_stats.max()), allocator);
212
5
    json_value.AddMember("total_sum", rj::Value(_stats.sum()), allocator);
213
214
5
    return json_value;
215
5
}
216
217
3.71k
std::string MetricPrototype::simple_name() const {
218
3.71k
    return group_name.empty() ? name : group_name;
219
3.71k
}
220
221
2.02k
std::string MetricPrototype::combine_name(const std::string& registry_name) const {
222
2.02k
    return (registry_name.empty() ? std::string() : registry_name + "_") + simple_name();
223
2.02k
}
224
225
866
std::string MetricPrototype::to_prometheus(const std::string& registry_name) const {
226
866
    std::stringstream ss;
227
866
    ss << "# TYPE " << combine_name(registry_name) << " " << type << "\n";
228
866
    return ss.str();
229
866
}
230
231
1.25k
void MetricEntity::deregister_metric(const MetricPrototype* metric_type) {
232
1.25k
    std::lock_guard<std::mutex> l(_lock);
233
1.25k
    auto metric = _metrics.find(metric_type);
234
1.25k
    if (metric != _metrics.end()) {
235
1.13k
        delete metric->second;
236
1.13k
        _metrics.erase(metric);
237
1.13k
    }
238
1.25k
}
239
240
152
Metric* MetricEntity::get_metric(const std::string& name, const std::string& group_name) const {
241
152
    MetricPrototype dummy(MetricType::UNTYPED, MetricUnit::NOUNIT, name, "", group_name);
242
152
    std::lock_guard<std::mutex> l(_lock);
243
152
    auto it = _metrics.find(&dummy);
244
152
    if (it == _metrics.end()) {
245
4
        return nullptr;
246
4
    }
247
148
    return it->second;
248
152
}
249
250
942
void MetricEntity::register_hook(const std::string& name, const std::function<void()>& hook) {
251
942
    std::lock_guard<std::mutex> l(_lock);
252
942
#ifndef BE_TEST
253
942
    DCHECK(_hooks.find(name) == _hooks.end()) << "hook is already exist! " << _name << ":" << name;
254
942
#endif
255
942
    _hooks.emplace(name, hook);
256
942
}
257
258
4.71k
void MetricEntity::deregister_hook(const std::string& name) {
259
4.71k
    std::lock_guard<std::mutex> l(_lock);
260
4.71k
    _hooks.erase(name);
261
4.71k
}
262
263
23.2M
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
23.2M
    if (!force && config::enable_metric_calculator) {
267
518
        return;
268
518
    }
269
23.2M
    for (const auto& hook : _hooks) {
270
116k
        hook.second();
271
116k
    }
272
23.2M
}
273
274
21
MetricRegistry::~MetricRegistry() {}
275
276
std::shared_ptr<MetricEntity> MetricRegistry::register_entity(const std::string& name,
277
                                                              const Labels& labels,
278
423k
                                                              MetricEntityType type) {
279
423k
    std::shared_ptr<MetricEntity> entity = std::make_shared<MetricEntity>(type, name, labels);
280
423k
    std::lock_guard<std::mutex> l(_lock);
281
423k
    auto inserted_entity = _entities.insert(std::make_pair(entity, 1));
282
423k
    if (!inserted_entity.second) {
283
        // If exist, increase the registered count
284
2.01k
        inserted_entity.first->second++;
285
2.01k
    }
286
423k
    return inserted_entity.first->first;
287
423k
}
288
289
181k
void MetricRegistry::deregister_entity(const std::shared_ptr<MetricEntity>& entity) {
290
181k
    std::lock_guard<std::mutex> l(_lock);
291
181k
    auto found_entity = _entities.find(entity);
292
181k
    if (found_entity != _entities.end()) {
293
        // Decrease the registered count
294
180k
        --found_entity->second;
295
180k
        if (found_entity->second == 0) {
296
            // Only erase it when registered count is zero
297
179k
            _entities.erase(found_entity);
298
179k
        }
299
180k
    }
300
181k
}
301
302
std::shared_ptr<MetricEntity> MetricRegistry::get_entity(const std::string& name,
303
                                                         const Labels& labels,
304
20
                                                         MetricEntityType type) {
305
20
    std::shared_ptr<MetricEntity> dummy = std::make_shared<MetricEntity>(type, name, labels);
306
307
20
    std::lock_guard<std::mutex> l(_lock);
308
20
    auto entity = _entities.find(dummy);
309
20
    if (entity == _entities.end()) {
310
4
        return std::shared_ptr<MetricEntity>();
311
4
    }
312
16
    return entity->first;
313
20
}
314
315
1.00k
void MetricRegistry::trigger_all_hooks(bool force) const {
316
1.00k
    std::lock_guard<std::mutex> l(_lock);
317
23.2M
    for (const auto& entity : _entities) {
318
23.2M
        std::lock_guard<std::mutex> le(entity.first->_lock);
319
23.2M
        entity.first->trigger_hook_unlocked(force);
320
23.2M
    }
321
1.00k
}
322
323
26
std::string MetricRegistry::to_prometheus(bool with_tablet_metrics) const {
324
    // Reorder by MetricPrototype
325
26
    EntityMetricsByType entity_metrics_by_types;
326
26
    std::lock_guard<std::mutex> l1(_lock);
327
272
    for (const auto& entity : _entities) {
328
272
        if (entity.first->_type == MetricEntityType::kTablet && !with_tablet_metrics) {
329
0
            continue;
330
0
        }
331
272
        std::lock_guard<std::mutex> l2(entity.first->_lock);
332
272
        entity.first->trigger_hook_unlocked(false);
333
2.54k
        for (const auto& metric : entity.first->_metrics) {
334
2.54k
            std::pair<MetricEntity*, Metric*> new_elem =
335
2.54k
                    std::make_pair(entity.first.get(), metric.second);
336
2.54k
            auto found = entity_metrics_by_types.find(metric.first);
337
2.54k
            if (found == entity_metrics_by_types.end()) {
338
1.14k
                entity_metrics_by_types.emplace(
339
1.14k
                        metric.first, std::vector<std::pair<MetricEntity*, Metric*>>({new_elem}));
340
1.40k
            } else {
341
1.40k
                found->second.emplace_back(new_elem);
342
1.40k
            }
343
2.54k
        }
344
272
    }
345
346
    // Output
347
26
    std::stringstream ss;
348
26
    std::string last_group_name;
349
1.14k
    for (const auto& entity_metrics_by_type : entity_metrics_by_types) {
350
1.14k
        if (last_group_name.empty() ||
351
1.14k
            last_group_name != entity_metrics_by_type.first->group_name) {
352
866
            ss << entity_metrics_by_type.first->to_prometheus(_name); // metric TYPE line
353
866
        }
354
1.14k
        last_group_name = entity_metrics_by_type.first->group_name;
355
1.14k
        std::string display_name = entity_metrics_by_type.first->combine_name(_name);
356
2.54k
        for (const auto& entity_metric : entity_metrics_by_type.second) {
357
2.54k
            ss << entity_metric.second->to_prometheus(display_name, // metric key-value line
358
2.54k
                                                      entity_metric.first->_labels,
359
2.54k
                                                      entity_metrics_by_type.first->labels);
360
2.54k
        }
361
1.14k
    }
362
363
26
    return ss.str();
364
26
}
365
366
19
std::string MetricRegistry::to_json(bool with_tablet_metrics) const {
367
19
    rj::Document doc {rj::kArrayType};
368
19
    rj::Document::AllocatorType& allocator = doc.GetAllocator();
369
19
    std::lock_guard<std::mutex> l(_lock);
370
85.0k
    for (const auto& entity : _entities) {
371
85.0k
        if (entity.first->_type == MetricEntityType::kTablet && !with_tablet_metrics) {
372
84.7k
            continue;
373
84.7k
        }
374
236
        std::lock_guard<std::mutex> le(entity.first->_lock);
375
236
        entity.first->trigger_hook_unlocked(false);
376
1.69k
        for (const auto& metric : entity.first->_metrics) {
377
1.69k
            rj::Value metric_obj(rj::kObjectType);
378
            // tags
379
1.69k
            rj::Value tag_obj(rj::kObjectType);
380
1.69k
            tag_obj.AddMember("metric", rj::Value(metric.first->simple_name().c_str(), allocator),
381
1.69k
                              allocator);
382
            // MetricPrototype's labels
383
1.69k
            for (auto& label : metric.first->labels) {
384
456
                tag_obj.AddMember(rj::Value(label.first.c_str(), allocator),
385
456
                                  rj::Value(label.second.c_str(), allocator), allocator);
386
456
            }
387
            // MetricEntity's labels
388
3.18k
            for (auto& label : entity.first->_labels) {
389
3.18k
                tag_obj.AddMember(rj::Value(label.first.c_str(), allocator),
390
3.18k
                                  rj::Value(label.second.c_str(), allocator), allocator);
391
3.18k
            }
392
1.69k
            metric_obj.AddMember("tags", tag_obj, allocator);
393
            // unit
394
1.69k
            rj::Value unit_val(unit_name(metric.first->unit), allocator);
395
1.69k
            metric_obj.AddMember("unit", unit_val, allocator);
396
            // value
397
1.69k
            metric_obj.AddMember("value", metric.second->to_json_value(allocator), allocator);
398
1.69k
            doc.PushBack(metric_obj, allocator);
399
1.69k
        }
400
236
    }
401
402
19
    rj::StringBuffer strBuf;
403
19
    rj::Writer<rj::StringBuffer> writer(strBuf);
404
19
    doc.Accept(writer);
405
19
    return strBuf.GetString();
406
19
}
407
408
14
std::string MetricRegistry::to_core_string() const {
409
14
    std::stringstream ss;
410
14
    std::lock_guard<std::mutex> l(_lock);
411
14
    for (const auto& entity : _entities) {
412
10
        std::lock_guard<std::mutex> le(entity.first->_lock);
413
10
        entity.first->trigger_hook_unlocked(false);
414
12
        for (const auto& metric : entity.first->_metrics) {
415
12
            if (metric.first->is_core_metric) {
416
2
                ss << metric.first->combine_name(_name) << " LONG " << metric.second->to_string()
417
2
                   << "\n";
418
2
            }
419
12
        }
420
10
    }
421
422
14
    return ss.str();
423
14
}
424
425
} // namespace doris