diff --git a/CHANGELOG b/CHANGELOG index c271c2638685..0834e360f02b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,10 @@ v3.7.15 (XXXX-XX-XX) -------------------- +* Make statistics available in metrics API even if `--server.statistics` is set + to `false`. This allows clients to fetch some process statistics metrics from + instances that have statistics aggregation and storage turned off. + * Updated arangosync to 1.8.0. * Added protocol specific metrics: histogram about request body size, total diff --git a/arangod/RestServer/MetricsFeature.cpp b/arangod/RestServer/MetricsFeature.cpp index 5b378cc8a669..662f970bc5f8 100644 --- a/arangod/RestServer/MetricsFeature.cpp +++ b/arangod/RestServer/MetricsFeature.cpp @@ -85,9 +85,7 @@ void MetricsFeature::validateOptions(std::shared_ptr) { } } - void MetricsFeature::toPrometheus(std::string& result) const { - // minimize reallocs result.reserve(32768); @@ -100,10 +98,8 @@ void MetricsFeature::toPrometheus(std::string& result) const { // StatisticsFeature auto& sf = server().getFeature(); - if (sf.enabled()) { - sf.toPrometheus(result, std::chrono::duration( - std::chrono::system_clock::now().time_since_epoch()).count()); - } + sf.toPrometheus(result, std::chrono::duration( + std::chrono::system_clock::now().time_since_epoch()).count()); // RocksDBEngine auto es = EngineSelectorFeature::ENGINE; diff --git a/arangod/Statistics/ConnectionStatistics.cpp b/arangod/Statistics/ConnectionStatistics.cpp index 853f0ad0ffc4..439e0e0af29d 100644 --- a/arangod/Statistics/ConnectionStatistics.cpp +++ b/arangod/Statistics/ConnectionStatistics.cpp @@ -72,11 +72,6 @@ ConnectionStatistics::Item ConnectionStatistics::acquire() { } void ConnectionStatistics::getSnapshot(Snapshot& snapshot) { - if (!StatisticsFeature::enabled()) { - // all the below objects may be deleted if we don't have statistics enabled - return; - } - snapshot.httpConnections = statistics::HttpConnections; snapshot.totalRequests = statistics::TotalRequests; snapshot.totalRequestsSuperuser = statistics::TotalRequestsSuperuser; diff --git a/arangod/Statistics/RequestStatistics.cpp b/arangod/Statistics/RequestStatistics.cpp index a14977e3ef9f..434090e581ce 100644 --- a/arangod/Statistics/RequestStatistics.cpp +++ b/arangod/Statistics/RequestStatistics.cpp @@ -195,11 +195,6 @@ void RequestStatistics::release() { } void RequestStatistics::getSnapshot(Snapshot& snapshot, stats::RequestStatisticsSource source) { - if (!StatisticsFeature::enabled()) { - // all the below objects may be deleted if we don't have statistics enabled - return; - } - statistics::RequestFigures& figures = source == stats::RequestStatisticsSource::USER ? statistics::UserRequestFigures : statistics::SuperuserRequestFigures; diff --git a/arangod/Statistics/StatisticsFeature.cpp b/arangod/Statistics/StatisticsFeature.cpp index 2015f4746b51..d637e55c26c5 100644 --- a/arangod/Statistics/StatisticsFeature.cpp +++ b/arangod/Statistics/StatisticsFeature.cpp @@ -25,8 +25,10 @@ #include "ApplicationFeatures/ApplicationServer.h" #include "Aql/Query.h" #include "Aql/QueryString.h" +#include "Basics/PhysicalMemory.h" #include "Basics/StaticStrings.h" #include "Basics/application-exit.h" +#include "Basics/process-utils.h" #include "Cluster/ClusterFeature.h" #include "Cluster/ServerState.h" #include "FeaturePhases/AqlFeaturePhase.h" @@ -43,13 +45,16 @@ #include "Statistics/RequestStatistics.h" #include "Statistics/ServerStatistics.h" #include "Statistics/StatisticsWorker.h" +#include "Statistics/figures.h" #include "Transaction/StandaloneContext.h" #include "Utils/ExecContext.h" #include "V8Server/V8DealerFeature.h" #include "VocBase/vocbase.h" #include +#include #include +#include using namespace arangodb; using namespace arangodb::application_features; @@ -64,6 +69,161 @@ namespace { std::string const stats15Query = "/*stats15*/ FOR s IN @@collection FILTER s.time > @start FILTER s.clusterId IN @clusterIds SORT s.time COLLECT clusterId = s.clusterId INTO clientConnections = s.client.httpConnections LET clientConnectionsCurrent = LAST(clientConnections) COLLECT AGGREGATE clientConnections15M = SUM(clientConnectionsCurrent) RETURN {clientConnections15M: clientConnections15M || 0}"; std::string const statsSamplesQuery = "/*statsSample*/ FOR s IN @@collection FILTER s.time > @start FILTER s.clusterId IN @clusterIds RETURN { time: s.time, clusterId: s.clusterId, physicalMemory: s.server.physicalMemory, residentSizeCurrent: s.system.residentSize, clientConnectionsCurrent: s.client.httpConnections, avgRequestTime: s.client.avgRequestTime, bytesSentPerSecond: s.client.bytesSentPerSecond, bytesReceivedPerSecond: s.client.bytesReceivedPerSecond, http: { optionsPerSecond: s.http.requestsOptionsPerSecond, putsPerSecond: s.http.requestsPutPerSecond, headsPerSecond: s.http.requestsHeadPerSecond, postsPerSecond: s.http.requestsPostPerSecond, getsPerSecond: s.http.requestsGetPerSecond, deletesPerSecond: s.http.requestsDeletePerSecond, othersPerSecond: s.http.requestsOptionsPerSecond, patchesPerSecond: s.http.requestsPatchPerSecond } }"; + +// local_name: {"prometheus_name", "type", "help"} +std::map> statStrings{ + {"bytesReceived", + {"arangodb_client_connection_statistics_bytes_received_bucket", "gauge", + "Bytes received for a request"}}, + {"bytesReceivedCount", + {"arangodb_client_connection_statistics_bytes_received_count", "gauge", + "Bytes received for a request"}}, + {"bytesReceivedSum", + {"arangodb_client_connection_statistics_bytes_received_sum", "gauge", + "Bytes received for a request"}}, + {"bytesSent", + {"arangodb_client_connection_statistics_bytes_sent_bucket", "gauge", + "Bytes sent for a request"}}, + {"bytesSentCount", + {"arangodb_client_connection_statistics_bytes_sent_count", "gauge", + "Bytes sent for a request"}}, + {"bytesSentSum", + {"arangodb_client_connection_statistics_bytes_sent_sum", "gauge", + "Bytes sent for a request"}}, + {"minorPageFaults", + {"arangodb_process_statistics_minor_page_faults", "gauge", + "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"}}, + {"majorPageFaults", + {"arangodb_process_statistics_major_page_faults", "gauge", + "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"}}, + {"bytesReceived", + {"arangodb_client_connection_statistics_bytes_received_bucket", "gauge", + "Bytes received for a request"}}, + {"userTime", + {"arangodb_process_statistics_user_time", "gauge", + "Amount of time that this process has been scheduled in user mode, measured in seconds"}}, + {"systemTime", + {"arangodb_process_statistics_system_time", "gauge", + "Amount of time that this process has been scheduled in kernel mode, measured in seconds"}}, + {"numberOfThreads", + {"arangodb_process_statistics_number_of_threads", "gauge", + "Number of threads in the arangod process"}}, + {"residentSize", + {"arangodb_process_statistics_resident_set_size", "gauge", "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"}}, + {"residentSizePercent", + {"arangodb_process_statistics_resident_set_size_percent", "gauge", "The relative size of the number of pages the process has in real memory compared to system 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 value is a ratio between 0.00 and 1.00"}}, + {"virtualSize", + {"arangodb_process_statistics_virtual_memory_size", "gauge", "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"}}, + {"clientHttpConnections", + {"arangodb_client_connection_statistics_client_connections", "gauge", + "The number of client connections that are currently open"}}, + {"connectionTime", + {"arangodb_client_connection_statistics_connection_time_bucket", "gauge", + "Total connection time of a client"}}, + {"connectionTimeCount", + {"arangodb_client_connection_statistics_connection_time_count", "gauge", + "Total connection time of a client"}}, + {"connectionTimeSum", + {"arangodb_client_connection_statistics_connection_time_sum", "gauge", + "Total connection time of a client"}}, + {"totalTime", + {"arangodb_client_connection_statistics_total_time_bucket", "gauge", + "Total time needed to answer a request"}}, + {"totalTimeCount", + {"arangodb_client_connection_statistics_total_time_count", "gauge", + "Total time needed to answer a request"}}, + {"totalTimeSum", + {"arangodb_client_connection_statistics_total_time_sum", "gauge", + "Total time needed to answer a request"}}, + {"requestTime", + {"arangodb_client_connection_statistics_request_time_bucket", "gauge", + "Request time needed to answer a request"}}, + {"requestTimeCount", + {"arangodb_client_connection_statistics_request_time_count", "gauge", + "Request time needed to answer a request"}}, + {"requestTimeSum", + {"arangodb_client_connection_statistics_request_time_sum", "gauge", + "Request time needed to answer a request"}}, + {"queueTime", + {"arangodb_client_connection_statistics_queue_time_bucket", "gauge", + "Request time needed to answer a request"}}, + {"queueTimeCount", + {"arangodb_client_connection_statistics_queue_time_count", "gauge", + "Request time needed to answer a request"}}, + {"queueTimeSum", + {"arangodb_client_connection_statistics_queue_time_sum", "gauge", + "Request time needed to answer a request"}}, + {"ioTime", + {"arangodb_client_connection_statistics_io_time_bucket", "gauge", + "Request time needed to answer a request"}}, + {"ioTimeCount", + {"arangodb_client_connection_statistics_io_time_count", "gauge", + "Request time needed to answer a request"}}, + {"ioTimeSum", + {"arangodb_client_connection_statistics_io_time_sum", "gauge", + "Request time needed to answer a request"}}, + {"httpReqsTotal", + {"arangodb_http_request_statistics_total_requests", "gauge", + "Total number of HTTP requests"}}, + {"httpReqsSuperuser", + {"arangodb_http_request_statistics_superuser_requests", "gauge", + "Total number of HTTP requests executed by superuser/JWT"}}, + {"httpReqsUser", + {"arangodb_http_request_statistics_user_requests", "gauge", + "Total number of HTTP requests executed by clients"}}, + {"httpReqsAsync", + {"arangodb_http_request_statistics_async_requests", "gauge", + "Number of asynchronously executed HTTP requests"}}, + {"httpReqsDelete", + {"arangodb_http_request_statistics_http_delete_requests", "gauge", + "Number of HTTP DELETE requests"}}, + {"httpReqsGet", + {"arangodb_http_request_statistics_http_get_requests", "gauge", + "Number of HTTP GET requests"}}, + {"httpReqsHead", + {"arangodb_http_request_statistics_http_head_requests", "gauge", + "Number of HTTP HEAD requests"}}, + {"httpReqsOptions", + {"arangodb_http_request_statistics_http_options_requests", "gauge", + "Number of HTTP OPTIONS requests"}}, + {"httpReqsPatch", + {"arangodb_http_request_statistics_http_patch_requests", "gauge", + "Number of HTTP PATCH requests"}}, + {"httpReqsPost", + {"arangodb_http_request_statistics_http_post_requests", "gauge", + "Number of HTTP POST requests"}}, + {"httpReqsPut", + {"arangodb_http_request_statistics_http_put_requests", "gauge", + "Number of HTTP PUT requests"}}, + {"httpReqsOther", + {"arangodb_http_request_statistics_other_http_requests", "gauge", + "Number of other HTTP requests"}}, + {"uptime", + {"arangodb_server_statistics_server_uptime", "gauge", + "Number of seconds elapsed since server start"}}, + {"physicalSize", + {"arangodb_server_statistics_physical_memory", "gauge", + "Physical memory in bytes"}}, + {"v8ContextAvailable", + {"arangodb_v8_context_alive", "gauge", + "Number of V8 contexts currently alive"}}, + {"v8ContextBusy", + {"arangodb_v8_context_busy", "gauge", + "Number of V8 contexts currently busy"}}, + {"v8ContextDirty", + {"arangodb_v8_context_dirty", "gauge", + "Number of V8 contexts currently dirty"}}, + {"v8ContextFree", + {"arangodb_v8_context_free", "gauge", + "Number of V8 contexts currently free"}}, + {"v8ContextMax", + {"arangodb_v8_context_max", "gauge", + "Maximum number of concurrent V8 contexts"}}, + {"v8ContextMin", + {"arangodb_v8_context_min", "gauge", + "Minimum number of concurrent V8 contexts"}}, +}; + } // namespace namespace arangodb { @@ -172,7 +332,7 @@ void StatisticsFeature::collectOptions(std::shared_ptr options) options->addSection("server", "Server features"); options->addOption("--server.statistics", - "turn statistics gathering on or off", + "turn statistics gathering/aggregation and APIs on or off", new BooleanParameter(&_statistics), arangodb::options::makeDefaultFlags(arangodb::options::Flags::Hidden)); options->addOption("--server.statistics-history", @@ -199,16 +359,14 @@ void StatisticsFeature::validateOptions(std::shared_ptr options) } _statisticsHistoryTouched = options->processingResult().touched("--server.statistics-history"); - } void StatisticsFeature::prepare() { // initialize counters for all HTTP request types - - STATISTICS = this; - ConnectionStatistics::initialize(); RequestStatistics::initialize(); + + STATISTICS = this; } void StatisticsFeature::start() { @@ -276,10 +434,135 @@ void StatisticsFeature::stop() { STATISTICS = nullptr; } +void StatisticsFeature::appendMetric(std::string& result, std::string const& val, std::string const& label) const { + auto const& stat = statStrings.at(label); + std::string const& name = stat.at(0); + + result += + "\n#TYPE " + name + " " + stat[1] + + "\n#HELP " + name + " " + stat[2] + + '\n' + name + " " + val + '\n'; +} + +void StatisticsFeature::appendHistogram( + std::string& result, statistics::Distribution const& dist, + std::string const& label, std::initializer_list const& les) const { + + auto const countLabel = label + "Count"; + auto const countSum = label + "Sum"; + VPackBuilder tmp = fillDistribution(dist); + VPackSlice slc = tmp.slice(); + VPackSlice counts = slc.get("counts"); + + auto const& stat = statStrings.at(label); + std::string const& name = stat.at(0); + + result += + "\n#TYPE " + name + " " + stat[1] + + "\n#HELP " + name + " " + stat[2] + '\n'; + + TRI_ASSERT(les.size() == counts.length()); + size_t i = 0; + for (auto const& le : les) { + result += + name + "{le=\"" + le + "\"}" + " " + + std::to_string(counts.at(i++).getNumber()) + '\n'; + } +} + +VPackBuilder StatisticsFeature::fillDistribution(statistics::Distribution const& dist) { + VPackBuilder builder; + builder.openObject(); + + builder.add("sum", VPackValue(dist._total)); + builder.add("count", VPackValue(dist._count)); + + builder.add("counts", VPackValue(VPackValueType::Array)); + for (auto const& val : dist._counts) { + builder.add(VPackValue(val)); + } + builder.close(); + + builder.close(); + + return builder; +} + void StatisticsFeature::toPrometheus(std::string& result, double const& now) { - if (_statisticsWorker != nullptr) { - _statisticsWorker->generateRawStatistics(result, now); + ProcessInfo info = TRI_ProcessInfoSelf(); + uint64_t rss = static_cast(info._residentSize); + double rssp = 0; + + if (PhysicalMemory::getValue() != 0) { + rssp = static_cast(rss) / static_cast(PhysicalMemory::getValue()); + } + + ConnectionStatistics::Snapshot connectionStats; + ConnectionStatistics::getSnapshot(connectionStats); + + RequestStatistics::Snapshot requestStats; + RequestStatistics::getSnapshot(requestStats, stats::RequestStatisticsSource::ALL); + + ServerStatistics const& serverInfo = + server().getFeature().serverStatistics(); + + // processStatistics() + appendMetric(result, std::to_string(info._minorPageFaults), "minorPageFaults"); + appendMetric(result, std::to_string(info._majorPageFaults), "majorPageFaults"); + if (info._scClkTck != 0) { // prevent division by zero + appendMetric( + result, std::to_string( + static_cast(info._userTime) / static_cast(info._scClkTck)), "userTime"); + appendMetric( + result, std::to_string( + static_cast(info._systemTime) / static_cast(info._scClkTck)), "systemTime"); + } + appendMetric(result, std::to_string(info._numberThreads), "numberOfThreads"); + appendMetric(result, std::to_string(rss), "residentSize"); + appendMetric(result, std::to_string(rssp), "residentSizePercent"); + appendMetric(result, std::to_string(info._virtualSize), "virtualSize"); + appendMetric(result, std::to_string(PhysicalMemory::getValue()), "physicalSize"); + appendMetric(result, std::to_string(serverInfo.uptime()), "uptime"); + + // _clientStatistics() + appendMetric(result, std::to_string(connectionStats.httpConnections.get()), "clientHttpConnections"); + appendHistogram(result, connectionStats.connectionTime, "connectionTime", {"0.01", "1.0", "60.0", "+Inf"}); + appendHistogram(result, requestStats.totalTime, "totalTime", {"0.01", "0.05", "0.1", "0.2", "0.5", "1.0", "+Inf"}); + appendHistogram(result, requestStats.requestTime, "requestTime", {"0.01", "0.05", "0.1", "0.2", "0.5", "1.0", "+Inf"}); + appendHistogram(result, requestStats.queueTime, "queueTime", {"0.01", "0.05", "0.1", "0.2", "0.5", "1.0", "+Inf"}); + appendHistogram(result, requestStats.ioTime, "ioTime", {"0.01", "0.05", "0.1", "0.2", "0.5", "1.0", "+Inf"}); + appendHistogram(result, requestStats.bytesSent, "bytesSent", {"250", "1000", "2000", "5000", "10000", "+Inf"}); + appendHistogram(result, requestStats.bytesReceived, "bytesReceived", {"250", "1000", "2000", "5000", "10000", "+Inf"}); + + // _httpStatistics() + using rest::RequestType; + appendMetric(result, std::to_string(connectionStats.asyncRequests.get()), "httpReqsAsync"); + appendMetric(result, std::to_string(connectionStats.methodRequests[(int)RequestType::DELETE_REQ].get()), "httpReqsDelete"); + appendMetric(result, std::to_string(connectionStats.methodRequests[(int)RequestType::GET].get()), "httpReqsGet"); + appendMetric(result, std::to_string(connectionStats.methodRequests[(int)RequestType::HEAD].get()), "httpReqsHead"); + appendMetric(result, std::to_string(connectionStats.methodRequests[(int)RequestType::OPTIONS].get()), "httpReqsOptions"); + appendMetric(result, std::to_string(connectionStats.methodRequests[(int)RequestType::PATCH].get()), "httpReqsPatch"); + appendMetric(result, std::to_string(connectionStats.methodRequests[(int)RequestType::POST].get()), "httpReqsPost"); + appendMetric(result, std::to_string(connectionStats.methodRequests[(int)RequestType::PUT].get()), "httpReqsPut"); + appendMetric(result, std::to_string(connectionStats.methodRequests[(int)RequestType::ILLEGAL].get()), "httpReqsOther"); + appendMetric(result, std::to_string(connectionStats.totalRequests.get()), "httpReqsTotal"); + appendMetric(result, std::to_string(connectionStats.totalRequestsSuperuser.get()), "httpReqsSuperuser"); + appendMetric(result, std::to_string(connectionStats.totalRequestsUser.get()), "httpReqsUser"); + + V8DealerFeature::Statistics v8Counters{}; + if (server().hasFeature()) { + V8DealerFeature& dealer = server().getFeature(); + if (dealer.isEnabled()) { + v8Counters = dealer.getCurrentContextNumbers(); + } } + appendMetric(result, std::to_string(v8Counters.available), "v8ContextAvailable"); + appendMetric(result, std::to_string(v8Counters.busy), "v8ContextBusy"); + appendMetric(result, std::to_string(v8Counters.dirty), "v8ContextDirty"); + appendMetric(result, std::to_string(v8Counters.free), "v8ContextFree"); + appendMetric(result, std::to_string(v8Counters.min), "v8ContextMin"); + appendMetric(result, std::to_string(v8Counters.max), "v8ContextMax"); + result += "\n"; } Result StatisticsFeature::getClusterSystemStatistics(TRI_vocbase_t& vocbase, diff --git a/arangod/Statistics/StatisticsFeature.h b/arangod/Statistics/StatisticsFeature.h index 6069e768b37f..ce95dad7d068 100644 --- a/arangod/Statistics/StatisticsFeature.h +++ b/arangod/Statistics/StatisticsFeature.h @@ -24,6 +24,7 @@ #define APPLICATION_FEATURES_STATISTICS_FEATURE_H 1 #include +#include #include "ApplicationFeatures/ApplicationFeature.h" #include "Basics/Result.h" @@ -115,7 +116,15 @@ class StatisticsFeature final : public application_features::ApplicationFeature bool allDatabases() const { return _statisticsAllDatabases; } + static arangodb::velocypack::Builder fillDistribution(statistics::Distribution const& dist); + private: + void appendHistogram( + std::string& result, statistics::Distribution const& dist, + std::string const& label, std::initializer_list const& les) const; + void appendMetric( + std::string& result, std::string const& val, std::string const& label) const; + bool _statistics; bool _statisticsHistory; bool _statisticsHistoryTouched; diff --git a/arangod/Statistics/StatisticsWorker.cpp b/arangod/Statistics/StatisticsWorker.cpp index b496056d5733..9ce27641d757 100644 --- a/arangod/Statistics/StatisticsWorker.cpp +++ b/arangod/Statistics/StatisticsWorker.cpp @@ -814,274 +814,6 @@ void StatisticsWorker::avgPercentDistributon(VPackBuilder& builder, VPackSlice c builder.close(); } -// local_name: {"prometheus_name", "type", "help"} -std::map> statStrings{ - {"bytesReceived", - {"arangodb_client_connection_statistics_bytes_received_bucket", "gauge", - "Bytes received for a request"}}, - {"bytesReceivedCount", - {"arangodb_client_connection_statistics_bytes_received_count", "gauge", - "Bytes received for a request"}}, - {"bytesReceivedSum", - {"arangodb_client_connection_statistics_bytes_received_sum", "gauge", - "Bytes received for a request"}}, - {"bytesSent", - {"arangodb_client_connection_statistics_bytes_sent_bucket", "gauge", - "Bytes sent for a request"}}, - {"bytesSentCount", - {"arangodb_client_connection_statistics_bytes_sent_count", "gauge", - "Bytes sent for a request"}}, - {"bytesSentSum", - {"arangodb_client_connection_statistics_bytes_sent_sum", "gauge", - "Bytes sent for a request"}}, - {"minorPageFaults", - {"arangodb_process_statistics_minor_page_faults", "gauge", - "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"}}, - {"majorPageFaults", - {"arangodb_process_statistics_major_page_faults", "gauge", - "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"}}, - {"bytesReceived", - {"arangodb_client_connection_statistics_bytes_received_bucket", "gauge", - "Bytes received for a request"}}, - {"userTime", - {"arangodb_process_statistics_user_time", "gauge", - "Amount of time that this process has been scheduled in user mode, measured in seconds"}}, - {"systemTime", - {"arangodb_process_statistics_system_time", "gauge", - "Amount of time that this process has been scheduled in kernel mode, measured in seconds"}}, - {"numberOfThreads", - {"arangodb_process_statistics_number_of_threads", "gauge", - "Number of threads in the arangod process"}}, - {"residentSize", - {"arangodb_process_statistics_resident_set_size", "gauge", "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"}}, - {"residentSizePercent", - {"arangodb_process_statistics_resident_set_size_percent", "gauge", "The relative size of the number of pages the process has in real memory compared to system 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 value is a ratio between 0.00 and 1.00"}}, - {"virtualSize", - {"arangodb_process_statistics_virtual_memory_size", "gauge", "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"}}, - {"clientHttpConnections", - {"arangodb_client_connection_statistics_client_connections", "gauge", - "The number of client connections that are currently open"}}, - {"connectionTime", - {"arangodb_client_connection_statistics_connection_time_bucket", "gauge", - "Total connection time of a client"}}, - {"connectionTimeCount", - {"arangodb_client_connection_statistics_connection_time_count", "gauge", - "Total connection time of a client"}}, - {"connectionTimeSum", - {"arangodb_client_connection_statistics_connection_time_sum", "gauge", - "Total connection time of a client"}}, - {"totalTime", - {"arangodb_client_connection_statistics_total_time_bucket", "gauge", - "Total time needed to answer a request"}}, - {"totalTimeCount", - {"arangodb_client_connection_statistics_total_time_count", "gauge", - "Total time needed to answer a request"}}, - {"totalTimeSum", - {"arangodb_client_connection_statistics_total_time_sum", "gauge", - "Total time needed to answer a request"}}, - {"requestTime", - {"arangodb_client_connection_statistics_request_time_bucket", "gauge", - "Request time needed to answer a request"}}, - {"requestTimeCount", - {"arangodb_client_connection_statistics_request_time_count", "gauge", - "Request time needed to answer a request"}}, - {"requestTimeSum", - {"arangodb_client_connection_statistics_request_time_sum", "gauge", - "Request time needed to answer a request"}}, - {"queueTime", - {"arangodb_client_connection_statistics_queue_time_bucket", "gauge", - "Request time needed to answer a request"}}, - {"queueTimeCount", - {"arangodb_client_connection_statistics_queue_time_count", "gauge", - "Request time needed to answer a request"}}, - {"queueTimeSum", - {"arangodb_client_connection_statistics_queue_time_sum", "gauge", - "Request time needed to answer a request"}}, - {"ioTime", - {"arangodb_client_connection_statistics_io_time_bucket", "gauge", - "Request time needed to answer a request"}}, - {"ioTimeCount", - {"arangodb_client_connection_statistics_io_time_count", "gauge", - "Request time needed to answer a request"}}, - {"ioTimeSum", - {"arangodb_client_connection_statistics_io_time_sum", "gauge", - "Request time needed to answer a request"}}, - {"httpReqsTotal", - {"arangodb_http_request_statistics_total_requests", "gauge", - "Total number of HTTP requests"}}, - {"httpReqsSuperuser", - {"arangodb_http_request_statistics_superuser_requests", "gauge", - "Total number of HTTP requests executed by superuser/JWT"}}, - {"httpReqsUser", - {"arangodb_http_request_statistics_user_requests", "gauge", - "Total number of HTTP requests executed by clients"}}, - {"httpReqsAsync", - {"arangodb_http_request_statistics_async_requests", "gauge", - "Number of asynchronously executed HTTP requests"}}, - {"httpReqsDelete", - {"arangodb_http_request_statistics_http_delete_requests", "gauge", - "Number of HTTP DELETE requests"}}, - {"httpReqsGet", - {"arangodb_http_request_statistics_http_get_requests", "gauge", - "Number of HTTP GET requests"}}, - {"httpReqsHead", - {"arangodb_http_request_statistics_http_head_requests", "gauge", - "Number of HTTP HEAD requests"}}, - {"httpReqsOptions", - {"arangodb_http_request_statistics_http_options_requests", "gauge", - "Number of HTTP OPTIONS requests"}}, - {"httpReqsPatch", - {"arangodb_http_request_statistics_http_patch_requests", "gauge", - "Number of HTTP PATCH requests"}}, - {"httpReqsPost", - {"arangodb_http_request_statistics_http_post_requests", "gauge", - "Number of HTTP POST requests"}}, - {"httpReqsPut", - {"arangodb_http_request_statistics_http_put_requests", "gauge", - "Number of HTTP PUT requests"}}, - {"httpReqsOther", - {"arangodb_http_request_statistics_other_http_requests", "gauge", - "Number of other HTTP requests"}}, - {"uptime", - {"arangodb_server_statistics_server_uptime", "gauge", - "Number of seconds elapsed since server start"}}, - {"physicalSize", - {"arangodb_server_statistics_physical_memory", "gauge", - "Physical memory in bytes"}}, - {"v8ContextAvailable", - {"arangodb_v8_context_alive", "gauge", - "Number of V8 contexts currently alive"}}, - {"v8ContextBusy", - {"arangodb_v8_context_busy", "gauge", - "Number of V8 contexts currently busy"}}, - {"v8ContextDirty", - {"arangodb_v8_context_dirty", "gauge", - "Number of V8 contexts currently dirty"}}, - {"v8ContextFree", - {"arangodb_v8_context_free", "gauge", - "Number of V8 contexts currently free"}}, - {"v8ContextMax", - {"arangodb_v8_context_max", "gauge", - "Maximum number of concurrent V8 contexts"}}, - {"v8ContextMin", - {"arangodb_v8_context_min", "gauge", - "Minimum number of concurrent V8 contexts"}}, -}; - -void StatisticsWorker::generateRawStatistics(std::string& result, double const& now) { - ProcessInfo info = TRI_ProcessInfoSelf(); - uint64_t rss = static_cast(info._residentSize); - double rssp = 0; - - if (PhysicalMemory::getValue() != 0) { - rssp = static_cast(rss) / static_cast(PhysicalMemory::getValue()); - } - - ConnectionStatistics::Snapshot connectionStats; - ConnectionStatistics::getSnapshot(connectionStats); - - RequestStatistics::Snapshot requestStats; - RequestStatistics::getSnapshot(requestStats, stats::RequestStatisticsSource::ALL); - - ServerStatistics const& serverInfo = - _vocbase.server().getFeature().serverStatistics(); - - // processStatistics() - appendMetric(result, std::to_string(info._minorPageFaults), "minorPageFaults"); - appendMetric(result, std::to_string(info._majorPageFaults), "majorPageFaults"); - if (info._scClkTck != 0) { // prevent division by zero - appendMetric( - result, std::to_string( - static_cast(info._userTime) / static_cast(info._scClkTck)), "userTime"); - appendMetric( - result, std::to_string( - static_cast(info._systemTime) / static_cast(info._scClkTck)), "systemTime"); - } - appendMetric(result, std::to_string(info._numberThreads), "numberOfThreads"); - appendMetric(result, std::to_string(rss), "residentSize"); - appendMetric(result, std::to_string(rssp), "residentSizePercent"); - appendMetric(result, std::to_string(info._virtualSize), "virtualSize"); - appendMetric(result, std::to_string(PhysicalMemory::getValue()), "physicalSize"); - appendMetric(result, std::to_string(serverInfo.uptime()), "uptime"); - - // _clientStatistics() - appendMetric(result, std::to_string(connectionStats.httpConnections.get()), "clientHttpConnections"); - appendHistogram(result, connectionStats.connectionTime, "connectionTime", {"0.01", "1.0", "60.0", "+Inf"}); - appendHistogram(result, requestStats.totalTime, "totalTime", {"0.01", "0.05", "0.1", "0.2", "0.5", "1.0", "+Inf"}); - appendHistogram(result, requestStats.requestTime, "requestTime", {"0.01", "0.05", "0.1", "0.2", "0.5", "1.0", "+Inf"}); - appendHistogram(result, requestStats.queueTime, "queueTime", {"0.01", "0.05", "0.1", "0.2", "0.5", "1.0", "+Inf"}); - appendHistogram(result, requestStats.ioTime, "ioTime", {"0.01", "0.05", "0.1", "0.2", "0.5", "1.0", "+Inf"}); - appendHistogram(result, requestStats.bytesSent, "bytesSent", {"250", "1000", "2000", "5000", "10000", "+Inf"}); - appendHistogram(result, requestStats.bytesReceived, "bytesReceived", {"250", "1000", "2000", "5000", "10000", "+Inf"}); - - // _httpStatistics() - using rest::RequestType; - appendMetric(result, std::to_string(connectionStats.asyncRequests.get()), "httpReqsAsync"); - appendMetric(result, std::to_string(connectionStats.methodRequests[(int)RequestType::DELETE_REQ].get()), "httpReqsDelete"); - appendMetric(result, std::to_string(connectionStats.methodRequests[(int)RequestType::GET].get()), "httpReqsGet"); - appendMetric(result, std::to_string(connectionStats.methodRequests[(int)RequestType::HEAD].get()), "httpReqsHead"); - appendMetric(result, std::to_string(connectionStats.methodRequests[(int)RequestType::OPTIONS].get()), "httpReqsOptions"); - appendMetric(result, std::to_string(connectionStats.methodRequests[(int)RequestType::PATCH].get()), "httpReqsPatch"); - appendMetric(result, std::to_string(connectionStats.methodRequests[(int)RequestType::POST].get()), "httpReqsPost"); - appendMetric(result, std::to_string(connectionStats.methodRequests[(int)RequestType::PUT].get()), "httpReqsPut"); - appendMetric(result, std::to_string(connectionStats.methodRequests[(int)RequestType::ILLEGAL].get()), "httpReqsOther"); - appendMetric(result, std::to_string(connectionStats.totalRequests.get()), "httpReqsTotal"); - appendMetric(result, std::to_string(connectionStats.totalRequestsSuperuser.get()), "httpReqsSuperuser"); - appendMetric(result, std::to_string(connectionStats.totalRequestsUser.get()), "httpReqsUser"); - - V8DealerFeature::Statistics v8Counters{}; - if (_server.hasFeature()) { - V8DealerFeature& dealer = _server.getFeature(); - if (dealer.isEnabled()) { - v8Counters = dealer.getCurrentContextNumbers(); - } - } - appendMetric(result, std::to_string(v8Counters.available), "v8ContextAvailable"); - appendMetric(result, std::to_string(v8Counters.busy), "v8ContextBusy"); - appendMetric(result, std::to_string(v8Counters.dirty), "v8ContextDirty"); - appendMetric(result, std::to_string(v8Counters.free), "v8ContextFree"); - appendMetric(result, std::to_string(v8Counters.min), "v8ContextMin"); - appendMetric(result, std::to_string(v8Counters.max), "v8ContextMax"); - result += "\n"; -} - -void StatisticsWorker::appendMetric(std::string& result, std::string const& val, std::string const& label) const { - auto const& stat = statStrings.at(label); - std::string const& name = stat.at(0); - - result += - "\n#TYPE " + name + " " + stat[1] + - "\n#HELP " + name + " " + stat[2] + - '\n' + name + " " + val + '\n'; -} - -void StatisticsWorker::appendHistogram( - std::string& result, Distribution const& dist, - std::string const& label, std::initializer_list const& les) const { - - auto const countLabel = label + "Count"; - auto const countSum = label + "Sum"; - VPackBuilder tmp = fillDistribution(dist); - VPackSlice slc = tmp.slice(); - VPackSlice counts = slc.get("counts"); - - auto const& stat = statStrings.at(label); - std::string const& name = stat.at(0); - - result += - "\n#TYPE " + name + " " + stat[1] + - "\n#HELP " + name + " " + stat[2] + '\n'; - - TRI_ASSERT(les.size() == counts.length()); - size_t i = 0; - for (auto const& le : les) { - result += - name + "{le=\"" + le + "\"}" + " " + - std::to_string(counts.at(i++).getNumber()) + '\n'; - } - -} - void StatisticsWorker::generateRawStatistics(VPackBuilder& builder, double const& now) { ProcessInfo info = TRI_ProcessInfoSelf(); uint64_t rss = static_cast(info._residentSize); @@ -1128,25 +860,25 @@ void StatisticsWorker::generateRawStatistics(VPackBuilder& builder, double const builder.add("client", VPackValue(VPackValueType::Object)); builder.add("httpConnections", VPackValue(connectionStats.httpConnections.get())); - VPackBuilder tmp = fillDistribution(connectionStats.connectionTime); + VPackBuilder tmp = StatisticsFeature::fillDistribution(connectionStats.connectionTime); builder.add("connectionTime", tmp.slice()); - tmp = fillDistribution(requestStats.totalTime); + tmp = StatisticsFeature::fillDistribution(requestStats.totalTime); builder.add("totalTime", tmp.slice()); - tmp = fillDistribution(requestStats.requestTime); + tmp = StatisticsFeature::fillDistribution(requestStats.requestTime); builder.add("requestTime", tmp.slice()); - tmp = fillDistribution(requestStats.queueTime); + tmp = StatisticsFeature::fillDistribution(requestStats.queueTime); builder.add("queueTime", tmp.slice()); - tmp = fillDistribution(requestStats.ioTime); + tmp = StatisticsFeature::fillDistribution(requestStats.ioTime); builder.add("ioTime", tmp.slice()); - tmp = fillDistribution(requestStats.bytesSent); + tmp = StatisticsFeature::fillDistribution(requestStats.bytesSent); builder.add("bytesSent", tmp.slice()); - tmp = fillDistribution(requestStats.bytesReceived); + tmp = StatisticsFeature::fillDistribution(requestStats.bytesReceived); builder.add("bytesReceived", tmp.slice()); builder.close(); @@ -1236,23 +968,6 @@ void StatisticsWorker::generateRawStatistics(VPackBuilder& builder, double const builder.close(); } -VPackBuilder StatisticsWorker::fillDistribution(Distribution const& dist) const { - VPackBuilder builder; - builder.openObject(); - - builder.add("sum", VPackValue(dist._total)); - builder.add("count", VPackValue(dist._count)); - - builder.add("counts", VPackValue(VPackValueType::Array)); - for (auto const& val : dist._counts) { - builder.add(VPackValue(val)); - } - builder.close(); - - builder.close(); - - return builder; -} void StatisticsWorker::saveSlice(VPackSlice const& slice, std::string const& collection) const { if (isStopping()) { diff --git a/arangod/Statistics/StatisticsWorker.h b/arangod/Statistics/StatisticsWorker.h index 587b6cc28bb8..3799360731a0 100644 --- a/arangod/Statistics/StatisticsWorker.h +++ b/arangod/Statistics/StatisticsWorker.h @@ -73,12 +73,6 @@ class StatisticsWorker final : public Thread { // save one statistics object void saveSlice(velocypack::Slice const&, std::string const&) const; - void appendHistogram( - std::string& result, statistics::Distribution const& dist, - std::string const& label, std::initializer_list const& les) const; - void appendMetric( - std::string& result, std::string const& val, std::string const& label) const; - static constexpr uint64_t STATISTICS_INTERVAL = 10; // 10 secs static constexpr uint64_t GC_INTERVAL = 8 * 60; // 8 mins static constexpr uint64_t HISTORY_INTERVAL = 15 * 60; // 15 mins diff --git a/tests/js/client/server_parameters/test-statistics-enabled-false.js b/tests/js/client/server_parameters/test-statistics-enabled-false.js new file mode 100644 index 000000000000..b8fe797de35a --- /dev/null +++ b/tests/js/client/server_parameters/test-statistics-enabled-false.js @@ -0,0 +1,88 @@ +/*jshint globalstrict:false, strict:false */ +/* global getOptions, assertEqual, assertTrue, arango */ + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test for security-related server options +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright 2010-2012 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 Inc, Cologne, Germany +/// +/// @author Jan Steemann +/// @author Copyright 2019, ArangoDB Inc, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +if (getOptions === true) { + return { + 'server.statistics': "false", + 'server.export-metrics-api': "true", + }; +} + +const jsunity = require('jsunity'); + +function testSuite() { + return { + testGetAdminStatistics : function() { + let res = arango.GET("/_admin/statistics"); + assertTrue(res.error); + assertEqual(404, res.code); + }, + + testGetAdminStatisticsDescription : function() { + let res = arango.GET("/_admin/statistics-description"); + assertTrue(res.error); + assertEqual(404, res.code); + }, + + testGetMetrics : function() { + let metrics = {}; + String(arango.GET("/_admin/metrics")) + .split("\n") + .filter((line) => line.match(/^[^#]/)) + .filter((line) => line.match(/^arangodb_/)) + .forEach((line) => { + let name = line.replace(/[ \{].*$/g, ''); + let value = Number(line.replace(/^.+ (\d+)$/, '$1')); + metrics[name] = value; + }); + + const expected = [ + "arangodb_process_statistics_minor_page_faults", + "arangodb_process_statistics_major_page_faults", + "arangodb_process_statistics_user_time", + "arangodb_process_statistics_system_time", + "arangodb_process_statistics_number_of_threads", + "arangodb_process_statistics_resident_set_size", + "arangodb_process_statistics_resident_set_size_percent", + "arangodb_process_statistics_virtual_memory_size", + "arangodb_server_statistics_physical_memory", + "arangodb_server_statistics_server_uptime", + ]; + + expected.forEach((name) => { + assertTrue(metrics.hasOwnProperty(name)); + assertEqual("number", typeof metrics[name]); + }); + }, + }; +} + +jsunity.run(testSuite); +return jsunity.done();