Skip to content

Commit

Permalink
admin: added json output support for stats handler (envoyproxy#1865)
Browse files Browse the repository at this point in the history
Signed-off-by: Rama <[email protected]>
  • Loading branch information
ramaraochavali authored and mattklein123 committed Oct 19, 2017
1 parent e366bcf commit d5695aa
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 4 deletions.
Empty file modified .clang-format
100644 → 100755
Empty file.
4 changes: 4 additions & 0 deletions docs/operations/admin.rst
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,7 @@ The fields are:
output as Envoy currently has no built in histogram support and relies on statsd for
aggregation. This command is very useful for local debugging. See :ref:`here <operations_stats>`
for more information.

.. http:get:: /stats?format=json
Outputs /stats in JSON format. This can be used for programmatic access of stats.
56 changes: 52 additions & 4 deletions source/server/http/admin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,19 @@
#include "common/upstream/host_utility.h"

#include "fmt/format.h"

// TODO(mattklein123): Switch to JSON interface methods and remove rapidjson dependency.
#include "rapidjson/document.h"
#include "rapidjson/error/en.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/reader.h"
#include "rapidjson/schema.h"
#include "rapidjson/stream.h"
#include "rapidjson/stringbuffer.h"
#include "spdlog/spdlog.h"

using namespace rapidjson;

namespace Envoy {
namespace Server {

Expand Down Expand Up @@ -267,9 +278,11 @@ Http::Code AdminImpl::handlerServerInfo(const std::string&, Buffer::Instance& re
return Http::Code::OK;
}

Http::Code AdminImpl::handlerStats(const std::string&, Buffer::Instance& response) {
Http::Code AdminImpl::handlerStats(const std::string& url, Buffer::Instance& response) {
// We currently don't support timers locally (only via statsd) so just group all the counters
// and gauges together, alpha sort them, and spit them out.
Http::Code rc = Http::Code::OK;
const Http::Utility::QueryParams params = Http::Utility::parseQueryString(url);
std::map<std::string, uint64_t> all_stats;
for (const Stats::CounterSharedPtr& counter : server_.stats().counters()) {
all_stats.emplace(counter->name(), counter->value());
Expand All @@ -279,11 +292,46 @@ Http::Code AdminImpl::handlerStats(const std::string&, Buffer::Instance& respons
all_stats.emplace(gauge->name(), gauge->value());
}

for (auto stat : all_stats) {
response.add(fmt::format("{}: {}\n", stat.first, stat.second));
if (params.size() == 0) {
// No Arguments so use the standard.
for (auto stat : all_stats) {
response.add(fmt::format("{}: {}\n", stat.first, stat.second));
}
} else {
const std::string format_key = params.begin()->first;
const std::string format_value = params.begin()->second;
if (format_key == "format" && format_value == "json") {
response.add(statsAsJson(all_stats));
} else {
response.add("usage: /stats?format=json \n");
response.add("\n");
rc = Http::Code::NotFound;
}
}
return rc;
}

return Http::Code::OK;
std::string AdminImpl::statsAsJson(const std::map<std::string, uint64_t>& all_stats) {
rapidjson::Document document;
document.SetObject();
rapidjson::Value stats_array(rapidjson::kArrayType);
rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
for (auto stat : all_stats) {
Value stat_obj;
stat_obj.SetObject();
Value stat_name;
stat_name.SetString(stat.first.c_str(), allocator);
stat_obj.AddMember("name", stat_name, allocator);
Value stat_value;
stat_value.SetInt(stat.second);
stat_obj.AddMember("value", stat_value, allocator);
stats_array.PushBack(stat_obj, allocator);
}
document.AddMember("stats", stats_array, allocator);
rapidjson::StringBuffer strbuf;
rapidjson::PrettyWriter<StringBuffer> writer(strbuf);
document.Accept(writer);
return strbuf.GetString();
}

Http::Code AdminImpl::handlerQuitQuitQuit(const std::string&, Buffer::Instance& response) {
Expand Down
1 change: 1 addition & 0 deletions source/server/http/admin.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ class AdminImpl : public Admin,
void addOutlierInfo(const std::string& cluster_name,
const Upstream::Outlier::Detector* outlier_detector,
Buffer::Instance& response);
std::string statsAsJson(const std::map<std::string, uint64_t>& all_stats);

/**
* URL handlers.
Expand Down
12 changes: 12 additions & 0 deletions test/integration/integration_admin_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,18 @@ TEST_P(IntegrationAdminTest, Admin) {
EXPECT_TRUE(response->complete());
EXPECT_STREQ("200", response->headers().Status()->value().c_str());

response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/stats?format=blah",
"", downstreamProtocol(), version_);
EXPECT_TRUE(response->complete());
EXPECT_STREQ("404", response->headers().Status()->value().c_str());

response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/stats?format=json",
"", downstreamProtocol(), version_);
EXPECT_TRUE(response->complete());
EXPECT_STREQ("200", response->headers().Status()->value().c_str());
Json::ObjectSharedPtr statsjson = Json::Factory::loadFromString(response->body());
EXPECT_TRUE(statsjson->hasObject("stats"));

response = IntegrationUtil::makeSingleRequest(lookupPort("admin"), "GET", "/clusters", "",
downstreamProtocol(), version_);
EXPECT_TRUE(response->complete());
Expand Down

0 comments on commit d5695aa

Please sign in to comment.