Skip to content

Commit b4ce61f

Browse files
Fix format of prometheus service (#2899)
Fix PrometheusMetricsDumper to dump unique comment of a certain metric name to follow the prometheus specification. Also refine the prometheus ut case to support mvar output Co-authored-by: Zhengwei Zhu <[email protected]>
1 parent cb1a620 commit b4ce61f

8 files changed

+143
-47
lines changed

BUILD.bazel

+1
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,7 @@ filegroup(
465465
proto_library(
466466
name = "brpc_idl_options_proto",
467467
srcs = [":brpc_idl_options_proto_srcs"],
468+
strip_import_prefix = "src",
468469
visibility = ["//visibility:public"],
469470
deps = [
470471
"@com_google_protobuf//:descriptor_proto",

src/brpc/builtin/prometheus_metrics_service.cpp

+17
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ class PrometheusMetricsDumper : public bvar::Dumper {
5454
}
5555

5656
bool dump(const std::string& name, const butil::StringPiece& desc) override;
57+
bool dump_mvar(const std::string& name, const butil::StringPiece& desc) override;
58+
bool dump_comment(const std::string& name, const std::string& type) override;
5759

5860
private:
5961
DISALLOW_COPY_AND_ASSIGN(PrometheusMetricsDumper);
@@ -108,6 +110,21 @@ bool PrometheusMetricsDumper::dump(const std::string& name,
108110
return true;
109111
}
110112

113+
bool PrometheusMetricsDumper::dump_mvar(const std::string& name, const butil::StringPiece& desc) {
114+
if (!desc.empty() && desc[0] == '"') {
115+
// there is no necessary to monitor string in prometheus
116+
return true;
117+
}
118+
*_os << name << " " << desc << "\n";
119+
return true;
120+
}
121+
122+
bool PrometheusMetricsDumper::dump_comment(const std::string& name, const std::string& type) {
123+
*_os << "# HELP " << name << '\n'
124+
<< "# TYPE " << name << " " << type << '\n';
125+
return true;
126+
}
127+
111128
const PrometheusMetricsDumper::SummaryItems*
112129
PrometheusMetricsDumper::ProcessLatencyRecorderSuffix(const butil::StringPiece& name,
113130
const butil::StringPiece& desc) {

src/bvar/multi_dimension_inl.h

+31-22
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ size_t MultiDimension<T>::dump(Dumper* dumper, const DumpOptions* options) {
252252
bvar->describe(oss, options->quote_string);
253253
std::ostringstream oss_key;
254254
make_dump_key(oss_key, label_name);
255-
if (!dumper->dump(oss_key.str(), oss.str())) {
255+
if (!dumper->dump_mvar(oss_key.str(), oss.str())) {
256256
continue;
257257
}
258258
n++;
@@ -269,20 +269,20 @@ size_t MultiDimension<bvar::LatencyRecorder>::dump(Dumper* dumper, const DumpOpt
269269
return 0;
270270
}
271271
size_t n = 0;
272+
// To meet prometheus specification, we must guarantee no second TYPE line for one metric name
273+
274+
// latency comment
275+
dumper->dump_comment(name() + "_latency", METRIC_TYPE_GAUGE);
272276
for (auto &label_name : label_names) {
273277
bvar::LatencyRecorder* bvar = get_stats_impl(label_name);
274278
if (!bvar) {
275279
continue;
276280
}
277281

278-
// latency comment
279-
if (!dumper->dump_comment(name() + "_latency", METRIC_TYPE_GAUGE)) {
280-
continue;
281-
}
282282
// latency
283283
std::ostringstream oss_latency_key;
284284
make_dump_key(oss_latency_key, label_name, "_latency");
285-
if (dumper->dump(oss_latency_key.str(), std::to_string(bvar->latency()))) {
285+
if (dumper->dump_mvar(oss_latency_key.str(), std::to_string(bvar->latency()))) {
286286
n++;
287287
}
288288
// latency_percentiles
@@ -291,53 +291,62 @@ size_t MultiDimension<bvar::LatencyRecorder>::dump(Dumper* dumper, const DumpOpt
291291
for (auto lp : latency_percentiles) {
292292
std::ostringstream oss_lp_key;
293293
make_dump_key(oss_lp_key, label_name, "_latency", lp);
294-
if (dumper->dump(oss_lp_key.str(), std::to_string(bvar->latency_percentile(lp / 100.0)))) {
294+
if (dumper->dump_mvar(oss_lp_key.str(), std::to_string(bvar->latency_percentile(lp / 100.0)))) {
295295
n++;
296296
}
297297
}
298298
// 999
299299
std::ostringstream oss_p999_key;
300300
make_dump_key(oss_p999_key, label_name, "_latency", 999);
301-
if (dumper->dump(oss_p999_key.str(), std::to_string(bvar->latency_percentile(0.999)))) {
301+
if (dumper->dump_mvar(oss_p999_key.str(), std::to_string(bvar->latency_percentile(0.999)))) {
302302
n++;
303303
}
304304
// 9999
305305
std::ostringstream oss_p9999_key;
306306
make_dump_key(oss_p9999_key, label_name, "_latency", 9999);
307-
if (dumper->dump(oss_p9999_key.str(), std::to_string(bvar->latency_percentile(0.9999)))) {
307+
if (dumper->dump_mvar(oss_p9999_key.str(), std::to_string(bvar->latency_percentile(0.9999)))) {
308308
n++;
309309
}
310+
}
310311

311-
// max_latency comment
312-
if (!dumper->dump_comment(name() + "_max_latency", METRIC_TYPE_GAUGE)) {
312+
// max_latency comment
313+
dumper->dump_comment(name() + "_max_latency", METRIC_TYPE_GAUGE);
314+
for (auto &label_name : label_names) {
315+
bvar::LatencyRecorder* bvar = get_stats_impl(label_name);
316+
if (!bvar) {
313317
continue;
314318
}
315-
// max_latency
316319
std::ostringstream oss_max_latency_key;
317320
make_dump_key(oss_max_latency_key, label_name, "_max_latency");
318-
if (dumper->dump(oss_max_latency_key.str(), std::to_string(bvar->max_latency()))) {
321+
if (dumper->dump_mvar(oss_max_latency_key.str(), std::to_string(bvar->max_latency()))) {
319322
n++;
320323
}
321-
322-
// qps comment
323-
if (!dumper->dump_comment(name() + "_qps", METRIC_TYPE_GAUGE)) {
324+
}
325+
326+
// qps comment
327+
dumper->dump_comment(name() + "_qps", METRIC_TYPE_GAUGE);
328+
for (auto &label_name : label_names) {
329+
bvar::LatencyRecorder* bvar = get_stats_impl(label_name);
330+
if (!bvar) {
324331
continue;
325332
}
326-
// qps
327333
std::ostringstream oss_qps_key;
328334
make_dump_key(oss_qps_key, label_name, "_qps");
329-
if (dumper->dump(oss_qps_key.str(), std::to_string(bvar->qps()))) {
335+
if (dumper->dump_mvar(oss_qps_key.str(), std::to_string(bvar->qps()))) {
330336
n++;
331337
}
338+
}
332339

333-
// qps comment
334-
if (!dumper->dump_comment(name() + "_count", METRIC_TYPE_COUNTER)) {
340+
// count comment
341+
dumper->dump_comment(name() + "_count", METRIC_TYPE_COUNTER);
342+
for (auto &label_name : label_names) {
343+
bvar::LatencyRecorder* bvar = get_stats_impl(label_name);
344+
if (!bvar) {
335345
continue;
336346
}
337-
// count
338347
std::ostringstream oss_count_key;
339348
make_dump_key(oss_count_key, label_name, "_count");
340-
if (dumper->dump(oss_count_key.str(), std::to_string(bvar->count()))) {
349+
if (dumper->dump_mvar(oss_count_key.str(), std::to_string(bvar->count()))) {
341350
n++;
342351
}
343352
}

src/bvar/variable.h

+6
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ class Dumper {
5353
virtual ~Dumper() { }
5454
virtual bool dump(const std::string& name,
5555
const butil::StringPiece& description) = 0;
56+
// Only for dumping value of multiple dimension var to prometheus service
57+
virtual bool dump_mvar(const std::string& name,
58+
const butil::StringPiece& description) {
59+
return true;
60+
}
61+
// Only for dumping comment of multiple dimension var to prometheus service
5662
virtual bool dump_comment(const std::string&, const std::string& /*type*/) {
5763
return true;
5864
}

test/BUILD.bazel

+21-3
Original file line numberDiff line numberDiff line change
@@ -154,11 +154,12 @@ proto_library(
154154
[
155155
"*.proto",
156156
],
157-
exclude = [
158-
"echo.proto",
159-
],
160157
),
158+
strip_import_prefix = "/test",
161159
visibility = ["//visibility:public"],
160+
deps = [
161+
"//:brpc_idl_options_proto",
162+
]
162163
)
163164

164165
cc_proto_library(
@@ -241,6 +242,23 @@ cc_test(
241242
],
242243
)
243244

245+
cc_test(
246+
name = "brpc_prometheus_test",
247+
srcs = glob(
248+
[
249+
"brpc_prometheus_*_unittest.cpp",
250+
],
251+
),
252+
copts = COPTS,
253+
deps = [
254+
":cc_test_proto",
255+
":sstream_workaround",
256+
"//:brpc",
257+
"@com_google_googletest//:gtest",
258+
"@com_google_googletest//:gtest_main",
259+
],
260+
)
261+
244262
refresh_compile_commands(
245263
name = "brpc_test_compdb",
246264
# Specify the targets of interest.

test/brpc_channel_unittest.cpp

-4
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,7 @@
4040
#include "brpc/selective_channel.h"
4141
#include "brpc/socket_map.h"
4242
#include "brpc/controller.h"
43-
#if BAZEL_TEST
44-
#include "test/echo.pb.h"
45-
#else
4643
#include "echo.pb.h"
47-
#endif // BAZEL_TEST
4844
#include "brpc/options.pb.h"
4945

5046
namespace brpc {

test/brpc_prometheus_metrics_unittest.cpp

+67-14
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "brpc/controller.h"
2424
#include "butil/strings/string_piece.h"
2525
#include "echo.pb.h"
26+
#include "bvar/multi_dimension.h"
2627

2728
int main(int argc, char* argv[]) {
2829
testing::InitGoogleTest(&argc, argv);
@@ -45,7 +46,13 @@ enum STATE {
4546
HELP = 0,
4647
TYPE,
4748
GAUGE,
48-
SUMMARY
49+
SUMMARY,
50+
COUNTER,
51+
// When meets a line with a gauge/counter with labels, we have no
52+
// idea the next line is a new HELP or the same gauge/counter just
53+
// with different labels
54+
HELP_OR_GAUGE,
55+
HELP_OR_COUNTER,
4956
};
5057

5158
TEST(PrometheusMetrics, sanity) {
@@ -54,10 +61,22 @@ TEST(PrometheusMetrics, sanity) {
5461
ASSERT_EQ(0, server.AddService(&echo_svc, brpc::SERVER_DOESNT_OWN_SERVICE));
5562
ASSERT_EQ(0, server.Start("127.0.0.1:8614", NULL));
5663

57-
brpc::Server server2;
58-
DummyEchoServiceImpl echo_svc2;
59-
ASSERT_EQ(0, server2.AddService(&echo_svc2, brpc::SERVER_DOESNT_OWN_SERVICE));
60-
ASSERT_EQ(0, server2.Start("127.0.0.1:8615", NULL));
64+
const std::list<std::string> labels = {"label1", "label2"};
65+
bvar::MultiDimension<bvar::Adder<uint32_t> > my_madder("madder", labels);
66+
bvar::Adder<uint32_t>* my_adder1 = my_madder.get_stats({"val1", "val2"});
67+
ASSERT_TRUE(my_adder1);
68+
*my_adder1 << 1 << 2;
69+
bvar::Adder<uint32_t>* my_adder2 = my_madder.get_stats({"val2", "val3"});
70+
ASSERT_TRUE(my_adder1);
71+
*my_adder2 << 3 << 4;
72+
73+
bvar::MultiDimension<bvar::LatencyRecorder > my_mlat("mlat", labels);
74+
bvar::LatencyRecorder* my_lat1 = my_mlat.get_stats({"val1", "val2"});
75+
ASSERT_TRUE(my_lat1);
76+
*my_lat1 << 1 << 2;
77+
bvar::LatencyRecorder* my_lat2 = my_mlat.get_stats({"val2", "val3"});
78+
ASSERT_TRUE(my_lat2);
79+
*my_lat2 << 3 << 4;
6180

6281
brpc::Channel channel;
6382
brpc::ChannelOptions channel_opts;
@@ -68,19 +87,22 @@ TEST(PrometheusMetrics, sanity) {
6887
channel.CallMethod(NULL, &cntl, NULL, NULL, NULL);
6988
ASSERT_FALSE(cntl.Failed());
7089
std::string res = cntl.response_attachment().to_string();
71-
90+
LOG(INFO) << "output:\n" << res;
7291
size_t start_pos = 0;
7392
size_t end_pos = 0;
93+
size_t label_start = 0;
7494
STATE state = HELP;
7595
char name_help[128];
7696
char name_type[128];
7797
char type[16];
7898
int matched = 0;
79-
int gauge_num = 0;
99+
int num = 0;
80100
bool summary_sum_gathered = false;
81101
bool summary_count_gathered = false;
82102
bool has_ever_summary = false;
83103
bool has_ever_gauge = false;
104+
bool has_ever_counter = false; // brought in by mvar latency recorder
105+
std::unordered_set<std::string> metric_name_set;
84106

85107
while ((end_pos = res.find('\n', start_pos)) != butil::StringPiece::npos) {
86108
res[end_pos] = '\0'; // safe;
@@ -98,21 +120,52 @@ TEST(PrometheusMetrics, sanity) {
98120
state = GAUGE;
99121
} else if (strcmp(type, "summary") == 0) {
100122
state = SUMMARY;
123+
} else if (strcmp(type, "counter") == 0) {
124+
state = COUNTER;
101125
} else {
102-
ASSERT_TRUE(false);
126+
ASSERT_TRUE(false) << "invalid type: " << type;
103127
}
128+
ASSERT_EQ(0, metric_name_set.count(name_type)) << "second TYPE line for metric name "
129+
<< name_type;
130+
metric_name_set.insert(name_help);
104131
break;
132+
case HELP_OR_GAUGE:
133+
case HELP_OR_COUNTER:
134+
matched = sscanf(res.data() + start_pos, "# HELP %s", name_help);
135+
// Try to figure out current line is a new COMMENT or not
136+
if (matched == 1) {
137+
state = HELP;
138+
} else {
139+
state = state == HELP_OR_GAUGE ? GAUGE : COUNTER;
140+
}
141+
res[end_pos] = '\n'; // revert to original
142+
continue; // do not jump to next line
105143
case GAUGE:
106-
matched = sscanf(res.data() + start_pos, "%s %d", name_type, &gauge_num);
144+
case COUNTER:
145+
matched = sscanf(res.data() + start_pos, "%s %d", name_type, &num);
107146
ASSERT_EQ(2, matched);
108-
ASSERT_STREQ(name_type, name_help);
109-
state = HELP;
110-
has_ever_gauge = true;
147+
if (state == GAUGE) {
148+
has_ever_gauge = true;
149+
}
150+
if (state == COUNTER) {
151+
has_ever_counter = true;
152+
}
153+
label_start = butil::StringPiece(name_type).find("{");
154+
if (label_start == strlen(name_help)) { // mvar
155+
ASSERT_EQ(name_type[strlen(name_type) - 1], '}');
156+
ASSERT_TRUE(strncmp(name_type, name_help, strlen(name_help)) == 0);
157+
state = state == GAUGE ? HELP_OR_GAUGE : HELP_OR_COUNTER;
158+
} else if (label_start == butil::StringPiece::npos) { // var
159+
ASSERT_STREQ(name_type, name_help);
160+
state = HELP;
161+
} else { // invalid
162+
ASSERT_TRUE(false);
163+
}
111164
break;
112165
case SUMMARY:
113166
if (butil::StringPiece(res.data() + start_pos, end_pos - start_pos).find("quantile=")
114167
== butil::StringPiece::npos) {
115-
matched = sscanf(res.data() + start_pos, "%s %d", name_type, &gauge_num);
168+
matched = sscanf(res.data() + start_pos, "%s %d", name_type, &num);
116169
ASSERT_EQ(2, matched);
117170
ASSERT_TRUE(strncmp(name_type, name_help, strlen(name_help)) == 0);
118171
if (butil::StringPiece(name_type).ends_with("_sum")) {
@@ -138,7 +191,7 @@ TEST(PrometheusMetrics, sanity) {
138191
}
139192
start_pos = end_pos + 1;
140193
}
141-
ASSERT_TRUE(has_ever_gauge && has_ever_summary);
194+
ASSERT_TRUE(has_ever_gauge && has_ever_summary && has_ever_counter);
142195
ASSERT_EQ(0, server.Stop(0));
143196
ASSERT_EQ(0, server.Join());
144197
}

test/iobuf_unittest.cpp

-4
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,7 @@
3232
#include <butil/fd_guard.h>
3333
#include <butil/errno.h>
3434
#include <butil/fast_rand.h>
35-
#if BAZEL_TEST
36-
#include "test/iobuf.pb.h"
37-
#else
3835
#include "iobuf.pb.h"
39-
#endif // BAZEL_TEST
4036

4137
namespace butil {
4238
namespace iobuf {

0 commit comments

Comments
 (0)