mirror of https://gitee.com/bigwinds/arangodb
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:
commit
6980ac70f9
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
|
|
@ -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(); }
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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");
|
||||
|
|
|
@ -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`);
|
Loading…
Reference in New Issue