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 devel
----- -----
* Fix execution ability in CentOS 6 regarding newer boost.
* Fixed permissions for dump/restore. * Fixed permissions for dump/restore.
* The _users collection is now properly restored when using arangorestore. * The _users collection is now properly restored when using arangorestore.

View File

@ -1140,6 +1140,7 @@ endif ()
# ZLIB_LIBS # ZLIB_LIBS
# ZLIB_INCLUDE_DIR # 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_definitions(-DBOOST_ALL_NO_LIB=1) #disable boost autolink on windows
add_subdirectory(3rdParty) 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([&] { auto cleanup = scopeGuard([&] {
if (nestingLevel() == 0) { if (nestingLevel() == 0) {
updateStatus(transaction::Status::ABORTED); updateStatus(transaction::Status::ABORTED);
MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._transactionsAborted++; _vocbase.server().getFeature<MetricsFeature>().serverStatistics()._transactionsStatistics._transactionsAborted++;
} }
// free what we have got so far // free what we have got so far
unuseCollections(nestingLevel()); unuseCollections(nestingLevel());
@ -75,7 +75,7 @@ Result ClusterTransactionState::beginTransaction(transaction::Hints hints) {
// all valid // all valid
if (nestingLevel() == 0) { if (nestingLevel() == 0) {
updateStatus(transaction::Status::RUNNING); updateStatus(transaction::Status::RUNNING);
MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._transactionsStarted++; _vocbase.server().getFeature<MetricsFeature>().serverStatistics()._transactionsStatistics._transactionsStarted++;
transaction::ManagerFeature::manager()->registerTransaction(id(), nullptr, isReadOnlyTransaction()); transaction::ManagerFeature::manager()->registerTransaction(id(), nullptr, isReadOnlyTransaction());
setRegistered(); setRegistered();
@ -122,7 +122,7 @@ Result ClusterTransactionState::commitTransaction(transaction::Methods* activeTr
arangodb::Result res; arangodb::Result res;
if (nestingLevel() == 0) { if (nestingLevel() == 0) {
updateStatus(transaction::Status::COMMITTED); updateStatus(transaction::Status::COMMITTED);
MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._transactionsCommitted++; _vocbase.server().getFeature<MetricsFeature>().serverStatistics()._transactionsStatistics._transactionsCommitted++;
} }
unuseCollections(nestingLevel()); unuseCollections(nestingLevel());
@ -136,7 +136,7 @@ Result ClusterTransactionState::abortTransaction(transaction::Methods* activeTrx
Result res; Result res;
if (nestingLevel() == 0) { if (nestingLevel() == 0) {
updateStatus(transaction::Status::ABORTED); updateStatus(transaction::Status::ABORTED);
MetricsFeature::metrics()->serverStatistics()._transactionsStatistics._transactionsAborted++; _vocbase.server().getFeature<MetricsFeature>().serverStatistics()._transactionsStatistics._transactionsAborted++;
} }
unuseCollections(nestingLevel()); unuseCollections(nestingLevel());

View File

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

View File

@ -58,6 +58,12 @@ RestStatus RestMetricsHandler::execute() {
} }
MetricsFeature& metrics = server.getFeature<MetricsFeature>(); 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; std::string result;
metrics.toPrometheus(result); metrics.toPrometheus(result);
_response->setResponseCode(rest::ResponseCode::OK); _response->setResponseCode(rest::ResponseCode::OK);

View File

@ -33,7 +33,7 @@ class RestMetricsHandler : public arangodb::RestBaseHandler {
GeneralResponse*); GeneralResponse*);
char const* name() const override final { return "RestMetricsHandler"; } 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; RestStatus execute() override;
}; };

View File

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

View File

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

View File

@ -53,37 +53,28 @@ using namespace arangodb::options;
// --SECTION-- MetricsFeature // --SECTION-- MetricsFeature
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
MetricsFeature* MetricsFeature::METRICS = nullptr;
#include <iostream>
MetricsFeature::MetricsFeature(application_features::ApplicationServer& server) MetricsFeature::MetricsFeature(application_features::ApplicationServer& server)
: ApplicationFeature(server, "Metrics"), : ApplicationFeature(server, "Metrics"), _export(true) {
_enabled(true) {
METRICS = this;
_serverStatistics = new
ServerStatistics(std::chrono::duration<double>(
std::chrono::system_clock::now().time_since_epoch()).count());
setOptional(false); setOptional(false);
startsAfter<LoggerFeature>(); startsAfter<LoggerFeature>();
startsBefore<GreetingsFeaturePhase>(); 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::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 { void MetricsFeature::toPrometheus(std::string& result) const {
{ {
std::lock_guard<std::mutex> guard(_lock); std::lock_guard<std::mutex> guard(_lock);
@ -111,38 +102,19 @@ void MetricsFeature::toPrometheus(std::string& result) const {
Counter& MetricsFeature::counter ( Counter& MetricsFeature::counter (
std::string const& name, uint64_t const& val, std::string const& help) { 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) { auto metric = std::make_shared<Counter>(val, name, help);
std::lock_guard<std::mutex> guard(_lock); bool success = false;
auto it = _registry.find(name); {
if (it == _registry.end()) { std::lock_guard<std::mutex> guard(_lock);
LOG_TOPIC("32d58", ERR, Logger::STATISTICS) success = _registry.emplace(name, std::dynamic_pointer_cast<Metric>(metric)).second;
<< "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);
} }
return *h; if (!success) {
}; THROW_ARANGO_EXCEPTION_MESSAGE(
TRI_ERROR_INTERNAL, std::string("counter ") + name + " alredy exists");
}
return *metric;
}
ServerStatistics& MetricsFeature::serverStatistics() { ServerStatistics& MetricsFeature::serverStatistics() {
return *_serverStatistics; return *_serverStatistics;

View File

@ -34,121 +34,122 @@ namespace arangodb {
class MetricsFeature final : public application_features::ApplicationFeature { class MetricsFeature final : public application_features::ApplicationFeature {
public: public:
bool enabled() const {
return _enabled; using registry_type = std::unordered_map<std::string, std::shared_ptr<Metric>>;
}
static double time(); static double time();
explicit MetricsFeature(application_features::ApplicationServer& server); explicit MetricsFeature(application_features::ApplicationServer& server);
bool exportAPI() const;
void collectOptions(std::shared_ptr<options::ProgramOptions>) override final; void collectOptions(std::shared_ptr<options::ProgramOptions>) override final;
void validateOptions(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>& template<typename T> Histogram<T>&
histogram (std::string const& name, size_t const& buckets, T const& low, histogram (std::string const& name, size_t const& buckets, T const& low,
T const& high, std::string const& help = std::string()) { T const& high, std::string const& help = std::string()) {
std::lock_guard<std::mutex> guard(_lock); auto metric = std::make_shared<Histogram<T>>(buckets, low, high, name, help);
auto const it = _registry.find(name); bool success = false;
if (it != _registry.end()) { {
LOG_TOPIC("32e85", ERR, Logger::STATISTICS) << "histogram " << name << " alredy exists"; std::lock_guard<std::mutex> guard(_lock);
TRI_ASSERT(false); success = _registry.try_emplace(
name, std::dynamic_pointer_cast<Metric>(metric)).second;
} }
auto h = std::make_shared<Histogram<T>>(buckets, low, high, name, help); if (!success) {
_registry.emplace(name, std::dynamic_pointer_cast<Metric>(h)); THROW_ARANGO_EXCEPTION_MESSAGE(
return *h; TRI_ERROR_INTERNAL, std::string("histogram ") + name + " alredy exists");
}
return *metric;
}; };
template<typename T> Histogram<T>& histogram (std::string const& name) { 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, uint64_t const& val, std::string const& help);
Counter& counter(std::string const& name);
template<typename T> template<typename T>
Gauge<T>& gauge(std::string const& name, T const& t, std::string const& help) { Gauge<T>& gauge(std::string const& name, T const& t, std::string const& help) {
std::lock_guard<std::mutex> guard(_lock); auto metric = std::make_shared<Gauge<T>>(t, name, help);
auto it = _registry.find(name); bool success = false;
if (it != _registry.end()) { {
LOG_TOPIC("c7b37", ERR, Logger::STATISTICS) << "gauge " << name << " alredy exists"; std::lock_guard<std::mutex> guard(_lock);
TRI_ASSERT(false); success = _registry.try_emplace(
throw(std::exception()); name, std::dynamic_pointer_cast<Metric>(metric)).second;
} }
auto g = std::make_shared<Gauge>(t, name, help); if (!success) {
_registry.emplace(name, std::dynamic_pointer_cast<Metric>(g)); THROW_ARANGO_EXCEPTION_MESSAGE(
return *g; TRI_ERROR_INTERNAL, std::string("gauge ") + name + " alredy exists");
}
return *metric;
} }
template<typename T> Gauge<T>& gauge(std::string const& name) { template<typename T> Gauge<T>& gauge(std::string const& name) {
std::lock_guard<std::mutex> guard(_lock);
auto it = _registry.find(name); std::shared_ptr<Gauge<T>> metric = nullptr;
if (it == _registry.end()) { std::string error;
LOG_TOPIC("5832d", ERR, Logger::STATISTICS) << "No metric booked as " << name; {
TRI_ASSERT(false); std::lock_guard<std::mutex> guard(_lock);
throw std::exception(); registry_type::const_iterator it = _registry.find(name);
} if (it == _registry.end()) {
std::shared_ptr<Gauge<T>> g = nullptr; THROW_ARANGO_EXCEPTION_MESSAGE(
try { TRI_ERROR_INTERNAL, std::string("No gauge booked as ") + name);
g = std::dynamic_pointer_cast<Gauge<T>>(it->second); }
if (g == nullptr) { try {
LOG_TOPIC("d4368", ERR, Logger::STATISTICS) << "Failed to retrieve gauge metric " << name; metric = std::dynamic_pointer_cast<Gauge<T>>(*it->second);
TRI_ASSERT(false); if (metric == nullptr) {
throw std::exception(); 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; void toPrometheus(std::string& result) const;
static MetricsFeature* metrics() {
return METRICS;
}
ServerStatistics& serverStatistics(); ServerStatistics& serverStatistics();
private:
static MetricsFeature* METRICS;
bool _enabled; private:
registry_type _registry;
std::unordered_map<std::string, std::shared_ptr<Metric>> _registry;
mutable std::mutex _lock; mutable std::mutex _lock;
ServerStatistics* _serverStatistics; std::unique_ptr<ServerStatistics> _serverStatistics;
bool _export;
}; };
} // namespace arangodb } // 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(GreetingsFeature)),
std::type_index(typeid(HttpEndpointProvider)), std::type_index(typeid(HttpEndpointProvider)),
std::type_index(typeid(LoggerBufferFeature)), std::type_index(typeid(LoggerBufferFeature)),
std::type_index(typeid(MetricsFeature)),
std::type_index(typeid(pregel::PregelFeature)), std::type_index(typeid(pregel::PregelFeature)),
std::type_index(typeid(ServerFeature)), std::type_index(typeid(ServerFeature)),
std::type_index(typeid(SslServerFeature)), std::type_index(typeid(SslServerFeature)),
@ -210,6 +209,7 @@ static int runServer(int argc, char** argv, ArangoGlobalContext& context) {
server.addFeature<LoggerFeature>(true); server.addFeature<LoggerFeature>(true);
server.addFeature<MaintenanceFeature>(); server.addFeature<MaintenanceFeature>();
server.addFeature<MaxMapCountFeature>(); server.addFeature<MaxMapCountFeature>();
server.addFeature<MetricsFeature>();
server.addFeature<NetworkFeature>(); server.addFeature<NetworkFeature>();
server.addFeature<NonceFeature>(); server.addFeature<NonceFeature>();
server.addFeature<PageSizeFeature>(); 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))}); std::vector<std::type_index>{std::type_index(typeid(ScriptFeature))});
server.addFeature<SslFeature>(); server.addFeature<SslFeature>();
server.addFeature<StatisticsFeature>(); server.addFeature<StatisticsFeature>();
server.addFeature<MetricsFeature>();
server.addFeature<StorageEngineFeature>(); server.addFeature<StorageEngineFeature>();
server.addFeature<SystemDatabaseFeature>(); server.addFeature<SystemDatabaseFeature>();
server.addFeature<TempFeature>(name); server.addFeature<TempFeature>(name);

View File

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

View File

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

View File

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

View File

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

View File

@ -187,7 +187,8 @@ void StatisticsFeature::prepare() {
STATISTICS = this; STATISTICS = this;
MetricsFeature::metrics()->serverStatistics().initialize(StatisticsFeature::time()); server().getFeature<MetricsFeature>().serverStatistics().initialize(
StatisticsFeature::time());
ConnectionStatistics::initialize(); ConnectionStatistics::initialize();
RequestStatistics::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); 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(); builder.openObject();
if (!_clusterId.empty()) { 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) TRI_V8_TRY_CATCH_BEGIN(isolate)
v8::HandleScope scope(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); v8::Handle<v8::Object> result = v8::Object::New(isolate);

View File

@ -194,6 +194,7 @@ set(ARANGODB_TESTS_SOURCES
Maintenance/MaintenanceRestHandlerTest.cpp Maintenance/MaintenanceRestHandlerTest.cpp
Maintenance/MaintenanceTest.cpp Maintenance/MaintenanceTest.cpp
${RCFILE} ${RCFILE}
Metrics/MetricsTest.cpp
Mocks/LogLevels.cpp Mocks/LogLevels.cpp
Mocks/StorageEngineMock.cpp Mocks/StorageEngineMock.cpp
Mocks/Servers.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")); 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() { testCanAccessAdminLogRw : function() {
arango.reconnect(endpoint, db._name(), "test_rw", "testi"); arango.reconnect(endpoint, db._name(), "test_rw", "testi");
let result = arango.GET("/_admin/log"); 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('/')).not.to.equal('/'); expect(encodeURIComponent('/')).not.to.equal('/');
expect(encodeURIComponent(encodeURIComponent('/'))).not.to.equal(encodeURIComponent('/')); expect(encodeURIComponent(encodeURIComponent('/'))).not.to.equal(encodeURIComponent('/'));
});
beforeEach(function () {
try { try {
fm.uninstall(mount, {force: true}); fm.uninstall(mount, {force: true});
} catch (e) {} } catch (e) {}
fm.install(fs.join(basePath, 'paths'), mount); fm.install(fs.join(basePath, 'paths'), mount);
internal.sleep(1);
}); });
afterEach(function () { beforeEach(function () { });
fm.uninstall(mount, {force: true}); afterEach(function () { });
});
it('supports plain URLs', () => { it('supports plain URLs', () => {
const res = request.get(`${baseUrl}/${mount}/hello/world`); const res = request.get(`${baseUrl}/${mount}/hello/world`);