1
0
Fork 0

Merge branch 'devel' of https://github.com/arangodb/arangodb into bug-fix/install-js-server-files

* 'devel' of https://github.com/arangodb/arangodb:
  de gray - test-shell-foxx-paths (#10555)
  fix UUID - devel (#10584)
  Metrics tests (#10578)
This commit is contained in:
Jan Christoph Uhde 2019-12-03 08:50:23 +01:00
commit 6980ac70f9
23 changed files with 416 additions and 189 deletions

View File

@ -1,6 +1,8 @@
devel
-----
* Fix execution ability in CentOS 6 regarding newer boost.
* Fixed permissions for dump/restore.
* The _users collection is now properly restored when using arangorestore.

View File

@ -1140,6 +1140,7 @@ endif ()
# ZLIB_LIBS
# ZLIB_INCLUDE_DIR
add_definitions(-DBOOST_UUID_RANDOM_PROVIDER_FORCE_POSIX=1) #fix random provider
add_definitions(-DBOOST_ALL_NO_LIB=1) #disable boost autolink on windows
add_subdirectory(3rdParty)

View File

@ -0,0 +1,31 @@
@startDocuBlock get_admin_metrics
@brief return the current instance metrics
@RESTHEADER{GET /_admin/metrics, Read the metrics, getMetrics}
@RESTDESCRIPTION
Returns the instance's current metrics in Prometheus format. The
returned document collects all instance metrics, which are measured
at any given time and exposes them for collection by Prometheus.
The document contains different metrics and metrics groups dependent
on the role of the queried instance. All exported metrics are
published with the `arangodb_` or `rocksdb_` string to distinguish
them from other collected data.
The API then needs to be added to the Prometheus configuration file
for collection.
@RESTRETURNCODES
@RESTRETURNCODE{200}
Metrics were returned successfully.
@RESTRETURNCODE{404}
The metrics API may be disabled using `--server.export-metrics-api
false` setting in the server. In this case, the result of the call
indicates the API to be not found.
@endDocuBlock

View File

@ -61,7 +61,7 @@ Result ClusterTransactionState::beginTransaction(transaction::Hints hints) {
auto cleanup = scopeGuard([&] {
if (nestingLevel() == 0) {
updateStatus(transaction::Status::ABORTED);
MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._transactionsAborted++;
_vocbase.server().getFeature<MetricsFeature>().serverStatistics()._transactionsStatistics._transactionsAborted++;
}
// free what we have got so far
unuseCollections(nestingLevel());
@ -75,7 +75,7 @@ Result ClusterTransactionState::beginTransaction(transaction::Hints hints) {
// all valid
if (nestingLevel() == 0) {
updateStatus(transaction::Status::RUNNING);
MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._transactionsStarted++;
_vocbase.server().getFeature<MetricsFeature>().serverStatistics()._transactionsStatistics._transactionsStarted++;
transaction::ManagerFeature::manager()->registerTransaction(id(), nullptr, isReadOnlyTransaction());
setRegistered();
@ -122,7 +122,7 @@ Result ClusterTransactionState::commitTransaction(transaction::Methods* activeTr
arangodb::Result res;
if (nestingLevel() == 0) {
updateStatus(transaction::Status::COMMITTED);
MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._transactionsCommitted++;
_vocbase.server().getFeature<MetricsFeature>().serverStatistics()._transactionsStatistics._transactionsCommitted++;
}
unuseCollections(nestingLevel());
@ -136,7 +136,7 @@ Result ClusterTransactionState::abortTransaction(transaction::Methods* activeTrx
Result res;
if (nestingLevel() == 0) {
updateStatus(transaction::Status::ABORTED);
MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._transactionsAborted++;
_vocbase.server().getFeature<MetricsFeature>().serverStatistics()._transactionsStatistics._transactionsAborted++;
}
unuseCollections(nestingLevel());

View File

@ -127,14 +127,14 @@ Result MMFilesTransactionState::beginTransaction(transaction::Hints hints) {
// all valid
if (nestingLevel() == 0) {
updateStatus(transaction::Status::RUNNING);
MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._transactionsStarted++;
_vocbase.server().getFeature<MetricsFeature>().serverStatistics()._transactionsStatistics._transactionsStarted++;
// defer writing of the begin marker until necessary!
}
} else {
// something is wrong
if (nestingLevel() == 0) {
updateStatus(transaction::Status::ABORTED);
MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._transactionsAborted++;
_vocbase.server().getFeature<MetricsFeature>().serverStatistics()._transactionsStatistics._transactionsAborted++;
}
// free what we have got so far
@ -175,7 +175,7 @@ Result MMFilesTransactionState::commitTransaction(transaction::Methods* activeTr
}
updateStatus(transaction::Status::COMMITTED);
MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._transactionsCommitted++;
_vocbase.server().getFeature<MetricsFeature>().serverStatistics()._transactionsStatistics._transactionsCommitted++;
// if a write query, clear the query cache for the participating collections
if (AccessMode::isWriteOrExclusive(_type) && !_collections.empty() &&
@ -204,7 +204,8 @@ Result MMFilesTransactionState::abortTransaction(transaction::Methods* activeTrx
result.reset(res);
updateStatus(transaction::Status::ABORTED);
MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._transactionsAborted++;
_vocbase.server().getFeature<MetricsFeature>().
serverStatistics()._transactionsStatistics._transactionsAborted++;
if (_hasOperations) {
// must clean up the query cache because the transaction

View File

@ -58,6 +58,12 @@ RestStatus RestMetricsHandler::execute() {
}
MetricsFeature& metrics = server.getFeature<MetricsFeature>();
if (!metrics.exportAPI()) {
// dont export metrics, if so desired
generateError(rest::ResponseCode::NOT_FOUND, TRI_ERROR_HTTP_NOT_FOUND);
return RestStatus::DONE;
}
std::string result;
metrics.toPrometheus(result);
_response->setResponseCode(rest::ResponseCode::OK);

View File

@ -33,7 +33,7 @@ class RestMetricsHandler : public arangodb::RestBaseHandler {
GeneralResponse*);
char const* name() const override final { return "RestMetricsHandler"; }
RequestLane lane() const override final { return RequestLane::CLIENT_SLOW; }
RequestLane lane() const override final { return RequestLane::CLIENT_FAST; }
RestStatus execute() override;
};

View File

@ -66,6 +66,16 @@ Counter& Counter::operator++(int n) {
return *this;
}
Counter& Counter::operator+=(uint64_t const& n) {
count(n);
return *this;
}
Counter& Counter::operator=(uint64_t const& n) {
store(n);
return *this;
}
void Counter::count() {
++_b;
}
@ -84,6 +94,10 @@ uint64_t Counter::load() const {
return _c.load();
}
void Counter::store(uint64_t const& n) {
_c.exchange(n);
}
void Counter::toPrometheus(std::string& result) const {
_b.push();
result += "#TYPE " + name() + " counter\n";
@ -91,7 +105,8 @@ void Counter::toPrometheus(std::string& result) const {
result += name() + " " + std::to_string(load()) + "\n";
}
Counter::Counter(uint64_t const& val, std::string const& name, std::string const& help) :
Counter::Counter(
uint64_t const& val, std::string const& name, std::string const& help) :
Metric(name, help), _c(val), _b(_c) {}
Counter::~Counter() { _b.push(); }

View File

@ -45,7 +45,7 @@ template<typename T> class Histogram;
class Metric {
public:
Metric(std::string const& name, std::string const& help);
virtual ~Metric();
virtual ~Metric();
std::string const& help() const;
std::string const& name() const;
virtual void toPrometheus(std::string& result) const = 0;
@ -62,7 +62,7 @@ struct Metrics {
using counter_type = gcl::counter::simplex<uint64_t, gcl::counter::atomicity::full>;
using hist_type = gcl::counter::simplex_array<uint64_t, gcl::counter::atomicity::full>;
using buffer_type = gcl::counter::buffer<uint64_t>;
};
@ -77,9 +77,12 @@ public:
std::ostream& print (std::ostream&) const;
Counter& operator++();
Counter& operator++(int);
Counter& operator+=(uint64_t const&);
Counter& operator=(uint64_t const&);
void count();
void count(uint64_t);
uint64_t load() const;
void store(uint64_t const&);
void push();
virtual void toPrometheus(std::string&) const override;
private:
@ -91,12 +94,12 @@ private:
template<typename T> class Gauge : public Metric {
public:
Gauge() = delete;
Gauge(uint64_t const& val, std::string const& name, std::string const& help)
Gauge(T const& val, std::string const& name, std::string const& help)
: Metric(name, help) {
_g.store(val);
}
Gauge(Gauge const&) = delete;
virtual ~Gauge();
~Gauge() = default;
std::ostream& print (std::ostream&) const;
Gauge<T>& operator+=(T const& t) {
_g.store(_g + t);
@ -111,6 +114,7 @@ public:
return *this;
}
Gauge<T>& operator/=(T const& t) {
TRI_ASSERT(t != T(0));
_g.store(_g / t);
return *this;
}
@ -121,7 +125,11 @@ public:
T load() const {
return _g.load();
};
virtual void toPrometheus(std::string&) const override {};
virtual void toPrometheus(std::string& result) const override {
result += "#TYPE " + name() + " gauge\n";
result += "#HELP " + name() + " " + help() + "\n";
result += name() + " " + std::to_string(load()) + "\n";
};
private:
std::atomic<T> _g;
};
@ -130,41 +138,38 @@ std::ostream& operator<< (std::ostream&, Metrics::hist_type const&);
/**
* @brief Histogram functionality
*/
*/
template<typename T> class Histogram : public Metric {
public:
Histogram() = delete;
Histogram (size_t const& buckets, T const& low, T const& high, std::string const& name, std::string const& help = "")
: Metric(name, help), _c(Metrics::hist_type(buckets)), _low(low), _high(high),
_lowr(std::numeric_limits<T>::max()), _highr(std::numeric_limits<T>::min()) {
TRI_ASSERT(_c.size() > 0);
_n = _c.size() - 1;
_div = std::floor((double)(high - low) / (double)_c.size());
_div = (high - low) / (double)_c.size();
TRI_ASSERT(_div != 0);
}
~Histogram() {}
~Histogram() = default;
void records(T const& t) {
if(t < _lowr) {
_lowr = t;
} else if (t > _highr) {
_highr = t;
}
}
void count(T const& t) {
if (t < _low) {
++_c[0];
} else if (t >= _high) {
++_c[_n];
} else {
++_c[static_cast<size_t>(std::floor(t / _div))];
}
records(t);
}
size_t pos(T const& t) const {
return static_cast<size_t>(std::floor((t - _low)/ _div));
}
void count(T const& t) {
count(t, 1);
}
void count(T const& t, uint64_t n) {
@ -173,7 +178,7 @@ public:
} else if (t >= _high) {
_c[_n] += n;
} else {
_c[static_cast<size_t>(std::floor(t / _div))] += n;
_c[pos(t)] += n;
}
records(t);
}
@ -194,7 +199,7 @@ public:
}
uint64_t load(size_t i) const { return _c.load(i); };
size_t size() const { return _c.size(); }
virtual void toPrometheus(std::string& result) const override {
@ -211,16 +216,17 @@ public:
}
result += name() + "_count " + std::to_string(sum) + "\n";
}
std::ostream& print(std::ostream& o) const {
o << "_div: " << _div << ", _c: " << _c << ", _r: [" << _lowr << ", " << _highr << "] " << name();
o << "_div: " << _div << ", _c: " << _c << ", _r: [" << _lowr << ", " << _highr << "] " << name();
return o;
}
private:
Metrics::hist_type _c;
T _low, _high, _div, _lowr, _highr;
size_t _n;
};

View File

@ -53,37 +53,28 @@ using namespace arangodb::options;
// --SECTION-- MetricsFeature
// -----------------------------------------------------------------------------
MetricsFeature* MetricsFeature::METRICS = nullptr;
#include <iostream>
MetricsFeature::MetricsFeature(application_features::ApplicationServer& server)
: ApplicationFeature(server, "Metrics"),
_enabled(true) {
METRICS = this;
_serverStatistics = new
ServerStatistics(std::chrono::duration<double>(
std::chrono::system_clock::now().time_since_epoch()).count());
: ApplicationFeature(server, "Metrics"), _export(true) {
setOptional(false);
startsAfter<LoggerFeature>();
startsBefore<GreetingsFeaturePhase>();
}
void MetricsFeature::collectOptions(std::shared_ptr<ProgramOptions> options) {}
void MetricsFeature::collectOptions(std::shared_ptr<ProgramOptions> options) {
_serverStatistics = std::make_unique<ServerStatistics>(
std::chrono::duration<double>(std::chrono::system_clock::now().time_since_epoch()).count());
options->addOption("--server.export-metrics-api",
"turn metrics API on or off",
new BooleanParameter(&_export),
arangodb::options::makeFlags(arangodb::options::Flags::Hidden));
}
bool MetricsFeature::exportAPI() const {
return _export;
}
void MetricsFeature::validateOptions(std::shared_ptr<ProgramOptions>) {}
void MetricsFeature::unprepare() {
METRICS = nullptr;
}
void MetricsFeature::prepare() {}
double time() {
return std::chrono::duration<double>( // time since epoch in seconds
std::chrono::system_clock::now().time_since_epoch())
.count();
}
void MetricsFeature::toPrometheus(std::string& result) const {
{
std::lock_guard<std::mutex> guard(_lock);
@ -111,38 +102,19 @@ void MetricsFeature::toPrometheus(std::string& result) const {
Counter& MetricsFeature::counter (
std::string const& name, uint64_t const& val, std::string const& help) {
std::lock_guard<std::mutex> guard(_lock);
auto const it = _registry.find(name);
if (it != _registry.end()) {
LOG_TOPIC("8523d", ERR, Logger::STATISTICS) << "Failed to retrieve histogram " << name;
TRI_ASSERT(false);
}
auto c = std::make_shared<Counter>(val, name, help);
_registry.emplace(name, std::dynamic_pointer_cast<Metric>(c));
return *c;
};
Counter& MetricsFeature::counter (std::string const& name) {
std::lock_guard<std::mutex> guard(_lock);
auto it = _registry.find(name);
if (it == _registry.end()) {
LOG_TOPIC("32d58", ERR, Logger::STATISTICS)
<< "Failed to retrieve counter " << name;
TRI_ASSERT(false);
throw std::exception();
}
std::shared_ptr<Counter> h;
try {
h = std::dynamic_pointer_cast<Counter>(it->second);
} catch (std::exception const& e) {
LOG_TOPIC("853d2", ERR, Logger::STATISTICS)
<< "Failed to retrieve counter " << name;
TRI_ASSERT(false);
throw(e);
auto metric = std::make_shared<Counter>(val, name, help);
bool success = false;
{
std::lock_guard<std::mutex> guard(_lock);
success = _registry.emplace(name, std::dynamic_pointer_cast<Metric>(metric)).second;
}
return *h;
};
if (!success) {
THROW_ARANGO_EXCEPTION_MESSAGE(
TRI_ERROR_INTERNAL, std::string("counter ") + name + " alredy exists");
}
return *metric;
}
ServerStatistics& MetricsFeature::serverStatistics() {
return *_serverStatistics;

View File

@ -34,121 +34,122 @@ namespace arangodb {
class MetricsFeature final : public application_features::ApplicationFeature {
public:
bool enabled() const {
return _enabled;
}
using registry_type = std::unordered_map<std::string, std::shared_ptr<Metric>>;
static double time();
explicit MetricsFeature(application_features::ApplicationServer& server);
bool exportAPI() const;
void collectOptions(std::shared_ptr<options::ProgramOptions>) override final;
void validateOptions(std::shared_ptr<options::ProgramOptions>) override final;
void prepare() override final;
void unprepare() override final;
template<typename T> Histogram<T>&
histogram (std::string const& name, size_t const& buckets, T const& low,
T const& high, std::string const& help = std::string()) {
std::lock_guard<std::mutex> guard(_lock);
auto const it = _registry.find(name);
if (it != _registry.end()) {
LOG_TOPIC("32e85", ERR, Logger::STATISTICS) << "histogram " << name << " alredy exists";
TRI_ASSERT(false);
auto metric = std::make_shared<Histogram<T>>(buckets, low, high, name, help);
bool success = false;
{
std::lock_guard<std::mutex> guard(_lock);
success = _registry.try_emplace(
name, std::dynamic_pointer_cast<Metric>(metric)).second;
}
auto h = std::make_shared<Histogram<T>>(buckets, low, high, name, help);
_registry.emplace(name, std::dynamic_pointer_cast<Metric>(h));
return *h;
if (!success) {
THROW_ARANGO_EXCEPTION_MESSAGE(
TRI_ERROR_INTERNAL, std::string("histogram ") + name + " alredy exists");
}
return *metric;
};
template<typename T> Histogram<T>& histogram (std::string const& name) {
std::lock_guard<std::mutex> guard(_lock);
auto const it = _registry.find(name);
if (it == _registry.end()) {
LOG_TOPIC("32d85", ERR, Logger::STATISTICS) << "No histogram booked as " << name;
TRI_ASSERT(false);
throw std::exception();
}
std::shared_ptr<Histogram<T>> h = nullptr;
try {
h = std::dynamic_pointer_cast<Histogram<T>>(*it->second);
if (h == nullptr) {
LOG_TOPIC("d2358", ERR, Logger::STATISTICS) << "Failed to retrieve histogram " << name;
}
} catch (std::exception const& e) {
LOG_TOPIC("32d75", ERR, Logger::STATISTICS)
<< "Failed to retrieve histogram " << name << ": " << e.what();
}
if (h == nullptr) {
TRI_ASSERT(false);
}
return *h;
};
std::shared_ptr<Histogram<T>> metric = nullptr;
std::string error;
{
std::lock_guard<std::mutex> guard(_lock);
registry_type::const_iterator it = _registry.find(name);
try {
metric = std::dynamic_pointer_cast<Histogram<T>>(*it->second);
if (metric == nullptr) {
error = std::string("Failed to retrieve histogram ") + name;
}
} catch (std::exception const& e) {
error = std::string("Failed to retrieve histogram ") + name + ": " + e.what();
}
}
if (!error.empty()) {
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, error);
}
return *metric;
}
Counter& counter(std::string const& name, uint64_t const& val, std::string const& help);
Counter& counter(std::string const& name);
template<typename T>
Gauge<T>& gauge(std::string const& name, T const& t, std::string const& help) {
std::lock_guard<std::mutex> guard(_lock);
auto it = _registry.find(name);
if (it != _registry.end()) {
LOG_TOPIC("c7b37", ERR, Logger::STATISTICS) << "gauge " << name << " alredy exists";
TRI_ASSERT(false);
throw(std::exception());
auto metric = std::make_shared<Gauge<T>>(t, name, help);
bool success = false;
{
std::lock_guard<std::mutex> guard(_lock);
success = _registry.try_emplace(
name, std::dynamic_pointer_cast<Metric>(metric)).second;
}
auto g = std::make_shared<Gauge>(t, name, help);
_registry.emplace(name, std::dynamic_pointer_cast<Metric>(g));
return *g;
if (!success) {
THROW_ARANGO_EXCEPTION_MESSAGE(
TRI_ERROR_INTERNAL, std::string("gauge ") + name + " alredy exists");
}
return *metric;
}
template<typename T> Gauge<T>& gauge(std::string const& name) {
std::lock_guard<std::mutex> guard(_lock);
auto it = _registry.find(name);
if (it == _registry.end()) {
LOG_TOPIC("5832d", ERR, Logger::STATISTICS) << "No metric booked as " << name;
TRI_ASSERT(false);
throw std::exception();
}
std::shared_ptr<Gauge<T>> g = nullptr;
try {
g = std::dynamic_pointer_cast<Gauge<T>>(it->second);
if (g == nullptr) {
LOG_TOPIC("d4368", ERR, Logger::STATISTICS) << "Failed to retrieve gauge metric " << name;
TRI_ASSERT(false);
throw std::exception();
std::shared_ptr<Gauge<T>> metric = nullptr;
std::string error;
{
std::lock_guard<std::mutex> guard(_lock);
registry_type::const_iterator it = _registry.find(name);
if (it == _registry.end()) {
THROW_ARANGO_EXCEPTION_MESSAGE(
TRI_ERROR_INTERNAL, std::string("No gauge booked as ") + name);
}
try {
metric = std::dynamic_pointer_cast<Gauge<T>>(*it->second);
if (metric == nullptr) {
error = std::string("Failed to retrieve gauge ") + name;
}
} catch (std::exception const& e) {
error = std::string("Failed to retrieve gauge ") + name + ": " + e.what();
}
} catch (std::exception const& e) {
LOG_TOPIC("c2348", ERR, Logger::STATISTICS)
<< "Failed to retrieve gauge metric " << name << ": " << e.what();
TRI_ASSERT(false);
throw(e);
}
if (!error.empty()) {
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, error);
}
return *metric;
}
void toPrometheus(std::string& result) const;
static MetricsFeature* metrics() {
return METRICS;
}
ServerStatistics& serverStatistics();
private:
static MetricsFeature* METRICS;
bool _enabled;
std::unordered_map<std::string, std::shared_ptr<Metric>> _registry;
private:
registry_type _registry;
mutable std::mutex _lock;
ServerStatistics* _serverStatistics;
std::unique_ptr<ServerStatistics> _serverStatistics;
bool _export;
};
} // namespace arangodb
#endif
#endif

View File

@ -156,7 +156,6 @@ static int runServer(int argc, char** argv, ArangoGlobalContext& context) {
std::type_index(typeid(GreetingsFeature)),
std::type_index(typeid(HttpEndpointProvider)),
std::type_index(typeid(LoggerBufferFeature)),
std::type_index(typeid(MetricsFeature)),
std::type_index(typeid(pregel::PregelFeature)),
std::type_index(typeid(ServerFeature)),
std::type_index(typeid(SslServerFeature)),
@ -210,6 +209,7 @@ static int runServer(int argc, char** argv, ArangoGlobalContext& context) {
server.addFeature<LoggerFeature>(true);
server.addFeature<MaintenanceFeature>();
server.addFeature<MaxMapCountFeature>();
server.addFeature<MetricsFeature>();
server.addFeature<NetworkFeature>();
server.addFeature<NonceFeature>();
server.addFeature<PageSizeFeature>();
@ -230,7 +230,6 @@ static int runServer(int argc, char** argv, ArangoGlobalContext& context) {
std::vector<std::type_index>{std::type_index(typeid(ScriptFeature))});
server.addFeature<SslFeature>();
server.addFeature<StatisticsFeature>();
server.addFeature<MetricsFeature>();
server.addFeature<StorageEngineFeature>();
server.addFeature<SystemDatabaseFeature>();
server.addFeature<TempFeature>(name);

View File

@ -107,7 +107,7 @@ Result RocksDBTransactionState::beginTransaction(transaction::Hints hints) {
// register with manager
transaction::ManagerFeature::manager()->registerTransaction(id(), nullptr, isReadOnlyTransaction());
updateStatus(transaction::Status::RUNNING);
MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._transactionsStarted++;
_vocbase.server().getFeature<MetricsFeature>().serverStatistics()._transactionsStatistics._transactionsStarted++;
setRegistered();
@ -398,7 +398,7 @@ Result RocksDBTransactionState::commitTransaction(transaction::Methods* activeTr
if (res.ok()) {
updateStatus(transaction::Status::COMMITTED);
cleanupTransaction(); // deletes trx
MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._transactionsCommitted++;
_vocbase.server().getFeature<MetricsFeature>().serverStatistics()._transactionsStatistics._transactionsCommitted++;
} else {
abortTransaction(activeTrx); // deletes trx
}
@ -430,7 +430,7 @@ Result RocksDBTransactionState::abortTransaction(transaction::Methods* activeTrx
TRI_ASSERT(!_rocksTransaction && !_cacheTx && !_readSnapshot);
}
MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._transactionsAborted++;
_vocbase.server().getFeature<MetricsFeature>().serverStatistics()._transactionsStatistics._transactionsAborted++;
unuseCollections(nestingLevel());
return result;
}
@ -602,7 +602,7 @@ Result RocksDBTransactionState::triggerIntermediateCommit(bool& hasPerformedInte
}
hasPerformedIntermediateCommit = true;
MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._intermediateCommits++;
_vocbase.server().getFeature<MetricsFeature>().serverStatistics()._transactionsStatistics._intermediateCommits++;
TRI_IF_FAILURE("FailAfterIntermediateCommit") {
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);

View File

@ -17,7 +17,7 @@
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Simon Grätzer
/// @author Simon Graetzer
////////////////////////////////////////////////////////////////////////////////
#include "Descriptions.h"
@ -411,7 +411,10 @@ stats::Descriptions::Descriptions()
}
void stats::Descriptions::serverStatistics(velocypack::Builder& b) const {
ServerStatistics const& info = MetricsFeature::metrics()->serverStatistics();
auto& server = application_features::ApplicationServer::server();
ServerStatistics const& info =
server.getFeature<MetricsFeature>().serverStatistics();
b.add("uptime", VPackValue(info._uptime));
b.add("physicalMemory", VPackValue(TRI_PhysicalMemory));
@ -422,7 +425,6 @@ void stats::Descriptions::serverStatistics(velocypack::Builder& b) const {
b.add("intermediateCommits", VPackValue(info._transactionsStatistics._intermediateCommits.load()));
b.close();
auto& server = application_features::ApplicationServer::server();
V8DealerFeature& dealer = server.getFeature<V8DealerFeature>();
if (dealer.isEnabled()) {
b.add("v8Context", VPackValue(VPackValueType::Object, true));

View File

@ -22,21 +22,23 @@
////////////////////////////////////////////////////////////////////////////////
#include "ServerStatistics.h"
#include "ApplicationFeatures/ApplicationFeature.h"
#include "Statistics/StatisticsFeature.h"
#include "RestServer/MetricsFeature.h"
using namespace arangodb;
TransactionStatistics::TransactionStatistics() :
_metrics(arangodb::MetricsFeature::metrics()),
_metrics(application_features::ApplicationServer::server().
getFeature<MetricsFeature>()),
_transactionsStarted(
_metrics->counter("arangodb_transactions_started", 0, "Transactions started")),
_metrics.counter("arangodb_transactions_started", 0, "Transactions started")),
_transactionsAborted(
_metrics->counter("arangodb_transactions_aborted", 0, "Transactions aborted")),
_metrics.counter("arangodb_transactions_aborted", 0, "Transactions aborted")),
_transactionsCommitted(
_metrics->counter("arangodb_transactions_committed", 0, "Transactions committed")),
_metrics.counter("arangodb_transactions_committed", 0, "Transactions committed")),
_intermediateCommits(
_metrics->counter("arangodb_intermediate_commits", 0, "Intermediate commits")) {}
_metrics.counter("arangodb_intermediate_commits", 0, "Intermediate commits")) {}
// -----------------------------------------------------------------------------
// --SECTION-- static public methods

View File

@ -40,7 +40,7 @@ struct TransactionStatistics {
TransactionStatistics& operator=(TransactionStatistics const&) = delete;
TransactionStatistics& operator=(TransactionStatistics &&) = delete;
arangodb::MetricsFeature* _metrics;
arangodb::MetricsFeature& _metrics;
Counter& _transactionsStarted;
Counter& _transactionsAborted;

View File

@ -187,7 +187,8 @@ void StatisticsFeature::prepare() {
STATISTICS = this;
MetricsFeature::metrics()->serverStatistics().initialize(StatisticsFeature::time());
server().getFeature<MetricsFeature>().serverStatistics().initialize(
StatisticsFeature::time());
ConnectionStatistics::initialize();
RequestStatistics::initialize();
}

View File

@ -1096,7 +1096,10 @@ void StatisticsWorker::generateRawStatistics(VPackBuilder& builder, double const
RequestStatistics::fill(totalTime, requestTime, queueTime, ioTime, bytesSent, bytesReceived, stats::RequestStatisticsSource::ALL);
ServerStatistics const& serverInfo = MetricsFeature::metrics()->serverStatistics();
ServerStatistics const& serverInfo =
application_features::ApplicationServer::server().
getFeature<MetricsFeature>().serverStatistics();
builder.openObject();
if (!_clusterId.empty()) {

View File

@ -99,7 +99,9 @@ static void JS_ServerStatistics(v8::FunctionCallbackInfo<v8::Value> const& args)
TRI_V8_TRY_CATCH_BEGIN(isolate)
v8::HandleScope scope(isolate);
ServerStatistics const& info = MetricsFeature::metrics()->serverStatistics();
ServerStatistics const& info =
application_features::ApplicationServer::server().
getFeature<MetricsFeature>().serverStatistics();
v8::Handle<v8::Object> result = v8::Object::New(isolate);

View File

@ -194,6 +194,7 @@ set(ARANGODB_TESTS_SOURCES
Maintenance/MaintenanceRestHandlerTest.cpp
Maintenance/MaintenanceTest.cpp
${RCFILE}
Metrics/MetricsTest.cpp
Mocks/LogLevels.cpp
Mocks/StorageEngineMock.cpp
Mocks/Servers.cpp

View File

@ -0,0 +1,171 @@
////////////////////////////////////////////////////////////////////////////////
/// @brief test suite for Cluster maintenance
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2017 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 Kaveh Vahedipour
/// @author Matthew Von-Maszewski
/// @author Copyright 2017-2018, ArangoDB GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
#include "gtest/gtest.h"
#include "RestServer/Metrics.h"
class MetricsTest : public ::testing::Test {
protected:
MetricsTest () {}
};
TEST_F(MetricsTest, test_counter) {
Counter c(0, "counter_1", "Counter 1");
ASSERT_EQ(c.load(), 0);
c++;
ASSERT_EQ(c.load(), 1);
c += 9;
ASSERT_EQ(c.load(), 10);
c = 0;
ASSERT_EQ(c.load(), 0);
c.count();
ASSERT_EQ(c.load(), 1);
c.count( 9);
ASSERT_EQ(c.load(), 10);
c.store(0);
ASSERT_EQ(c.load(), 0);
std::string s;
c.toPrometheus(s);
LOG_DEVEL << s;
}
template<typename T> void gauge_test() {
T zdo = .1, zero = 0., one = 1.;
Gauge g(zero, "gauge_1", "Gauge 1");
ASSERT_DOUBLE_EQ(g.load(), zero);
g += zdo;
ASSERT_DOUBLE_EQ(g.load(), zdo);
g -= zdo;
ASSERT_DOUBLE_EQ(g.load(), zero);
g += zdo;
g *= g.load();
ASSERT_DOUBLE_EQ(g.load(), zdo*zdo);
g /= g.load();
ASSERT_DOUBLE_EQ(g.load(), one);
g -= g.load();
ASSERT_DOUBLE_EQ(g.load(), zero);
std::string s;
g.toPrometheus(s);
LOG_DEVEL << s;
}
TEST_F(MetricsTest, long_double) {
gauge_test<long double>();
}
TEST_F(MetricsTest, test_gauge_double) {
gauge_test<double>();
}
TEST_F(MetricsTest, test_gauge_float) {
gauge_test<float>();
}
template<typename T> void histogram_test(
size_t const& buckets, T const& mn, T const& mx) {
T span = mx - mn, step = span/(T)buckets,
mmin = (std::is_floating_point<T>::value) ? span / T(1.e6) : T(1),
one = T(1), ten = T(10), thousand(10);
Histogram h(buckets, mn, mx, "hist_test", "Hist test");
//lower bucket bounds
for (size_t i = 0; i < buckets; ++i) {
auto d = mn + step*i + mmin;
h.count(d);
ASSERT_DOUBLE_EQ(h.load(i), 1);
}
//upper bucket bounds
for (size_t i = 0; i < buckets; ++i) {
auto d = mn + step*(i+1) - mmin;
h.count(d);
ASSERT_DOUBLE_EQ(h.load(i), 2);
}
//below lower limit
h.count(mn - one);
h.count(mn - ten);
h.count(mn - thousand);
ASSERT_DOUBLE_EQ(h.load(0), 5);
// above upper limit
h.count(mx + one);
h.count(mx + ten);
h.count(mx + thousand);
ASSERT_DOUBLE_EQ(h.load(buckets-1), 5);
// dump
std::string s;
h.toPrometheus(s);
LOG_DEVEL << s;
}
TEST_F(MetricsTest, test_float_histogram) {
histogram_test<long double>( 9, 1., 2.);
histogram_test<long double>(10, -1., 1.);
histogram_test<long double>( 8, -2., -1.);
}
TEST_F(MetricsTest, test_double_histogram) {
histogram_test<long double>(12, 1., 2.);
histogram_test<long double>(11, -1., 1.);
histogram_test<long double>(13, -2., -1.);
}
TEST_F(MetricsTest, test_long_double_histogram) {
histogram_test<long double>(12, 1., 2.);
histogram_test<long double>(17, -1., 1.);
histogram_test<long double>( 7, -2., -1.);
}
TEST_F(MetricsTest, test_short_histogram) {
histogram_test<short>(6, -17, 349);
histogram_test<short>(7, 20, 40);
histogram_test<short>(8, -63, -11);
}
TEST_F(MetricsTest, test_int_histogram) {
histogram_test<short>(6, -17, 349);
histogram_test<short>(7, 20, 40);
histogram_test<short>(8, -63, -11);
}
TEST_F(MetricsTest, test_long_histogram) {
histogram_test<short>(6, -17, 349);
histogram_test<short>(7, 20, 40);
histogram_test<short>(8, -63, -11);
}

View File

@ -115,6 +115,18 @@ function testSuite() {
assertFalse(result.hasOwnProperty("foxxApi"));
},
testCanAccessAdminMetricsRw : function() {
arango.reconnect(endpoint, db._name(), "test_rw", "testi");
let result = arango.GET("/_admin/metrics");
},
testCanAccessAdminMetricsRo : function() {
arango.reconnect(endpoint, db._name(), "test_ro", "testi");
let result = arango.GET("/_admin/metrics");
assertTrue(result.error);
assertEqual(403, result.code);
},
testCanAccessAdminLogRw : function() {
arango.reconnect(endpoint, db._name(), "test_rw", "testi");
let result = arango.GET("/_admin/log");

View File

@ -18,17 +18,16 @@ describe('Foxx service path handling', () => {
expect(encodeURIComponent('+')).not.to.equal('+');
expect(encodeURIComponent('/')).not.to.equal('/');
expect(encodeURIComponent(encodeURIComponent('/'))).not.to.equal(encodeURIComponent('/'));
});
beforeEach(function () {
try {
fm.uninstall(mount, {force: true});
} catch (e) {}
fm.install(fs.join(basePath, 'paths'), mount);
internal.sleep(1);
});
afterEach(function () {
fm.uninstall(mount, {force: true});
});
beforeEach(function () { });
afterEach(function () { });
it('supports plain URLs', () => {
const res = request.get(`${baseUrl}/${mount}/hello/world`);