1
0
Fork 0

added HTTP REST APIs for online loglevel adjustments:

- GET `/_admin/log/level` returns the current loglevel settings
- PUT `/_admin/log/level` modifies the current loglevel settings
This commit is contained in:
jsteemann 2016-09-08 11:15:49 +02:00
parent 2df53c92ce
commit 504b102268
11 changed files with 173 additions and 36 deletions

View File

@ -1,6 +1,11 @@
devel
-----
* added HTTP REST APIs for online loglevel adjustments:
- GET `/_admin/log/level` returns the current loglevel settings
- PUT `/_admin/log/level` modifies the current loglevel settings
* changed default for keepNull of modifyVertex/modifyEdge to true in Gharial
* renamed `maximalSize` attribute in parameter.json files to `journalSize`

View File

@ -6,7 +6,11 @@ monitoring of the server.
<!-- lib/Admin/RestAdminLogHandler.cpp -->
@startDocuBlock JSF_get_admin_modules_flush
@startDocuBlock JSF_get_admin_log
@startDocuBlock JSF_get_admin_loglevel
@startDocuBlock JSF_put_admin_loglevel
<!-- js/actions/api-system.js -->

View File

@ -1,6 +1,6 @@
@startDocuBlock JSF_get_admin_modules_flush
@brief returns the log files
@startDocuBlock JSF_get_admin_log
@brief returns the server logs
@RESTHEADER{GET /_admin/log, Read global logs from the server}
@ -65,3 +65,54 @@ is returned if the server cannot generate the result due to an out-of-memory
error.
@endDocuBlock
@startDocuBlock JSF_get_admin_loglevel
@brief returns the current loglevel settings
@RESTHEADER{GET /_admin/log/level, Return the current server loglevel}
@RESTDESCRIPTION
Returns the server's current loglevel settings.
The result is a JSON object with the log topics being the object keys, and
the log levels being the object values.
@RESTRETURNCODES
@RESTRETURNCODE{200}
is returned if the request is valid
@RESTRETURNCODE{500}
is returned if the server cannot generate the result due to an out-of-memory
error.
@endDocuBlock
@startDocuBlock JSF_put_admin_loglevel
@brief modifies the current loglevel settings
@RESTHEADER{PUT /_admin/log/level, Modify and return the current server loglevel}
@RESTDESCRIPTION
Modifies and returns the server's current loglevel settings.
The request body must be a JSON object with the log topics being the object keys
and the log levels being the object values.
The result is a JSON object with the adjusted log topics being the object keys, and
the adjusted log levels being the object values.
@RESTRETURNCODES
@RESTRETURNCODE{200}
is returned if the request is valid
@RESTRETURNCODE{400}
is returned when the request body contains invalid JSON.
@RESTRETURNCODE{405}
is returned when an invalid HTTP method is used.
@RESTRETURNCODE{500}
is returned if the server cannot generate the result due to an out-of-memory
error.
@endDocuBlock

View File

@ -521,7 +521,7 @@ void GeneralServerFeature::defineHandlers() {
"/_admin/version", RestHandlerCreator<RestVersionHandler>::createNoData);
// further admin handlers
_handlerFactory->addHandler(
_handlerFactory->addPrefixHandler(
"/_admin/log",
RestHandlerCreator<arangodb::RestAdminLogHandler>::createNoData);

View File

@ -28,6 +28,8 @@
#include "Logger/Logger.h"
#include "Rest/GeneralRequest.h"
#include <velocypack/Exception.h>
using namespace arangodb;
using namespace arangodb::basics;
using namespace arangodb::rest;
@ -72,6 +74,10 @@ RestHandler::status RestHandler::executeFull() {
} catch (Exception const& ex) {
requestStatisticsAgentSetExecuteError();
handleError(ex);
} catch (arangodb::velocypack::Exception const& ex) {
requestStatisticsAgentSetExecuteError();
Exception err(TRI_ERROR_INTERNAL, std::string("VPack error: ") + ex.what(), __FILE__, __LINE__);
handleError(err);
} catch (std::bad_alloc const& ex) {
requestStatisticsAgentSetExecuteError();
Exception err(TRI_ERROR_OUT_OF_MEMORY, ex.what(), __FILE__, __LINE__);

View File

@ -24,6 +24,7 @@
#include "RestAdminLogHandler.h"
#include <velocypack/Builder.h>
#include <velocypack/Iterator.h>
#include <velocypack/velocypack-aliases.h>
#include "Basics/StringUtils.h"
@ -41,11 +42,18 @@ RestAdminLogHandler::RestAdminLogHandler(GeneralRequest* request,
bool RestAdminLogHandler::isDirect() const { return true; }
////////////////////////////////////////////////////////////////////////////////
/// @brief was docuBlock JSF_get_admin_modules_flush
////////////////////////////////////////////////////////////////////////////////
RestHandler::status RestAdminLogHandler::execute() {
size_t const len = _request->suffix().size();
if (len == 0) {
reportLogs();
} else {
setLogLevel();
}
return status::DONE;
}
void RestAdminLogHandler::reportLogs() {
// check the maximal log level to report
bool found1;
std::string const& upto =
@ -86,7 +94,7 @@ RestHandler::status RestAdminLogHandler::execute() {
TRI_ERROR_HTTP_BAD_PARAMETER,
std::string("unknown '") + (found2 ? "level" : "upto") +
"' log level: '" + logLevel + "'");
return status::DONE;
return;
}
}
@ -246,6 +254,67 @@ RestHandler::status RestAdminLogHandler::execute() {
// Has been ignored thus far
// So ignore again
}
return status::DONE;
}
void RestAdminLogHandler::setLogLevel() {
std::vector<std::string> const& suffix = _request->suffix();
// was validated earlier
TRI_ASSERT(suffix.size() > 0);
if (suffix[0] != "level") {
generateError(rest::ResponseCode::BAD,
TRI_ERROR_HTTP_SUPERFLUOUS_SUFFICES,
"superfluous suffix, expecting /_admin/log/level");
return;
}
auto const type = _request->requestType();
if (type == rest::RequestType::GET) {
// report loglevel
VPackBuilder builder;
builder.openObject();
auto const& levels = Logger::logLevelTopics();
for (auto const& level : levels) {
builder.add(level.first, VPackValue(Logger::translateLogLevel(level.second)));
}
builder.close();
generateResult(rest::ResponseCode::OK, builder.slice());
} else if (type == rest::RequestType::PUT) {
// set loglevel
bool parseSuccess = true;
std::shared_ptr<VPackBuilder> parsedBody = parseVelocyPackBody(&VPackOptions::Defaults, parseSuccess);
if (!parseSuccess) {
return;
}
VPackSlice slice = parsedBody->slice();
if (slice.isString()) {
Logger::setLogLevel(slice.copyString());
} else if (slice.isObject()) {
for (auto const& it : VPackObjectIterator(slice)) {
if (it.value.isString()) {
std::string const l = it.key.copyString() + "=" + it.value.copyString();
Logger::setLogLevel(l);
}
}
}
// now report current loglevel
VPackBuilder builder;
builder.openObject();
auto const& levels = Logger::logLevelTopics();
for (auto const& level : levels) {
builder.add(level.first, VPackValue(Logger::translateLogLevel(level.second)));
}
builder.close();
generateResult(rest::ResponseCode::OK, builder.slice());
} else {
// invalid method
generateError(rest::ResponseCode::METHOD_NOT_ALLOWED,
TRI_ERROR_HTTP_METHOD_NOT_ALLOWED);
}
}

View File

@ -45,6 +45,10 @@ class RestAdminLogHandler : public RestBaseHandler {
//////////////////////////////////////////////////////////////////////////////
status execute() override;
private:
void reportLogs();
void setLogLevel();
};
}

View File

@ -43,6 +43,25 @@ RestBaseHandler::RestBaseHandler(GeneralRequest* request,
GeneralResponse* response)
: RestHandler(request, response) {}
////////////////////////////////////////////////////////////////////////////////
/// @brief parses the body as VelocyPack
////////////////////////////////////////////////////////////////////////////////
std::shared_ptr<VPackBuilder> RestBaseHandler::parseVelocyPackBody(
VPackOptions const* options, bool& success) {
try {
success = true;
return _request->toVelocyPackBuilderPtr(options);
} catch (VPackException const& e) {
std::string errmsg("VPackError error: ");
errmsg.append(e.what());
generateError(rest::ResponseCode::BAD, TRI_ERROR_HTTP_CORRUPTED_JSON,
errmsg);
}
success = false;
return std::make_shared<VPackBuilder>();
}
void RestBaseHandler::handleError(Exception const& ex) {
generateError(GeneralResponse::responseCode(ex.code()), ex.code(), ex.what());
}

View File

@ -32,6 +32,7 @@ namespace arangodb {
class TransactionContext;
namespace velocypack {
class Builder;
struct Options;
class Slice;
}
@ -66,6 +67,9 @@ class RestBaseHandler : public rest::RestHandler {
void generateCanceled();
protected:
/// @brief parses the body as VelocyPack
std::shared_ptr<arangodb::velocypack::Builder> parseVelocyPackBody(arangodb::velocypack::Options const*, bool&);
template <typename Payload>
void writeResult(Payload&&, arangodb::velocypack::Options const& options);
};

View File

@ -636,25 +636,6 @@ bool RestVocbaseBaseHandler::extractBooleanParameter(char const* name,
return def;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief parses the body as VelocyPack
////////////////////////////////////////////////////////////////////////////////
std::shared_ptr<VPackBuilder> RestVocbaseBaseHandler::parseVelocyPackBody(
VPackOptions const* options, bool& success) {
try {
success = true;
return _request->toVelocyPackBuilderPtr(options);
} catch (VPackException const& e) {
std::string errmsg("VpackError error: ");
errmsg.append(e.what());
generateError(rest::ResponseCode::BAD, TRI_ERROR_HTTP_CORRUPTED_JSON,
errmsg);
}
success = false;
return std::make_shared<VPackBuilder>();
}
////////////////////////////////////////////////////////////////////////////////
/// @brief prepareExecute, to react to X-Arango-Nolock header
////////////////////////////////////////////////////////////////////////////////

View File

@ -266,12 +266,6 @@ class RestVocbaseBaseHandler : public RestBaseHandler {
bool extractBooleanParameter(char const* name, bool def) const;
//////////////////////////////////////////////////////////////////////////////
/// @brief parses the body as VelocyPack
//////////////////////////////////////////////////////////////////////////////
std::shared_ptr<VPackBuilder> parseVelocyPackBody(VPackOptions const*, bool&);
protected:
//////////////////////////////////////////////////////////////////////////////
/// @brief request context