Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
96ca9f8
Add histogram bucket information to /stats endpoint.
VillePihlava Jan 10, 2022
cc874ef
Add nonoverlappingComputedBuckets() and fix spelling.
VillePihlava Jan 25, 2022
bdf901f
Change naming and remove unnecessary const prefixes.
VillePihlava Jan 26, 2022
0b90dc3
Refactor computeDisjointBuckets().
VillePihlava Jan 31, 2022
cea16a5
Add ASSERT to computeDisjointBucketSummary and loop to min value of v…
VillePihlava Jan 31, 2022
1c5b193
Change histogram_buckets query parameter to have 2 possible values, c…
VillePihlava Feb 4, 2022
d00801c
Change documentation.
VillePihlava Feb 4, 2022
c010782
Fix format.
VillePihlava Feb 4, 2022
e0d0700
Merge branch 'main' of github.com:VillePihlava/envoy into histogram-b…
VillePihlava Feb 4, 2022
3a7e6c9
Change current.rst to alphabetical order.
VillePihlava Feb 4, 2022
00c963c
Fix comment.
VillePihlava Feb 4, 2022
2927e3d
Add JSON format for histogram buckets to stats endpoint.
VillePihlava Feb 9, 2022
acb92d5
Fix format.
VillePihlava Feb 9, 2022
d2dbe87
Add tests for histogram_buckets JSON.
VillePihlava Feb 14, 2022
7fd1f16
Fix vectors to const references.
VillePihlava Feb 14, 2022
37a57b0
Split ASSERTs.
VillePihlava Feb 14, 2022
fd8c4bd
Clarify ASSERT with comment.
VillePihlava Feb 14, 2022
c6e0bc0
Make statsAsJson more readable.
VillePihlava Feb 14, 2022
1e3613d
Add documentation for histogram buckets JSON output.
VillePihlava Feb 14, 2022
7401db0
Fix format.
VillePihlava Feb 14, 2022
7e03e9b
Create enum for histogram_bucket values.
VillePihlava Feb 17, 2022
ba359e4
Change computeDisjointBucketSummary.
VillePihlava Feb 17, 2022
ba6a6dc
Add statsAsJsonHistogramBucketsHelper.
VillePihlava Feb 17, 2022
5ba7520
Change histogram_buckets JSON format and make tests shorter.
VillePihlava Feb 17, 2022
dbf7b27
Merge branch 'main' into histogram-buckets-stats-endpoint
VillePihlava Feb 17, 2022
e392cad
Change tests to work with stats handler tests changes.
VillePihlava Feb 17, 2022
7ec7a8f
Change documentation to new JSON format.
VillePihlava Feb 17, 2022
1723109
Remove default from switches.
VillePihlava Feb 17, 2022
7645c3a
Merge branch 'main' into histogram-buckets-stats-endpoint
VillePihlava Feb 20, 2022
5dbca5d
Change current.rst.
VillePihlava Feb 20, 2022
6e8d867
Rename histogram_buckets_value to histogram_buckets_mode and Null to …
VillePihlava Feb 21, 2022
505aed2
Simplify statsAsJsonHistogramBucketsHelper.
VillePihlava Feb 21, 2022
741cc7b
Specify return type.
VillePihlava Feb 22, 2022
85138ae
Create variable for histogram->intervalStatistics().
VillePihlava Feb 22, 2022
842d5ce
Simplify JSON string used in test.
VillePihlava Feb 22, 2022
3ff6c45
Use absl::Status as return value type.
VillePihlava Feb 22, 2022
47ca897
Add comments about ValueUtil::numberValue.
VillePihlava Feb 22, 2022
9d62d48
Merge branch 'main' into histogram-buckets-stats-endpoint
VillePihlava Feb 23, 2022
b18d0f3
Kick CI
VillePihlava Feb 23, 2022
be1b95b
Merge branch 'main' into histogram-buckets-stats-endpoint
VillePihlava Feb 25, 2022
c4369be
Change current.rst.
VillePihlava Feb 25, 2022
3691e2a
Change setHistogramBucketSettings test helper function and add comments.
VillePihlava Mar 2, 2022
c296df9
Merge branch 'main' into histogram-buckets-stats-endpoint
VillePihlava Mar 2, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/root/operations/admin.rst
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,13 @@ modify different aspects of the server:
Full-string matching can be specified with begin- and end-line anchors. (i.e.
``/stats?filter=^server.concurrency$``)

.. http:get:: /stats?histogram_buckets

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a draft PR #19546 which will hopefully be ready soon, where I introduce a 'type=' which makes me wonder about exactly how best to control this.

First -- is there any reason not to always be in histogram_buckets mode? Is there any reason you'd want to see the current mode?

The new PR introduces a '&type=' query-param which has possible values (Counters, Gauges, Histograms, TextReadouts, or All). I'm unsure whether this would best fit as a new category, or as a boolean property which only makes sense if you are displaying histograms.

Also, what are your thoughts about how this parameter works with format=json or format=prometheus? Or does this only make sense for text?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First -- is there any reason not to always be in histogram_buckets mode? Is there any reason you'd want to see the current mode?

I don't have enough experience on this to give a good opinion, but I didn't find a use for the current mode.

The new PR introduces a '&type=' query-param which has possible values (Counters, Gauges, Histograms, TextReadouts, or All). I'm unsure whether this would best fit as a new category, or as a boolean property which only makes sense if you are displaying histograms.

I think a boolean property would suit this best.

Also, what are your thoughts about how this parameter works with format=json or format=prometheus? Or does this only make sense for text?

From what I've understood, the current way format=prometheus displays histogram buckets cumulatively is the correct way for prometheus, so it seems like changing this would be unnecessary.

Using format=json outputs the same quantile summary data available from the plain text endpoint for histograms. If the quantile summary is replaced by the histogram_buckets output in the plain text endpoint, I think it should be replaced here too.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So...can you add a test for the JSON mode as well then?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll work on a JSON mode next. Also something that came to mind is that should there be a query parameter or an option of seeing the original overlapping (or cumulative) histogram buckets? It should be easy to implement if there is any use for it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine to have a query-param for the new mode. The absence of that query-params means you want the old mode. Am I missing something?

@jmarantz jmarantz Jan 25, 2022

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see -- there are really 3 choices. Can you just have one query-param with 3 options then (default value being 'none'), rather than bools? I think that'll look better in the UI I've got pending -- see image in description of #18670

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, one query-param with 2 options sounds much better. I think something like this would work, although there might be a better word for nonoverlapping:
/stats?histogram_buckets=cumulative
/stats?histogram_buckets=nonoverlapping

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh...a better word for nonoverlapping might be "disjoint"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! I'll start using this


Changes histogram output to display buckets with upper bounds (e.g. B0.5, B1, B5, ...).
The output for each bucket will be in the form of (interval,cumulative) (e.g. B0.5(0,0)).
Buckets do not include values from other buckets with smaller upper bounds;
the previous bucket's upper bound acts as a lower bound. Compatible with ``usedonly`` and ``filter``.
Comment thread
jmarantz marked this conversation as resolved.

.. http:get:: /stats?format=json

Outputs /stats in JSON format. This can be used for programmatic access of stats. Counters and Gauges
Expand Down
1 change: 1 addition & 0 deletions docs/root/version_history/current.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Removed Config or Runtime
New Features
------------
* http3: downstream HTTP/3 support is now GA! Upstream HTTP/3 also GA for specific deployments. See :ref:`here <arch_overview_http3>` for details.
* stats: histogram_buckets query parameter added to plain text stats to change histogram output to show buckets.


Deprecated
Expand Down
11 changes: 11 additions & 0 deletions envoy/stats/histogram.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ class HistogramStatistics {
*/
virtual const std::vector<uint64_t>& computedBuckets() const PURE;

/**
* Returns nonoverlapping version of computedBuckets(). This vector is
* guaranteed to be the same length as supportedBuckets().
*/
virtual const std::vector<uint64_t> nonoverlappingComputedBuckets() const PURE;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 nits :)

  1. https://clang.llvm.org/extra/clang-tidy/checks/readability-avoid-const-params-in-decls.html -- you can just return the vector without making it const in the declaration -- that's a no-op
  2. rename new function to computeNonOverlappingBuckets(). The function is doing the computation and returning the result, not returning an already-existing result, so I think it's better to start the function name with the verb.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry that was the wrong clang link; it's this one that's applicable: https://clang.llvm.org/extra/clang-tidy/checks/readability-const-return-type.html

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed


/**
* Returns number of values during the period. This number may be an approximation
* of the number of samples in the histogram, it is not guaranteed that this will be
Expand Down Expand Up @@ -161,6 +167,11 @@ class ParentHistogram : public Histogram {
* Returns the bucket summary representation.
*/
virtual const std::string bucketSummary() const PURE;

/**
* Returns the bucket summary representation with nonoverlapping buckets.
*/
virtual const std::string nonoverlappingBucketSummary() const PURE;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

computeNonOverlappingBucketSummary()

remove the const prefix -- also do this for bucketSummary() which shouldn't have the const prefix either.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Removed the const prefix from quantileSummary() too

};

using ParentHistogramSharedPtr = RefcountPtr<ParentHistogram>;
Expand Down
12 changes: 12 additions & 0 deletions source/common/stats/histogram_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,18 @@ const std::vector<double>& HistogramStatisticsImpl::supportedQuantiles() const {
{0, 0.25, 0.5, 0.75, 0.90, 0.95, 0.99, 0.995, 0.999, 1});
}

const std::vector<uint64_t> HistogramStatisticsImpl::nonoverlappingComputedBuckets() const {
std::vector<uint64_t> buckets;
buckets.reserve(computed_buckets_.size());
uint64_t previous_computed_bucket = 0;
for (size_t i = 0; i < computed_buckets_.size(); ++i) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit; for (uint64_t computed_bucket : computed_buckets_)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

uint64_t current_computed_bucket = computed_buckets_[i];
buckets.push_back(current_computed_bucket - previous_computed_bucket);
previous_computed_bucket = current_computed_bucket;
}
return buckets;
}

std::string HistogramStatisticsImpl::quantileSummary() const {
std::vector<std::string> summary;
const std::vector<double>& supported_quantiles = supportedQuantiles();
Expand Down
1 change: 1 addition & 0 deletions source/common/stats/histogram_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class HistogramStatisticsImpl final : public HistogramStatistics, NonCopyable {
const std::vector<double>& computedQuantiles() const override { return computed_quantiles_; }
ConstSupportedBuckets& supportedBuckets() const override { return supported_buckets_; }
const std::vector<uint64_t>& computedBuckets() const override { return computed_buckets_; }
const std::vector<uint64_t> nonoverlappingComputedBuckets() const override;
uint64_t sampleCount() const override { return sample_count_; }
double sampleSum() const override { return sample_sum_; }

Expand Down
20 changes: 20 additions & 0 deletions source/common/stats/thread_local_store.cc
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,26 @@ const std::string ParentHistogramImpl::bucketSummary() const {
}
}

const std::string ParentHistogramImpl::nonoverlappingBucketSummary() const {
if (used()) {
std::vector<std::string> bucket_summary;
ConstSupportedBuckets& supported_buckets = interval_statistics_.supportedBuckets();
const std::vector<uint64_t> nonoverlapping_interval_buckets =
interval_statistics_.nonoverlappingComputedBuckets();
const std::vector<uint64_t> nonoverlapping_cumulative_buckets =
cumulative_statistics_.nonoverlappingComputedBuckets();
bucket_summary.reserve(supported_buckets.size());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Paranoia nit: ASSERT here that the 3 array lengths are the same, and make the loop go to their min value?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created ASSERT and looped to min value

for (size_t i = 0; i < supported_buckets.size(); ++i) {
bucket_summary.push_back(fmt::format("B{:g}({},{})", supported_buckets[i],
nonoverlapping_interval_buckets[i],
nonoverlapping_cumulative_buckets[i]));
}
return absl::StrJoin(bucket_summary, " ");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like we should have this API return the array, and have a separate layer that joins it as strings.

For example, if I have a richer HTML display of histograms I might like to be able to render these values graphically.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moreover, I think it'd be great to return a structured result that's an array of triples, and do the formatting in the stats handler (e.g. json-population vs emitting strings manually)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll start looking into this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added nonoverlappingComputedBuckets() to HistogramStatistics which can be used from the ParentHistogram. It returns an array with the desired values as output. Does this accomplish what you meant? I changed the nonoverlappingBucketSummary() to use the new method. Should I also move the summary to the stats handler? Originally I wrote it here because it is similar to the existing bucketSummary().

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is better but I still think the formatting of this could be removed from the histogram and go into the stats handler class.

E.g. one thing I was thinking (in a follow-up after an in-progress PR) is to have HTML-mode actually render the histograms graphically. So the more we do via structured API, and the less we commit to string representations inside the Histogram class, the more flexible we are about how to present the data in the stats handler.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved computeDisjointBucketSummary() to the stats handler.

} else {
return std::string("No recorded values");
}
}

void ParentHistogramImpl::addTlsHistogram(const TlsHistogramSharedPtr& hist_ptr) {
Thread::LockGuard lock(merge_lock_);
tls_histograms_.emplace_back(hist_ptr);
Expand Down
1 change: 1 addition & 0 deletions source/common/stats/thread_local_store.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ class ParentHistogramImpl : public MetricImpl<ParentHistogram> {
}
const std::string quantileSummary() const override;
const std::string bucketSummary() const override;
const std::string nonoverlappingBucketSummary() const override;

// Stats::Metric
SymbolTable& symbolTable() override;
Expand Down
19 changes: 15 additions & 4 deletions source/server/admin/stats_handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ Http::Code StatsHandler::handlerStats(absl::string_view url,
const Http::Utility::QueryParams params = Http::Utility::parseAndDecodeQueryString(url);

const bool used_only = params.find("usedonly") != params.end();
const bool histogram_buckets = params.find("histogram_buckets") != params.end();
absl::optional<std::regex> regex;
if (!Utility::filterParam(params, response, regex)) {
return Http::Code::BadRequest;
Expand Down Expand Up @@ -113,7 +114,8 @@ Http::Code StatsHandler::handlerStats(absl::string_view url,

if (!format_value.has_value()) {
// Display plain stats if format query param is not there.
statsAsText(all_stats, text_readouts, server_.stats().histograms(), used_only, regex, response);
statsAsText(all_stats, text_readouts, server_.stats().histograms(), used_only,
histogram_buckets, regex, response);
return Http::Code::OK;
}

Expand Down Expand Up @@ -174,7 +176,8 @@ Http::Code StatsHandler::handlerContention(absl::string_view,
void StatsHandler::statsAsText(const std::map<std::string, uint64_t>& all_stats,
const std::map<std::string, std::string>& text_readouts,
const std::vector<Stats::ParentHistogramSharedPtr>& histograms,
bool used_only, const absl::optional<std::regex>& regex,
bool used_only, bool histogram_buckets,
const absl::optional<std::regex>& regex,
Buffer::Instance& response) {
// Display plain stats if format query param is not there.
for (const auto& text_readout : text_readouts) {
Expand All @@ -187,8 +190,16 @@ void StatsHandler::statsAsText(const std::map<std::string, uint64_t>& all_stats,
std::map<std::string, std::string> all_histograms;
for (const Stats::ParentHistogramSharedPtr& histogram : histograms) {
if (shouldShowMetric(*histogram, used_only, regex)) {
auto insert = all_histograms.emplace(histogram->name(), histogram->quantileSummary());
ASSERT(insert.second); // No duplicates expected.
bool success = false;
// Display summary of nonoverlapping buckets if histogram_buckets query parameter is found.
if (histogram_buckets) {
success =
all_histograms.emplace(histogram->name(), histogram->nonoverlappingBucketSummary())
.second;
} else {
success = all_histograms.emplace(histogram->name(), histogram->quantileSummary()).second;
}
ASSERT(success); // No duplicates expected.
}
}
for (const auto& histogram : all_histograms) {
Expand Down
2 changes: 1 addition & 1 deletion source/server/admin/stats_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class StatsHandler : public HandlerContextBase {
void statsAsText(const std::map<std::string, uint64_t>& all_stats,
const std::map<std::string, std::string>& text_readouts,
const std::vector<Stats::ParentHistogramSharedPtr>& all_histograms,
bool used_only, const absl::optional<std::regex>& regex,
bool used_only, bool histogram_buckets, const absl::optional<std::regex>& regex,
Buffer::Instance& response);
};

Expand Down
28 changes: 28 additions & 0 deletions test/common/stats/thread_local_store_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1567,6 +1567,34 @@ TEST_F(HistogramTest, ParentHistogramBucketSummary) {
parent_histogram->bucketSummary());
}

TEST_F(HistogramTest, ParentHistogramNonoverlappingBucketSummary) {
ScopePtr scope1 = store_->createScope("scope1.");
Histogram& histogram =
store_->histogramFromString("histogram", Stats::Histogram::Unit::Unspecified);
store_->mergeHistograms([]() -> void {});
ASSERT_EQ(1, store_->histograms().size());
ParentHistogramSharedPtr parent_histogram = store_->histograms()[0];
EXPECT_EQ("No recorded values", parent_histogram->nonoverlappingBucketSummary());

EXPECT_CALL(sink_, onHistogramComplete(Ref(histogram), 200));
histogram.recordValue(200);

EXPECT_CALL(sink_, onHistogramComplete(Ref(histogram), 300));
histogram.recordValue(300);

store_->mergeHistograms([]() -> void {});

EXPECT_CALL(sink_, onHistogramComplete(Ref(histogram), 300));
histogram.recordValue(300);

store_->mergeHistograms([]() -> void {});

EXPECT_EQ("B0.5(0,0) B1(0,0) B5(0,0) B10(0,0) B25(0,0) B50(0,0) B100(0,0) B250(0,1) B500(1,2) "
"B1000(0,0) B2500(0,0) B5000(0,0) B10000(0,0) B30000(0,0) B60000(0,0) B300000(0,0) "
"B600000(0,0) B1.8e+06(0,0) B3.6e+06(0,0)",
parent_histogram->nonoverlappingBucketSummary());
}

class ThreadLocalRealThreadsTestBase : public Thread::RealThreadsTestHelper,
public ThreadLocalStoreNoMocksTestBase {
protected:
Expand Down
1 change: 1 addition & 0 deletions test/mocks/stats/mocks.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ class MockParentHistogram : public MockMetric<ParentHistogram> {
void merge() override {}
const std::string quantileSummary() const override { return ""; };
const std::string bucketSummary() const override { return ""; };
const std::string nonoverlappingBucketSummary() const override { return ""; };

MOCK_METHOD(bool, used, (), (const));
MOCK_METHOD(Histogram::Unit, unit, (), (const));
Expand Down
30 changes: 30 additions & 0 deletions test/server/admin/stats_handler_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,36 @@ TEST_P(AdminStatsTest, HandlerStatsPlainText) {
shutdownThreading();
}

TEST_P(AdminStatsTest, HandlerStatsPlainTextHistogramBuckets) {
const std::string url = "/stats?histogram_buckets";
Http::TestResponseHeaderMapImpl response_headers;
Buffer::OwnedImpl data;
MockAdminStream admin_stream;
Configuration::MockStatsConfig stats_config;
EXPECT_CALL(stats_config, flushOnAdmin()).WillRepeatedly(testing::Return(false));
MockInstance instance;
store_->initializeThreading(main_thread_dispatcher_, tls_);
EXPECT_CALL(instance, stats()).WillRepeatedly(testing::ReturnRef(*store_));
EXPECT_CALL(instance, statsConfig()).WillRepeatedly(testing::ReturnRef(stats_config));
StatsHandler handler(instance);

Stats::Histogram& h1 = store_->histogramFromString("h1", Stats::Histogram::Unit::Unspecified);

EXPECT_CALL(sink_, onHistogramComplete(Ref(h1), 200));
h1.recordValue(200);

store_->mergeHistograms([]() -> void {});

Http::Code code = handler.handlerStats(url, response_headers, data, admin_stream);
EXPECT_EQ(Http::Code::OK, code);
EXPECT_EQ("h1: B0.5(0,0) B1(0,0) B5(0,0) B10(0,0) B25(0,0) B50(0,0) B100(0,0) B250(1,1) "
"B500(0,0) B1000(0,0) B2500(0,0) B5000(0,0) B10000(0,0) B30000(0,0) B60000(0,0) "
"B300000(0,0) B600000(0,0) B1.8e+06(0,0) B3.6e+06(0,0)\n",
data.toString());

shutdownThreading();
}

TEST_P(AdminStatsTest, HandlerStatsJson) {
const std::string url = "/stats?format=json";
Http::TestResponseHeaderMapImpl response_headers;
Expand Down