forked from envoyproxy/nighthawk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmetrics_plugin_impl.cc
217 lines (199 loc) · 8.32 KB
/
metrics_plugin_impl.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
#include "source/adaptive_load/metrics_plugin_impl.h"
#include <cmath>
#include "envoy/registry/registry.h"
#include "external/envoy/source/common/common/assert.h"
#include "external/envoy/source/common/protobuf/protobuf.h"
#include "absl/status/status.h"
#include "absl/strings/numbers.h"
#include "absl/strings/string_view.h"
namespace Nighthawk {
namespace {
using Envoy::Protobuf::util::TimeUtil;
/**
* Finds a Result proto with the given name within a Nighthawk Output proto.
*
* @param nighthawk_output The Nighthawk Output proto.
* @param result_name The name of the desired Result, e.g. "global".
*
* @return StatusOr<Result> The Result with the specified name if found, otherwise an error Status.
*/
absl::StatusOr<nighthawk::client::Result>
GetResult(const nighthawk::client::Output& nighthawk_output, absl::string_view result_name) {
for (const nighthawk::client::Result& result : nighthawk_output.results()) {
if (result.name() == result_name) {
return result;
}
}
return absl::InternalError(
absl::StrCat("Result '", result_name, "' not found in Nighthawk output."));
}
/**
* Returns the value of the counter with the given name within a Result proto.
*
* @param result A Result proto taken from a Nighthawk Output proto.
* @param counter_name The name of the counter, e.g. "benchmark.http_2xx".
*
* @return StatusOr<uint32_t> The counter value if found, otherwise an error Status.
*/
absl::StatusOr<uint32_t> GetCounter(const nighthawk::client::Result& result,
absl::string_view counter_name) {
for (const nighthawk::client::Counter& counter : result.counters()) {
if (counter.name() == counter_name) {
return counter.value();
}
}
return absl::InternalError(
absl::StrCat("Counter '", counter_name, "' not found in Result proto."));
}
/**
* Finds a Statistic proto with the given id within a Result proto.
*
* @param result A Result proto taken from a Nighthawk Output proto.
* @param statistic_id The name of the desired Statistic, e.g.
* "benchmark_http_client.request_to_response".
*
* @return StatusOr<Statistic> The Statistic with the specified id if found, otherwise an error
* Status.
*/
absl::StatusOr<nighthawk::client::Statistic> GetStatistic(const nighthawk::client::Result& result,
absl::string_view statistic_id) {
for (const nighthawk::client::Statistic& statistic : result.statistics()) {
if (statistic.id() == statistic_id) {
return statistic;
}
}
return absl::InternalError(
absl::StrCat("Statistic '", statistic_id, "' not found in Result proto."));
}
/**
* Extracts counters from a Nighthawk Service Output proto and computes metrics from them, storing
* the metrics in a map.
*
* @param nighthawk_output An Output proto returned from Nighthawk Service.
* @param metric_from_name A map to write computed metrics under various names.
* @param errors A place to accumulate error messages.
*/
void ExtractCounters(const nighthawk::client::Output& nighthawk_output,
absl::flat_hash_map<std::string, double>& metric_from_name,
std::vector<std::string>& errors) {
absl::StatusOr<nighthawk::client::Result> global_result_or =
GetResult(nighthawk_output, "global");
if (!global_result_or.ok()) {
errors.emplace_back(global_result_or.status().message());
return;
}
nighthawk::client::Result global_result = global_result_or.value();
const int64_t actual_duration_seconds =
TimeUtil::DurationToSeconds(global_result.execution_duration());
// 1 worker: 'global' Result only. >1 workers: Result for each worker plus a 'global' Result.
const uint32_t number_of_workers =
nighthawk_output.results_size() == 1 ? 1 : nighthawk_output.results_size() - 1;
const double total_specified =
static_cast<double>(nighthawk_output.options().requests_per_second().value() *
actual_duration_seconds * number_of_workers);
// Proceed through all calculations without crashing in order to capture all errors.
double total_sent;
absl::StatusOr<double> total_sent_or = GetCounter(global_result, "upstream_rq_total");
if (total_sent_or.ok()) {
total_sent = total_sent_or.value();
} else {
total_sent = std::numeric_limits<double>::quiet_NaN();
errors.emplace_back(total_sent_or.status().message());
}
double total_2xx;
absl::StatusOr<double> total_2xx_or = GetCounter(global_result, "benchmark.http_2xx");
if (total_2xx_or.ok()) {
total_2xx = total_2xx_or.value();
} else {
total_2xx = std::numeric_limits<double>::quiet_NaN();
errors.emplace_back(total_2xx_or.status().message());
}
if (actual_duration_seconds > 0.0) {
metric_from_name["attempted-rps"] = total_specified / actual_duration_seconds;
metric_from_name["achieved-rps"] = total_sent / actual_duration_seconds;
} else {
errors.emplace_back("Nighthawk returned a benchmark result with zero actual duration.");
}
if (total_specified > 0) {
metric_from_name["send-rate"] = total_sent / total_specified;
} else {
metric_from_name["send-rate"] = 0.0;
}
if (total_sent > 0) {
metric_from_name["success-rate"] = total_2xx / total_sent;
} else {
metric_from_name["success-rate"] = 0.0;
}
}
/**
* Extracts a Statistic for latency from a Nighthawk Service Output proto and computes metrics from
* Statistic values, storing the metrics in a map.
*
* @param nighthawk_output An Output proto returned from Nighthawk Service.
* @param metric_from_name A map to write computed metrics under various names.
* @param errors A place to accumulate error messages.
*/
void ExtractStatistics(const nighthawk::client::Output& nighthawk_output,
absl::flat_hash_map<std::string, double>& metric_from_name,
std::vector<std::string>& errors) {
absl::StatusOr<nighthawk::client::Result> global_result_or =
GetResult(nighthawk_output, "global");
if (!global_result_or.ok()) {
errors.emplace_back(global_result_or.status().message());
return;
}
nighthawk::client::Result global_result = global_result_or.value();
absl::StatusOr<nighthawk::client::Statistic> statistic_or =
GetStatistic(global_result, "benchmark_http_client.request_to_response");
if (!statistic_or.ok()) {
errors.emplace_back(statistic_or.status().message());
return;
}
nighthawk::client::Statistic statistic = statistic_or.value();
const double min = TimeUtil::DurationToNanoseconds(statistic.min());
const double mean = TimeUtil::DurationToNanoseconds(statistic.mean());
const double max = TimeUtil::DurationToNanoseconds(statistic.max());
const double pstdev = TimeUtil::DurationToNanoseconds(statistic.pstdev());
metric_from_name["latency-ns-min"] = min;
metric_from_name["latency-ns-mean"] = mean;
metric_from_name["latency-ns-max"] = max;
metric_from_name["latency-ns-mean-plus-1stdev"] = mean + pstdev;
metric_from_name["latency-ns-mean-plus-2stdev"] = mean + 2 * pstdev;
metric_from_name["latency-ns-mean-plus-3stdev"] = mean + 3 * pstdev;
metric_from_name["latency-ns-pstdev"] = pstdev;
}
} // namespace
NighthawkStatsEmulatedMetricsPlugin::NighthawkStatsEmulatedMetricsPlugin(
const nighthawk::client::Output& nighthawk_output) {
ExtractCounters(nighthawk_output, metric_from_name_, errors_);
ExtractStatistics(nighthawk_output, metric_from_name_, errors_);
}
absl::StatusOr<double>
NighthawkStatsEmulatedMetricsPlugin::GetMetricByName(absl::string_view metric_name) {
if (!errors_.empty()) {
return absl::InternalError(absl::StrJoin(errors_, "\n"));
}
if (metric_from_name_.find(metric_name) == metric_from_name_.end()) {
return absl::InternalError(
absl::StrCat("Metric '", metric_name, "' was not computed by the 'builtin' plugin."));
}
return metric_from_name_[metric_name];
}
const std::vector<std::string>
NighthawkStatsEmulatedMetricsPlugin::GetAllSupportedMetricNames() const {
return {
"achieved-rps",
"attempted-rps",
"latency-ns-max",
"latency-ns-mean",
"latency-ns-mean-plus-1stdev",
"latency-ns-mean-plus-2stdev",
"latency-ns-mean-plus-3stdev",
"latency-ns-min",
"latency-ns-pstdev",
"send-rate",
"success-rate",
};
}
// Note: Don't use REGISTER_FACTORY for NighthawkStatsEmulatedMetricsPlugin. See header for details.
} // namespace Nighthawk