diff --git a/CHANGELOG b/CHANGELOG index 24e6cf9a45..05362fa6d6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,8 @@ devel * fix issue #4583 - add AQL ASSERT and AQL WARN +* remove _admin/echo handler + * remove long disfunctional admin/long_echo handler * fixed Foxx API: diff --git a/Documentation/Books/HTTP/MiscellaneousFunctions/README.md b/Documentation/Books/HTTP/MiscellaneousFunctions/README.md index 7060afaeac..8548e86fc1 100644 --- a/Documentation/Books/HTTP/MiscellaneousFunctions/README.md +++ b/Documentation/Books/HTTP/MiscellaneousFunctions/README.md @@ -24,9 +24,6 @@ This is an overview of ArangoDB's HTTP interface for miscellaneous functions. @startDocuBlock JSF_get_admin_time - -@startDocuBlock JSF_get_admin_echo - @startDocuBlock JSF_get_admin_database_version diff --git a/Documentation/DocuBlocks/Rest/Administration/JSF_get_admin_echo.md b/Documentation/DocuBlocks/Rest/Administration/JSF_get_admin_echo.md deleted file mode 100644 index 145e630ac8..0000000000 --- a/Documentation/DocuBlocks/Rest/Administration/JSF_get_admin_echo.md +++ /dev/null @@ -1,22 +0,0 @@ - -@startDocuBlock JSF_get_admin_echo -@brief Send back what was sent in, headers, post body etc. - -@RESTHEADER{GET /_admin/echo, Return current request} - -@RESTDESCRIPTION - -The call returns an object with the following attributes: - -- *headers*: object with HTTP headers received - -- *requestType*: the HTTP request method (e.g. GET) - -- *parameters*: object with query parameters received - -@RESTRETURNCODES - -@RESTRETURNCODE{200} -Echo was returned successfully. -@endDocuBlock - diff --git a/UnitTests/HttpInterface/api-admin-spec.rb b/UnitTests/HttpInterface/api-admin-spec.rb index 13c97e24c5..192cc0fead 100644 --- a/UnitTests/HttpInterface/api-admin-spec.rb +++ b/UnitTests/HttpInterface/api-admin-spec.rb @@ -53,65 +53,6 @@ describe ArangoDB do end -################################################################################ -## /_admin/echo -################################################################################ - - context "checks /_admin/echo" do - - prefix = "api-system" - - it "using GET" do - cmd = "/_admin/echo" - doc = ArangoDB.log_get("#{prefix}-echo", cmd) - - doc.code.should eq(200) - doc.parsed_response['url'].should eq("/_admin/echo") - doc.parsed_response['path'].should eq("/") - doc.parsed_response['parameters'].should eq({}) - doc.parsed_response['requestType'].should eq("GET") - end - - it "using GET, with query parameter" do - cmd = "/_admin/echo?a=1" - doc = ArangoDB.log_get("#{prefix}-echo", cmd) - - doc.code.should eq(200) - doc.parsed_response['url'].should eq("/_admin/echo?a=1") - doc.parsed_response['path'].should eq("/") - doc.parsed_response['parameters'].should eq({ "a" => "1" }) - doc.parsed_response['requestType'].should eq("GET") - end - - it "using POST, with query parameters" do - cmd = "/_admin/echo?a=1&b=2&foo[]=bar&foo[]=baz" - body = "{\"foo\": \"bar\", \"baz\": { \"bump\": true, \"moo\": [ ] } }" - doc = ArangoDB.log_post("#{prefix}-echo", cmd, :body => body) - - doc.code.should eq(200) - doc.parsed_response['url'].should eq("/_admin/echo?a=1&b=2&foo[]=bar&foo[]=baz") - doc.parsed_response['path'].should eq("/") - doc.parsed_response['parameters'].should eq( { "a"=>"1", "b"=>"2", "foo"=>["bar", "baz"] } ) - doc.parsed_response['requestType'].should eq("POST") - doc.parsed_response['requestBody'].should eq("{\"foo\": \"bar\", \"baz\": { \"bump\": true, \"moo\": [ ] } }") - end - - it "using PUT, with headers" do - cmd = "/_admin/echo?" - body = "{ }" - headers = { "X-Foo" => "Bar", "x-meow" => "mOO" } - doc = ArangoDB.log_put("#{prefix}-echo", cmd, :body => body, :headers => headers) - - doc.code.should eq(200) - doc.parsed_response['url'].should eq("/_admin/echo?") - doc.parsed_response['path'].should eq("/") - doc.parsed_response['parameters'].should eq({ }) - doc.parsed_response['requestType'].should eq("PUT") - doc.parsed_response['requestBody'].should eq("{ }") - end - - end - ################################################################################ ## check whether admin interface is accessible ################################################################################ diff --git a/arangod/CMakeLists.txt b/arangod/CMakeLists.txt index 1fccc268aa..b78b248200 100644 --- a/arangod/CMakeLists.txt +++ b/arangod/CMakeLists.txt @@ -363,6 +363,7 @@ SET(ARANGOD_SOURCES RestHandler/RestAdminLogHandler.cpp RestHandler/RestAdminRoutingHandler.cpp RestHandler/RestAdminServerHandler.cpp + RestHandler/RestAdminStatisticsHandler.cpp RestHandler/RestAqlFunctionsHandler.cpp RestHandler/RestAqlUserFunctionsHandler.cpp RestHandler/RestAuthHandler.cpp @@ -374,7 +375,6 @@ SET(ARANGOD_SOURCES RestHandler/RestDebugHandler.cpp RestHandler/RestDemoHandler.cpp RestHandler/RestDocumentHandler.cpp - RestHandler/RestEchoHandler.cpp RestHandler/RestEdgesHandler.cpp RestHandler/RestEndpointHandler.cpp RestHandler/RestEngineHandler.cpp @@ -436,6 +436,7 @@ SET(ARANGOD_SOURCES Scheduler/SocketTcp.cpp Scheduler/Task.cpp Statistics/ConnectionStatistics.cpp + Statistics/Descriptions.cpp Statistics/RequestStatistics.cpp Statistics/ServerStatistics.cpp Statistics/StatisticsFeature.cpp diff --git a/arangod/GeneralServer/GeneralServerFeature.cpp b/arangod/GeneralServer/GeneralServerFeature.cpp index d57660efd2..c1058ca14c 100644 --- a/arangod/GeneralServer/GeneralServerFeature.cpp +++ b/arangod/GeneralServer/GeneralServerFeature.cpp @@ -45,6 +45,7 @@ #include "RestHandler/RestAdminLogHandler.h" #include "RestHandler/RestAdminRoutingHandler.h" #include "RestHandler/RestAdminServerHandler.h" +#include "RestHandler/RestAdminStatisticsHandler.h" #include "RestHandler/RestAqlFunctionsHandler.h" #include "RestHandler/RestAqlUserFunctionsHandler.h" #include "RestHandler/RestAuthHandler.h" @@ -55,7 +56,6 @@ #include "RestHandler/RestDebugHandler.h" #include "RestHandler/RestDemoHandler.h" #include "RestHandler/RestDocumentHandler.h" -#include "RestHandler/RestEchoHandler.h" #include "RestHandler/RestEdgesHandler.h" #include "RestHandler/RestEndpointHandler.h" #include "RestHandler/RestEngineHandler.h" @@ -526,9 +526,6 @@ void GeneralServerFeature::defineHandlers() { "/_admin/work-monitor", RestHandlerCreator::createNoData); - _handlerFactory->addHandler( - "/_admin/json-echo", RestHandlerCreator::createNoData); - #ifdef ARANGODB_ENABLE_FAILURE_TESTS // This handler is to activate SYS_DEBUG_FAILAT on DB servers _handlerFactory->addPrefixHandler( @@ -548,6 +545,14 @@ void GeneralServerFeature::defineHandlers() { _handlerFactory->addPrefixHandler( "/_admin/server", RestHandlerCreator::createNoData); + + _handlerFactory->addHandler( + "/_admin/statistics", + RestHandlerCreator::createNoData); + + _handlerFactory->addHandler( + "/_admin/statistics-description", + RestHandlerCreator::createNoData); // ........................................................................... // actions defined in v8 diff --git a/arangod/RestHandler/RestAdminStatisticsHandler.cpp b/arangod/RestHandler/RestAdminStatisticsHandler.cpp new file mode 100644 index 0000000000..960a882210 --- /dev/null +++ b/arangod/RestHandler/RestAdminStatisticsHandler.cpp @@ -0,0 +1,123 @@ +//////////////////////////////////////////////////////////////////////////////// +/// DISCLAIMER +/// +/// Copyright 2018 ArangoDB GmbH, Cologne, Germany +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// +/// Copyright holder is ArangoDB GmbH, Cologne, Germany +/// +/// @author Simon Grätzer +//////////////////////////////////////////////////////////////////////////////// + +#include "RestAdminStatisticsHandler.h" +#include "Statistics/Descriptions.h" +#include "Statistics/StatisticsFeature.h" + +using namespace arangodb; +using namespace arangodb::basics; +using namespace arangodb::rest; + +RestAdminStatisticsHandler::RestAdminStatisticsHandler(GeneralRequest* request, + GeneralResponse* response) + : RestBaseHandler(request, response) {} + +RestStatus RestAdminStatisticsHandler::execute() { + if (_request->requestType() != rest::RequestType::GET) { + generateError(rest::ResponseCode::METHOD_NOT_ALLOWED, + TRI_ERROR_HTTP_METHOD_NOT_ALLOWED); + return RestStatus::DONE; + } + + if (_request->requestPath() == "/_admin/statistics") { + getStatistics(); + } else if (_request->requestPath() == "/_admin/statistics-description") { + getStatisticsDescription(); + } else { + generateError(rest::ResponseCode::NOT_FOUND, TRI_ERROR_HTTP_NOT_FOUND); + } + + // this handler is done + return RestStatus::DONE; +} + +void RestAdminStatisticsHandler::getStatistics() { + stats::Descriptions const* desc = StatisticsFeature::descriptions(); + if (!desc) { + generateError(rest::ResponseCode::SERVICE_UNAVAILABLE, + TRI_ERROR_SHUTTING_DOWN); + return; + } + + VPackBuffer buffer; + VPackBuilder tmp(buffer); + tmp.add(VPackValue(VPackValueType::Object, true)); + + tmp.add("time", VPackValue(TRI_microtime())); + tmp.add("enabled", VPackValue(StatisticsFeature::enabled())); + + tmp.add("system", VPackValue(VPackValueType::Object, true)); + desc->processStatistics(tmp); + tmp.close(); // system + + tmp.add("client", VPackValue(VPackValueType::Object, true)); + desc->clientStatistics(tmp); + tmp.close(); // client + + tmp.add("http", VPackValue(VPackValueType::Object, true)); + desc->httpStatistics(tmp); + tmp.close(); // http + + tmp.add("server", VPackValue(VPackValueType::Object, true)); + desc->serverStatistics(tmp); + tmp.close(); // server + + tmp.add(StaticStrings::Error, VPackValue(false)); + tmp.add(StaticStrings::Code, VPackValue(static_cast(ResponseCode::OK))); + tmp.close(); // outer + generateResult(ResponseCode::OK, std::move(buffer)); +} + +void RestAdminStatisticsHandler::getStatisticsDescription() { + stats::Descriptions const* desc = StatisticsFeature::descriptions(); + if (!desc) { + generateError(rest::ResponseCode::SERVICE_UNAVAILABLE, + TRI_ERROR_SHUTTING_DOWN); + return; + } + + VPackBuffer buffer; + VPackBuilder tmp(buffer); + tmp.add(VPackValue(VPackValueType::Object)); + + tmp.add("groups", VPackValue(VPackValueType::Array, true)); + for (stats::Group const& group : desc->groups()) { + tmp.openObject(); + group.toVPack(tmp); + tmp.close(); + } + tmp.close(); // groups + + tmp.add("figures", VPackValue(VPackValueType::Array, true)); + for (stats::Figure const& figure : desc->figures()) { + tmp.openObject(); + figure.toVPack(tmp); + tmp.close(); + } + tmp.close(); // figures + + tmp.add(StaticStrings::Error, VPackValue(false)); + tmp.add(StaticStrings::Code, VPackValue(static_cast(ResponseCode::OK))); + tmp.close(); // outer + generateResult(ResponseCode::OK, std::move(buffer)); +} diff --git a/arangod/RestHandler/RestEchoHandler.h b/arangod/RestHandler/RestAdminStatisticsHandler.h similarity index 57% rename from arangod/RestHandler/RestEchoHandler.h rename to arangod/RestHandler/RestAdminStatisticsHandler.h index b19347e659..8a002250c2 100644 --- a/arangod/RestHandler/RestEchoHandler.h +++ b/arangod/RestHandler/RestAdminStatisticsHandler.h @@ -1,8 +1,7 @@ //////////////////////////////////////////////////////////////////////////////// /// DISCLAIMER /// -/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany -/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany +/// Copyright 2018 ArangoDB GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. @@ -18,23 +17,28 @@ /// /// Copyright holder is ArangoDB GmbH, Cologne, Germany /// -/// @author Achim Brandt +/// @author Simon Grätzer //////////////////////////////////////////////////////////////////////////////// -#ifndef ARANGOD_REST_HANDLER_REST_ECHO_HANDLER_H -#define ARANGOD_REST_HANDLER_REST_ECHO_HANDLER_H 1 +#ifndef ARANGOD_REST_HANDLER_REST_ADMIN_STATISTICS_HANDLER_H +#define ARANGOD_REST_HANDLER_REST_ADMIN_STATISTICS_HANDLER_H 1 -#include "RestHandler/RestVocbaseBaseHandler.h" +#include "Basics/Common.h" +#include "RestHandler/RestBaseHandler.h" namespace arangodb { -class RestEchoHandler : public arangodb::RestVocbaseBaseHandler { +class RestAdminStatisticsHandler : public RestBaseHandler { public: - RestEchoHandler(GeneralRequest*, GeneralResponse*); + RestAdminStatisticsHandler(GeneralRequest*, GeneralResponse*); public: - char const* name() const override final { return "RestEchoHandler"; } - bool isDirect() const override { return true; } - RestStatus execute() override; + char const* name() const override final { return "RestAdminStatisticsHandler"; } + bool isDirect() const override final { return false; } + RestStatus execute() override final; + + private: + void getStatistics(); + void getStatisticsDescription(); }; } diff --git a/arangod/RestHandler/RestBaseHandler.cpp b/arangod/RestHandler/RestBaseHandler.cpp index fa0b4061e3..e7d581ace5 100644 --- a/arangod/RestHandler/RestBaseHandler.cpp +++ b/arangod/RestHandler/RestBaseHandler.cpp @@ -107,7 +107,7 @@ void RestBaseHandler::generateOk(rest::ResponseCode code, try { VPackBuffer buffer; VPackBuilder tmp(buffer); - tmp.add(VPackValue(VPackValueType::Object)); + tmp.add(VPackValue(VPackValueType::Object, true)); tmp.add(StaticStrings::Error, VPackValue(false)); tmp.add(StaticStrings::Code, VPackValue(static_cast(code))); tmp.add("result", payload); @@ -127,7 +127,7 @@ void RestBaseHandler::generateOk(rest::ResponseCode code, try { VPackBuilder tmp; - tmp.add(VPackValue(VPackValueType::Object)); + tmp.add(VPackValue(VPackValueType::Object, true)); tmp.add(StaticStrings::Error, VPackValue(false)); tmp.add(StaticStrings::Code, VPackValue(static_cast(code))); tmp.close(); diff --git a/arangod/RestHandler/RestEchoHandler.cpp b/arangod/RestHandler/RestEchoHandler.cpp deleted file mode 100644 index e4ca811676..0000000000 --- a/arangod/RestHandler/RestEchoHandler.cpp +++ /dev/null @@ -1,50 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -/// DISCLAIMER -/// -/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany -/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany -/// -/// Licensed under the Apache License, Version 2.0 (the "License"); -/// you may not use this file except in compliance with the License. -/// You may obtain a copy of the License at -/// -/// http://www.apache.org/licenses/LICENSE-2.0 -/// -/// Unless required by applicable law or agreed to in writing, software -/// distributed under the License is distributed on an "AS IS" BASIS, -/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -/// See the License for the specific language governing permissions and -/// limitations under the License. -/// -/// Copyright holder is ArangoDB GmbH, Cologne, Germany -/// -/// @author Achim Brandt -//////////////////////////////////////////////////////////////////////////////// - -#include "RestEchoHandler.h" - -#include "ApplicationFeatures/ApplicationServer.h" -#include "Rest/HttpRequest.h" - -#include -#include - -using namespace arangodb; -using namespace arangodb::basics; -using namespace arangodb::rest; - -RestEchoHandler::RestEchoHandler(GeneralRequest* request, GeneralResponse* response) - : RestVocbaseBaseHandler(request, response) {} - -RestStatus RestEchoHandler::execute() { - bool parseSuccess = true; - std::shared_ptr parsedBody = - parseVelocyPackBody(parseSuccess); - - if (parseSuccess) { - VPackBuilder result; - generateResult(rest::ResponseCode::OK, parsedBody->slice()); - } - - return RestStatus::DONE; -} diff --git a/arangod/Statistics/Descriptions.cpp b/arangod/Statistics/Descriptions.cpp new file mode 100644 index 0000000000..0ff0cdd260 --- /dev/null +++ b/arangod/Statistics/Descriptions.cpp @@ -0,0 +1,468 @@ +//////////////////////////////////////////////////////////////////////////////// +/// DISCLAIMER +/// +/// Copyright 2018 ArangoDB GmbH, Cologne, Germany +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// +/// Copyright holder is ArangoDB GmbH, Cologne, Germany +/// +/// @author Simon Grätzer +//////////////////////////////////////////////////////////////////////////////// + +#include "Descriptions.h" +#include "Basics/Common.h" +#include "Basics/process-utils.h" +#include "Statistics/ConnectionStatistics.h" +#include "Statistics/RequestStatistics.h" +#include "Statistics/ServerStatistics.h" +#include "Statistics/StatisticsFeature.h" +#include "Scheduler/Scheduler.h" +#include "Scheduler/SchedulerFeature.h" +#include "V8Server/V8DealerFeature.h" + +#include +#include + +using namespace arangodb; + +std::string stats::fromGroupType(stats::GroupType gt) { + switch (gt) { + case stats::GroupType::System: + return "system"; + case stats::GroupType::Client: + return "client"; + case stats::GroupType::Http: + return "http"; + case stats::GroupType::Vst: + return "vst"; + case stats::GroupType::Server: + return "server"; + } +} + +void stats::Group::toVPack(velocypack::Builder& b) const { + b.add("group", VPackValue(stats::fromGroupType(type))); + b.add("name", VPackValue(name)); + b.add("description", VPackValue(description)); +} + +std::string stats::fromFigureType(stats::FigureType t) { + switch (t) { + case stats::FigureType::Current: + return "current"; + case stats::FigureType::Accumulated: + return "accumulated"; + case stats::FigureType::Distribution: + return "distribution"; + } +} + +std::string stats::fromUnit(stats::Unit u) { + switch (u) { + case stats::Unit::Seconds: + return "seconds"; + case stats::Unit::Bytes: + return "bytes"; + case stats::Unit::Percent: + return "percent"; + case stats::Unit::Number: + return "number"; + } +} + +void stats::Figure::toVPack(velocypack::Builder& b) const { + b.add("group", VPackValue(stats::fromGroupType(groupType))); + b.add("identifier", VPackValue(identifier)); + b.add("name", VPackValue(name)); + b.add("description", VPackValue(description)); + b.add("type", VPackValue(stats::fromFigureType(type))); + if (type == stats::FigureType::Distribution) { + TRI_ASSERT(!cuts.empty()); + b.add("cuts", VPackValue(VPackValueType::Array, true)); + for (double cut : cuts) { + b.add(VPackValue(cut)); + } + b.close(); + } + b.add("units", VPackValue(stats::fromUnit(units))); +} + +stats::Descriptions::Descriptions() + : _requestTimeCuts({0.01, 0.05, 0.1, 0.2, 0.5, 1.0}), + _connectionTimeCuts({0.1, 1.0, 60.0}), + _bytesSendCuts({250, 1000, 2 * 1000, 5 * 1000, 10 * 1000}), + _bytesReceivedCuts({250, 1000, 2 * 1000, 5 * 1000, 10 * 1000}) { + _groups.emplace_back(Group{stats::GroupType::System, "Process Statistics", + "Statistics about the ArangoDB process"}); + _groups.emplace_back(Group{stats::GroupType::Client, + "Client Connection Statistics", + "Statistics about the connections."}); + _groups.emplace_back(Group{stats::GroupType::Http, "HTTP Request Statistics", + "Statistics about the HTTP requests."}); + _groups.emplace_back(Group{stats::GroupType::Server, "Server Statistics", + "Statistics about the ArangoDB server"}); + + _figures.emplace_back(Figure{stats::GroupType::System, + "userTime", + "User Time", + "Amount of time that this process has been " + "scheduled in user mode, measured in seconds.", + stats::FigureType::Accumulated, + stats::Unit::Seconds, + {}}); + + _figures.emplace_back(Figure{stats::GroupType::System, + "systemTime", + "System Time", + "Amount of time that this process has been " + "scheduled in kernel mode, measured in seconds.", + stats::FigureType::Accumulated, + stats::Unit::Seconds, + {}}); + + _figures.emplace_back(Figure{stats::GroupType::System, + "numberOfThreads", + "Number of Threads", + "Number of threads in the arangod process.", + stats::FigureType::Current, + stats::Unit::Number, + {}}); + + _figures.emplace_back(Figure{ + stats::GroupType::System, + "residentSize", + "Resident Set Size", + "The total size of the number of pages the process has in real memory. " + "This is just the pages which count toward text, data, or stack space. " + "This does not include pages which have not been demand-loaded in, or " + "which are swapped out. The resident set size is reported in bytes.", + stats::FigureType::Current, + stats::Unit::Bytes, + {}}); + + _figures.emplace_back(Figure{stats::GroupType::System, + "residentSizePercent", + "Resident Set Size", + "The percentage of physical memory used by the " + "process as resident set size.", + stats::FigureType::Current, + stats::Unit::Percent, + {}}); + + _figures.emplace_back(Figure{stats::GroupType::System, + "virtualSize", + "Virtual Memory Size", + "On Windows, this figure contains the total " + "amount of memory that the memory manager has " + "committed for the arangod process. On other " + "systems, this figure contains The size of the " + "virtual memory the process is using.", + stats::FigureType::Current, + stats::Unit::Bytes, + {}}); + + _figures.emplace_back(Figure{stats::GroupType::System, + "minorPageFaults", + "Minor Page Faults", + "The number of minor faults the process has " + "made which have not required loading a memory " + "page from disk. This figure is not reported on " + "Windows.", + stats::FigureType::Accumulated, + stats::Unit::Number, + {}}); + + _figures.emplace_back(Figure{ + stats::GroupType::System, + "majorPageFaults", + "Major Page Faults", + "On Windows, this figure contains the total number of page faults. On " + "other system, this figure contains the number of major faults the " + "process has made which have required loading a memory page from disk.", + stats::FigureType::Accumulated, + stats::Unit::Number, + {}}); + + // ............................................................................. + // client statistics + // ............................................................................. + + _figures.emplace_back( + Figure{stats::GroupType::Client, + "httpConnections", + "Client Connections", + "The number of connections that are currently open.", + stats::FigureType::Current, + stats::Unit::Number, + {}}); + + _figures.emplace_back(Figure{ + stats::GroupType::Client, "totalTime", "Total Time", + "Total time needed to answer a request.", stats::FigureType::Distribution, + // cuts: internal.requestTimeDistribution, + stats::Unit::Seconds, _requestTimeCuts}); + + _figures.emplace_back(Figure{stats::GroupType::Client, "requestTime", + "Request Time", + "Request time needed to answer a request.", + stats::FigureType::Distribution, + // cuts: internal.requestTimeDistribution, + stats::Unit::Seconds, _requestTimeCuts}); + + _figures.emplace_back(Figure{ + stats::GroupType::Client, "queueTime", "Queue Time", + "Queue time needed to answer a request.", stats::FigureType::Distribution, + // cuts: internal.requestTimeDistribution, + stats::Unit::Seconds, _requestTimeCuts}); + + _figures.emplace_back(Figure{stats::GroupType::Client, "bytesSent", + "Bytes Sent", "Bytes sents for a request.", + stats::FigureType::Distribution, + // cuts: internal.bytesSentDistribution, + stats::Unit::Bytes, _bytesSendCuts}); + + _figures.emplace_back( + Figure{stats::GroupType::Client, "bytesReceived", "Bytes Received", + "Bytes receiveds for a request.", stats::FigureType::Distribution, + // cuts: internal.bytesReceivedDistribution, + stats::Unit::Bytes, _bytesReceivedCuts}); + + _figures.emplace_back(Figure{ + stats::GroupType::Client, "connectionTime", "Connection Time", + "Total connection time of a client.", stats::FigureType::Distribution, + // cuts: internal.connectionTimeDistribution, + stats::Unit::Seconds, _connectionTimeCuts}); + + _figures.emplace_back(Figure{stats::GroupType::Http, + "requestsTotal", + "Total requests", + "Total number of HTTP requests.", + stats::FigureType::Accumulated, + stats::Unit::Number, + {}}); + + _figures.emplace_back( + Figure{stats::GroupType::Http, + "requestsAsync", + "Async requests", + "Number of asynchronously executed HTTP requests.", + stats::FigureType::Accumulated, + stats::Unit::Number, + {}}); + + _figures.emplace_back(Figure{stats::GroupType::Http, + "requestsGet", + "HTTP GET requests", + "Number of HTTP GET requests.", + stats::FigureType::Accumulated, + stats::Unit::Number, + {}}); + + _figures.emplace_back(Figure{stats::GroupType::Http, + "requestsHead", + "HTTP HEAD requests", + "Number of HTTP HEAD requests.", + stats::FigureType::Accumulated, + stats::Unit::Number, + {}}); + + _figures.emplace_back(Figure{stats::GroupType::Http, + "requestsPost", + "HTTP POST requests", + "Number of HTTP POST requests.", + stats::FigureType::Accumulated, + stats::Unit::Number, + {}}); + + _figures.emplace_back(Figure{stats::GroupType::Http, + "requestsPut", + "HTTP PUT requests", + "Number of HTTP PUT requests.", + stats::FigureType::Accumulated, + stats::Unit::Number, + {}}); + + _figures.emplace_back(Figure{stats::GroupType::Http, + "requestsPatch", + "HTTP PATCH requests", + "Number of HTTP PATCH requests.", + stats::FigureType::Accumulated, + stats::Unit::Number, + {}}); + + _figures.emplace_back(Figure{stats::GroupType::Http, + "requestsDelete", + "HTTP DELETE requests", + "Number of HTTP DELETE requests.", + stats::FigureType::Accumulated, + stats::Unit::Number, + {}}); + + _figures.emplace_back(Figure{stats::GroupType::Http, + "requestsOptions", + "HTTP OPTIONS requests", + "Number of HTTP OPTIONS requests.", + stats::FigureType::Accumulated, + stats::Unit::Number, + {}}); + + _figures.emplace_back(Figure{stats::GroupType::Http, + "requestsOther", + "other HTTP requests", + "Number of other HTTP requests.", + stats::FigureType::Accumulated, + stats::Unit::Number, + {}}); + + // ............................................................................. + // server statistics + // ............................................................................. + + _figures.emplace_back(Figure{stats::GroupType::Server, + "uptime", + "Server Uptime", + "Number of seconds elapsed since server start.", + stats::FigureType::Current, + stats::Unit::Seconds, + {}}); + + _figures.emplace_back(Figure{stats::GroupType::Server, + "physicalMemory", + "Physical Memory", + "Physical memory in bytes.", + stats::FigureType::Current, + stats::Unit::Bytes, + {}}); +} + +void stats::Descriptions::serverStatistics(velocypack::Builder& b) const { + ServerStatistics info = ServerStatistics::statistics(); + b.add("uptime", VPackValue(info._uptime)); + b.add("physicalMemory", VPackValue((double)TRI_PhysicalMemory)); + + V8DealerFeature* dealer = + application_features::ApplicationServer::getFeature("V8Dealer"); + + b.add("v8Context", VPackValue(VPackValueType::Object, true)); + auto v8Counters = dealer->getCurrentContextNumbers(); + b.add( "available", VPackValue(static_cast(v8Counters.available))); + b.add( "busy", VPackValue(static_cast(v8Counters.busy))); + b.add( "dirty", VPackValue(static_cast(v8Counters.dirty))); + b.add( "free", VPackValue(static_cast(v8Counters.free))); + b.add( "max", VPackValue(static_cast(v8Counters.max))); + b.close(); + + b.add("threads", VPackValue(VPackValueType::Object, true)); + + auto countersRaw = SchedulerFeature::SCHEDULER->getCounters(); + b.add("running", VPackValue(static_cast(rest::Scheduler::numRunning(countersRaw)))); + b.add("working", VPackValue(static_cast(rest::Scheduler::numWorking(countersRaw)))); + b.add("blocked", VPackValue(static_cast(rest::Scheduler::numBlocked(countersRaw)))); + b.add("queued", VPackValue(static_cast(SchedulerFeature::SCHEDULER->numQueued()))); + b.close(); +} + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief fills the distribution +//////////////////////////////////////////////////////////////////////////////// + +static void FillDistribution(VPackBuilder& b, std::string const& name, + basics::StatisticsDistribution const& dist) { + b.add(name, VPackValue(VPackValueType::Object, true)); + b.add("sum", VPackValue(dist._total)); + b.add("count", VPackValue((double)dist._count)); + b.add("counts", VPackValue(VPackValueType::Array, true)); + for (std::vector::const_iterator i = dist._counts.begin(); + i != dist._counts.end(); ++i) { + b.add(VPackValue(*i)); + } + b.close(); + b.close(); +} + +void stats::Descriptions::clientStatistics(velocypack::Builder& b) const { + basics::StatisticsCounter httpConnections; + basics::StatisticsCounter totalRequests; + std::vector methodRequests; + basics::StatisticsCounter asyncRequests; + basics::StatisticsDistribution connectionTime; + + // FIXME why are httpConnections in here ? + ConnectionStatistics::fill(httpConnections, totalRequests, methodRequests, + asyncRequests, connectionTime); + + b.add("httpConnections", VPackValue((double)httpConnections._count)); + FillDistribution(b, "connectionTime", connectionTime); + + basics::StatisticsDistribution totalTime; + basics::StatisticsDistribution requestTime; + basics::StatisticsDistribution queueTime; + basics::StatisticsDistribution ioTime; + basics::StatisticsDistribution bytesSent; + basics::StatisticsDistribution bytesReceived; + + RequestStatistics::fill(totalTime, requestTime, queueTime, ioTime, bytesSent, + bytesReceived); + + FillDistribution(b, "totalTime", totalTime); + FillDistribution(b, "requestTime", requestTime); + FillDistribution(b, "queueTime", queueTime); + FillDistribution(b, "ioTime", ioTime); + FillDistribution(b, "bytesSent", bytesSent); + FillDistribution(b, "bytesReceived", bytesReceived); +} + +void stats::Descriptions::httpStatistics(velocypack::Builder& b) const { + basics::StatisticsCounter httpConnections; + basics::StatisticsCounter totalRequests; + std::vector methodRequests; + basics::StatisticsCounter asyncRequests; + basics::StatisticsDistribution connectionTime; + + ConnectionStatistics::fill(httpConnections, totalRequests, methodRequests, + asyncRequests, connectionTime); + + // request counters + b.add("requestsTotal", VPackValue((double)totalRequests._count)); + b.add("requestsAsync", VPackValue((double)asyncRequests._count)); + b.add("requestsGet", VPackValue((double)methodRequests[(int)rest::RequestType::GET]._count)); + b.add("requestsHead", VPackValue((double)methodRequests[(int)rest::RequestType::HEAD]._count)); + b.add("requestsPost", VPackValue((double)methodRequests[(int)rest::RequestType::POST]._count)); + b.add("requestsPut", VPackValue((double)methodRequests[(int)rest::RequestType::PUT]._count)); + b.add("requestsPatch", VPackValue((double)methodRequests[(int)rest::RequestType::PATCH]._count)); + b.add("requestsDelete", VPackValue((double)methodRequests[(int)rest::RequestType::DELETE_REQ]._count)); + b.add("requestsOptions", VPackValue((double)methodRequests[(int)rest::RequestType::OPTIONS]._count)); + b.add("requestsOther", VPackValue((double)methodRequests[(int)rest::RequestType::ILLEGAL]._count)); +} + + +void stats::Descriptions::processStatistics(VPackBuilder& b) const { + ProcessInfo info = TRI_ProcessInfoSelf(); + double rss = (double)info._residentSize; + double rssp = 0; + + if (TRI_PhysicalMemory != 0) { + rssp = rss / TRI_PhysicalMemory; + } + + b.add("minorPageFaults", VPackValue((double)info._minorPageFaults)); + b.add("majorPageFaults", VPackValue((double)info._majorPageFaults)); + b.add("userTime", VPackValue((double)info._userTime / (double)info._scClkTck)); + b.add("systemTime", VPackValue((double)info._systemTime / (double)info._scClkTck)); + b.add("numberOfThreads", VPackValue((double)info._numberThreads)); + b.add("residentSize", VPackValue(rss)); + b.add("residentSizePercent", VPackValue(rssp)); + b.add("virtualSize", VPackValue((double)info._virtualSize)); +} diff --git a/arangod/Statistics/Descriptions.h b/arangod/Statistics/Descriptions.h new file mode 100644 index 0000000000..707ac147b0 --- /dev/null +++ b/arangod/Statistics/Descriptions.h @@ -0,0 +1,95 @@ +//////////////////////////////////////////////////////////////////////////////// +/// DISCLAIMER +/// +/// Copyright 2018 ArangoDB GmbH, Cologne, Germany +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// +/// Copyright holder is ArangoDB GmbH, Cologne, Germany +/// +/// @author Simon Grätzer +//////////////////////////////////////////////////////////////////////////////// + +#ifndef ARANGOD_STATISTICS_DESCRIPTIONS_H +#define ARANGOD_STATISTICS_DESCRIPTIONS_H 1 + +#include +#include + +namespace arangodb { +namespace stats { + +enum class GroupType { System, Client, Http, Vst, Server }; + +std::string fromGroupType(stats::GroupType); + +struct Group { + stats::GroupType type; + std::string name; + std::string description; + + public: + void toVPack(velocypack::Builder&) const; +}; + +enum class FigureType : char { Current, Accumulated, Distribution }; + +std::string fromFigureType(stats::FigureType); + +enum class Unit : char { Seconds, Bytes, Percent, Number }; + +std::string fromUnit(stats::Unit); + +struct Figure { + stats::GroupType groupType; + std::string identifier; + std::string name; + std::string description; + stats::FigureType type; + stats::Unit units; + + std::vector cuts; + + public: + void toVPack(velocypack::Builder&) const; +}; + +class Descriptions final { + +public: + Descriptions(); + + std::vector const& groups() const { return _groups; } + + std::vector const& figures() const { + return _figures; + } + + void serverStatistics(velocypack::Builder&) const; + void clientStatistics(velocypack::Builder&) const; + void httpStatistics(velocypack::Builder&) const; + void processStatistics(velocypack::Builder&) const; + + private: + std::vector _requestTimeCuts; + std::vector _connectionTimeCuts; + std::vector _bytesSendCuts; + std::vector _bytesReceivedCuts; + + std::vector _groups; + std::vector _figures; +}; +} +} + +#endif diff --git a/arangod/Statistics/StatisticsFeature.cpp b/arangod/Statistics/StatisticsFeature.cpp index 80a03f0df9..b5a478b882 100644 --- a/arangod/Statistics/StatisticsFeature.cpp +++ b/arangod/Statistics/StatisticsFeature.cpp @@ -25,6 +25,7 @@ #include "ProgramOptions/ProgramOptions.h" #include "ProgramOptions/Section.h" #include "Statistics/ConnectionStatistics.h" +#include "Statistics/Descriptions.h" #include "Statistics/RequestStatistics.h" #include "Statistics/ServerStatistics.h" #include "Statistics/StatisticsWorker.h" @@ -117,7 +118,9 @@ StatisticsFeature* StatisticsFeature::STATISTICS = nullptr; StatisticsFeature::StatisticsFeature( application_features::ApplicationServer* server) - : ApplicationFeature(server, "Statistics"), _statistics(true) { + : ApplicationFeature(server, "Statistics"), + _statistics(true), + _descriptions(new stats::Descriptions()) { startsAfter("Logger"); startsAfter("Aql"); } diff --git a/arangod/Statistics/StatisticsFeature.h b/arangod/Statistics/StatisticsFeature.h index 4b0db870e3..3b6cffa61a 100644 --- a/arangod/Statistics/StatisticsFeature.h +++ b/arangod/Statistics/StatisticsFeature.h @@ -45,6 +45,9 @@ extern StatisticsVector TRI_ConnectionTimeDistributionVectorStatistics; extern StatisticsVector TRI_RequestTimeDistributionVectorStatistics; extern std::vector TRI_MethodRequestsStatistics; } +namespace stats{ + class Descriptions; +} class StatisticsThread; class StatisticsWorker; @@ -70,12 +73,20 @@ class StatisticsFeature final void start() override final; void unprepare() override final; + static stats::Descriptions const* descriptions() { + if (STATISTICS != nullptr) { + return STATISTICS->_descriptions.get(); + } + return nullptr; + } + public: void disableStatistics() { _statistics = false; } private: bool _statistics; + std::unique_ptr _descriptions; std::unique_ptr _statisticsThread; std::unique_ptr _statisticsWorker; }; diff --git a/js/actions/_admin/app.js b/js/actions/_admin/app.js index 8ff307e23c..0b4214323d 100644 --- a/js/actions/_admin/app.js +++ b/js/actions/_admin/app.js @@ -32,7 +32,6 @@ var internal = require('internal'); var console = require('console'); var actions = require('@arangodb/actions'); -var arangodb = require('@arangodb'); // ////////////////////////////////////////////////////////////////////////////// // / @brief was docuBlock JSF_get_admin_time @@ -47,375 +46,6 @@ actions.defineHttp({ } }); -// ////////////////////////////////////////////////////////////////////////////// -// / @brief was docuBlock JSF_get_admin_echo -// ////////////////////////////////////////////////////////////////////////////// - -actions.defineHttp({ - url: '_admin/echo', - prefix: true, - - callback: function (req, res) { - res.responseCode = actions.HTTP_OK; - res.contentType = 'application/json; charset=utf-8'; - req.rawRequestBody = require('internal').rawRequestBody(req); - res.body = JSON.stringify(req); - } -}); - -// ////////////////////////////////////////////////////////////////////////////// -// / @brief was docuBlock JSF_get_admin_statistics -// ////////////////////////////////////////////////////////////////////////////// - -actions.defineHttp({ - url: '_admin/statistics', - prefix: false, - - callback: function (req, res) { - var result; - - try { - result = {}; - result.time = internal.time(); - result.enabled = internal.enabledStatistics(); - result.system = internal.processStatistics(); - result.client = internal.clientStatistics(); - result.http = internal.httpStatistics(); - result.server = internal.serverStatistics(); - - actions.resultOk(req, res, actions.HTTP_OK, result); - } catch (err) { - actions.resultException(req, res, err, undefined, false); - } - } -}); - -// ////////////////////////////////////////////////////////////////////////////// -// / @brief was docuBlock JSF_get_admin_statistics_description -// ////////////////////////////////////////////////////////////////////////////// - -actions.defineHttp({ - url: '_admin/statistics-description', - prefix: false, - - callback: function (req, res) { - var result; - - try { - result = { - groups: [ - { - group: 'system', - name: 'Process Statistics', - description: 'Statistics about the ArangoDB process' - }, - - { - group: 'client', - name: 'Client Connection Statistics', - description: 'Statistics about the connections.' - }, - - { - group: 'http', - name: 'HTTP Request Statistics', - description: 'Statistics about the HTTP requests.' - }, - - { - group: 'server', - name: 'Server Statistics', - description: 'Statistics about the ArangoDB server' - } - - ], - - figures: [ - - // ............................................................................. - // system statistics - // ............................................................................. - - { - group: 'system', - identifier: 'userTime', - name: 'User Time', - description: 'Amount of time that this process has been scheduled in user mode, ' + - 'measured in seconds.', - type: 'accumulated', - units: 'seconds' - }, - - { - group: 'system', - identifier: 'systemTime', - name: 'System Time', - description: 'Amount of time that this process has been scheduled in kernel mode, ' + - 'measured in seconds.', - type: 'accumulated', - units: 'seconds' - }, - - { - group: 'system', - identifier: 'numberOfThreads', - name: 'Number of Threads', - description: 'Number of threads in the arangod process.', - type: 'current', - units: 'number' - }, - - { - group: 'system', - identifier: 'residentSize', - name: 'Resident Set Size', - description: 'The total size of the number of pages the process has in real memory. ' + - 'This is just the pages which count toward text, data, or stack space. ' + - 'This does not include pages which have not been demand-loaded in, ' + - 'or which are swapped out. The resident set size is reported in bytes.', - type: 'current', - units: 'bytes' - }, - - { - group: 'system', - identifier: 'residentSizePercent', - name: 'Resident Set Size', - description: 'The percentage of physical memory used by the process as resident ' + - 'set size.', - type: 'current', - units: 'percent' - }, - - { - group: 'system', - identifier: 'virtualSize', - name: 'Virtual Memory Size', - description: 'On Windows, this figure contains the total amount of memory that the ' + - 'memory manager has committed for the arangod process. On other ' + - 'systems, this figure contains The size of the virtual memory the ' + - 'process is using.', - type: 'current', - units: 'bytes' - }, - - { - group: 'system', - identifier: 'minorPageFaults', - name: 'Minor Page Faults', - description: 'The number of minor faults the process has made which have ' + - 'not required loading a memory page from disk. This figure is ' + - 'not reported on Windows.', - type: 'accumulated', - units: 'number' - }, - - { - group: 'system', - identifier: 'majorPageFaults', - name: 'Major Page Faults', - description: 'On Windows, this figure contains the total number of page faults. ' + - 'On other system, this figure contains the number of major faults the ' + - 'process has made which have required loading a memory page from disk.', - type: 'accumulated', - units: 'number' - }, - - // ............................................................................. - // client statistics - // ............................................................................. - - { - group: 'client', - identifier: 'httpConnections', - name: 'Client Connections', - description: 'The number of connections that are currently open.', - type: 'current', - units: 'number' - }, - - { - group: 'client', - identifier: 'totalTime', - name: 'Total Time', - description: 'Total time needed to answer a request.', - type: 'distribution', - cuts: internal.requestTimeDistribution, - units: 'seconds' - }, - - { - group: 'client', - identifier: 'requestTime', - name: 'Request Time', - description: 'Request time needed to answer a request.', - type: 'distribution', - cuts: internal.requestTimeDistribution, - units: 'seconds' - }, - - { - group: 'client', - identifier: 'queueTime', - name: 'Queue Time', - description: 'Queue time needed to answer a request.', - type: 'distribution', - cuts: internal.requestTimeDistribution, - units: 'seconds' - }, - - { - group: 'client', - identifier: 'bytesSent', - name: 'Bytes Sent', - description: 'Bytes sents for a request.', - type: 'distribution', - cuts: internal.bytesSentDistribution, - units: 'bytes' - }, - - { - group: 'client', - identifier: 'bytesReceived', - name: 'Bytes Received', - description: 'Bytes receiveds for a request.', - type: 'distribution', - cuts: internal.bytesReceivedDistribution, - units: 'bytes' - }, - - { - group: 'client', - identifier: 'connectionTime', - name: 'Connection Time', - description: 'Total connection time of a client.', - type: 'distribution', - cuts: internal.connectionTimeDistribution, - units: 'seconds' - }, - - { - group: 'http', - identifier: 'requestsTotal', - name: 'Total requests', - description: 'Total number of HTTP requests.', - type: 'accumulated', - units: 'number' - }, - - { - group: 'http', - identifier: 'requestsAsync', - name: 'Async requests', - description: 'Number of asynchronously executed HTTP requests.', - type: 'accumulated', - units: 'number' - }, - - { - group: 'http', - identifier: 'requestsGet', - name: 'HTTP GET requests', - description: 'Number of HTTP GET requests.', - type: 'accumulated', - units: 'number' - }, - - { - group: 'http', - identifier: 'requestsHead', - name: 'HTTP HEAD requests', - description: 'Number of HTTP HEAD requests.', - type: 'accumulated', - units: 'number' - }, - - { - group: 'http', - identifier: 'requestsPost', - name: 'HTTP POST requests', - description: 'Number of HTTP POST requests.', - type: 'accumulated', - units: 'number' - }, - - { - group: 'http', - identifier: 'requestsPut', - name: 'HTTP PUT requests', - description: 'Number of HTTP PUT requests.', - type: 'accumulated', - units: 'number' - }, - - { - group: 'http', - identifier: 'requestsPatch', - name: 'HTTP PATCH requests', - description: 'Number of HTTP PATCH requests.', - type: 'accumulated', - units: 'number' - }, - - { - group: 'http', - identifier: 'requestsDelete', - name: 'HTTP DELETE requests', - description: 'Number of HTTP DELETE requests.', - type: 'accumulated', - units: 'number' - }, - - { - group: 'http', - identifier: 'requestsOptions', - name: 'HTTP OPTIONS requests', - description: 'Number of HTTP OPTIONS requests.', - type: 'accumulated', - units: 'number' - }, - - { - group: 'http', - identifier: 'requestsOther', - name: 'other HTTP requests', - description: 'Number of other HTTP requests.', - type: 'accumulated', - units: 'number' - }, - - // ............................................................................. - // server statistics - // ............................................................................. - - { - group: 'server', - identifier: 'uptime', - name: 'Server Uptime', - description: 'Number of seconds elapsed since server start.', - type: 'current', - units: 'seconds' - }, - - { - group: 'server', - identifier: 'physicalMemory', - name: 'Physical Memory', - description: 'Physical memory in bytes.', - type: 'current', - units: 'bytes' - } - - ] - }; - - actions.resultOk(req, res, actions.HTTP_OK, result); - } catch (err) { - actions.resultException(req, res, err, undefined, false); - } - } -}); - // ////////////////////////////////////////////////////////////////////////////// // / @brief was docuBlock JSF_post_admin_execute // //////////////////////////////////////////////////////////////////////////////