From 504b1022683fde48fd0799aef563cfb7c6f01a22 Mon Sep 17 00:00:00 2001 From: jsteemann Date: Thu, 8 Sep 2016 11:15:49 +0200 Subject: [PATCH] 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 --- CHANGELOG | 5 ++ .../AdministrationAndMonitoring/README.mdpp | 6 +- .../JSF_get_admin_modules_flush.md | 55 +++++++++++- .../GeneralServer/GeneralServerFeature.cpp | 2 +- arangod/GeneralServer/RestHandler.cpp | 6 ++ arangod/RestHandler/RestAdminLogHandler.cpp | 83 +++++++++++++++++-- arangod/RestHandler/RestAdminLogHandler.h | 4 + arangod/RestHandler/RestBaseHandler.cpp | 19 +++++ arangod/RestHandler/RestBaseHandler.h | 4 + .../RestHandler/RestVocbaseBaseHandler.cpp | 19 ----- arangod/RestHandler/RestVocbaseBaseHandler.h | 6 -- 11 files changed, 173 insertions(+), 36 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 994e1aa3d9..8ee691bee3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -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` diff --git a/Documentation/Books/HTTP/AdministrationAndMonitoring/README.mdpp b/Documentation/Books/HTTP/AdministrationAndMonitoring/README.mdpp index d768cb35e4..ef1fe661c9 100644 --- a/Documentation/Books/HTTP/AdministrationAndMonitoring/README.mdpp +++ b/Documentation/Books/HTTP/AdministrationAndMonitoring/README.mdpp @@ -6,7 +6,11 @@ monitoring of the server. -@startDocuBlock JSF_get_admin_modules_flush +@startDocuBlock JSF_get_admin_log + +@startDocuBlock JSF_get_admin_loglevel + +@startDocuBlock JSF_put_admin_loglevel diff --git a/Documentation/DocuBlocks/Rest/Administration/JSF_get_admin_modules_flush.md b/Documentation/DocuBlocks/Rest/Administration/JSF_get_admin_modules_flush.md index 634caa4030..0064350275 100644 --- a/Documentation/DocuBlocks/Rest/Administration/JSF_get_admin_modules_flush.md +++ b/Documentation/DocuBlocks/Rest/Administration/JSF_get_admin_modules_flush.md @@ -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 + diff --git a/arangod/GeneralServer/GeneralServerFeature.cpp b/arangod/GeneralServer/GeneralServerFeature.cpp index 319084e27c..d0ae49fbdd 100644 --- a/arangod/GeneralServer/GeneralServerFeature.cpp +++ b/arangod/GeneralServer/GeneralServerFeature.cpp @@ -521,7 +521,7 @@ void GeneralServerFeature::defineHandlers() { "/_admin/version", RestHandlerCreator::createNoData); // further admin handlers - _handlerFactory->addHandler( + _handlerFactory->addPrefixHandler( "/_admin/log", RestHandlerCreator::createNoData); diff --git a/arangod/GeneralServer/RestHandler.cpp b/arangod/GeneralServer/RestHandler.cpp index f18b0c3b5d..f484d1be0d 100644 --- a/arangod/GeneralServer/RestHandler.cpp +++ b/arangod/GeneralServer/RestHandler.cpp @@ -28,6 +28,8 @@ #include "Logger/Logger.h" #include "Rest/GeneralRequest.h" +#include + 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__); diff --git a/arangod/RestHandler/RestAdminLogHandler.cpp b/arangod/RestHandler/RestAdminLogHandler.cpp index 482bfb03e2..3ef577c841 100644 --- a/arangod/RestHandler/RestAdminLogHandler.cpp +++ b/arangod/RestHandler/RestAdminLogHandler.cpp @@ -24,6 +24,7 @@ #include "RestAdminLogHandler.h" #include +#include #include #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 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 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); + } } diff --git a/arangod/RestHandler/RestAdminLogHandler.h b/arangod/RestHandler/RestAdminLogHandler.h index 3bb2b504d3..d8fa71a115 100644 --- a/arangod/RestHandler/RestAdminLogHandler.h +++ b/arangod/RestHandler/RestAdminLogHandler.h @@ -45,6 +45,10 @@ class RestAdminLogHandler : public RestBaseHandler { ////////////////////////////////////////////////////////////////////////////// status execute() override; + + private: + void reportLogs(); + void setLogLevel(); }; } diff --git a/arangod/RestHandler/RestBaseHandler.cpp b/arangod/RestHandler/RestBaseHandler.cpp index d13f12b9a9..a3124ea804 100644 --- a/arangod/RestHandler/RestBaseHandler.cpp +++ b/arangod/RestHandler/RestBaseHandler.cpp @@ -43,6 +43,25 @@ RestBaseHandler::RestBaseHandler(GeneralRequest* request, GeneralResponse* response) : RestHandler(request, response) {} +//////////////////////////////////////////////////////////////////////////////// +/// @brief parses the body as VelocyPack +//////////////////////////////////////////////////////////////////////////////// + +std::shared_ptr 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(); +} + void RestBaseHandler::handleError(Exception const& ex) { generateError(GeneralResponse::responseCode(ex.code()), ex.code(), ex.what()); } diff --git a/arangod/RestHandler/RestBaseHandler.h b/arangod/RestHandler/RestBaseHandler.h index 0a99e338ec..02214463e0 100644 --- a/arangod/RestHandler/RestBaseHandler.h +++ b/arangod/RestHandler/RestBaseHandler.h @@ -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 parseVelocyPackBody(arangodb::velocypack::Options const*, bool&); + template void writeResult(Payload&&, arangodb::velocypack::Options const& options); }; diff --git a/arangod/RestHandler/RestVocbaseBaseHandler.cpp b/arangod/RestHandler/RestVocbaseBaseHandler.cpp index d553539469..566cfb9c2e 100644 --- a/arangod/RestHandler/RestVocbaseBaseHandler.cpp +++ b/arangod/RestHandler/RestVocbaseBaseHandler.cpp @@ -636,25 +636,6 @@ bool RestVocbaseBaseHandler::extractBooleanParameter(char const* name, return def; } -//////////////////////////////////////////////////////////////////////////////// -/// @brief parses the body as VelocyPack -//////////////////////////////////////////////////////////////////////////////// - -std::shared_ptr 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(); -} - //////////////////////////////////////////////////////////////////////////////// /// @brief prepareExecute, to react to X-Arango-Nolock header //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/RestHandler/RestVocbaseBaseHandler.h b/arangod/RestHandler/RestVocbaseBaseHandler.h index d59c35e5ee..e73fcfa266 100644 --- a/arangod/RestHandler/RestVocbaseBaseHandler.h +++ b/arangod/RestHandler/RestVocbaseBaseHandler.h @@ -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 parseVelocyPackBody(VPackOptions const*, bool&); - protected: ////////////////////////////////////////////////////////////////////////////// /// @brief request context