Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RPC] Add logging RPC #1451

Merged
merged 4 commits into from
Apr 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion doc/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,11 @@ The following commands have been removed from the RPC interface:
### Newly introduced commands

The following new commands have been added to the RPC interface:
- `...`
- `logging` <br>Gets and sets the logging configuration.<br>
When called without an argument, returns the list of categories that are currently being debug logged.<br>
When called with arguments, adds or removes categories from debug logging.<br>
E.g. `logging "[\"all\"]" "[\"http\"]""`


Details about each new command can be found below.

Expand Down
29 changes: 21 additions & 8 deletions src/httpserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -389,14 +389,13 @@ bool InitHTTPServer()

// Redirect libevent's logging to our own log
event_set_log_callback(&libevent_log_cb);
#if LIBEVENT_VERSION_NUMBER >= 0x02010100
// If -debug=libevent, set full libevent debugging.
// Otherwise, disable all libevent debugging.
if (LogAcceptCategory(BCLog::LIBEVENT))
event_enable_debug_logging(EVENT_DBG_ALL);
else
event_enable_debug_logging(EVENT_DBG_NONE);
#endif
// Update libevent's log handling. Returns false if our version of
// libevent doesn't support debug logging, in which case we should
// clear the BCLog::LIBEVENT flag.
if (!UpdateHTTPServerLogging(logCategories & BCLog::LIBEVENT)) {
logCategories &= ~BCLog::LIBEVENT;
}

#ifdef WIN32
evthread_use_windows_threads();
#else
Expand Down Expand Up @@ -439,6 +438,20 @@ bool InitHTTPServer()
return true;
}

bool UpdateHTTPServerLogging(bool enable) {
#if LIBEVENT_VERSION_NUMBER >= 0x02010100
if (enable) {
event_enable_debug_logging(EVENT_DBG_ALL);
} else {
event_enable_debug_logging(EVENT_DBG_NONE);
}
return true;
#else
// Can't update libevent logging if version < 02010100
return false;
#endif
}

std::thread threadHTTP;
std::future<bool> threadResult;

Expand Down
4 changes: 4 additions & 0 deletions src/httpserver.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ void InterruptHTTPServer();
/** Stop HTTP server */
void StopHTTPServer();

/** Change logging level for libevent. Removes BCLog::LIBEVENT from logCategories if
* libevent doesn't support debug logging.*/
bool UpdateHTTPServerLogging(bool enable);

/** Handler for requests to a certain HTTP path */
typedef std::function<void(HTTPRequest* req, const std::string &)> HTTPRequestHandler;
/** Register handler for prefix.
Expand Down
2 changes: 2 additions & 0 deletions src/rpc/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ static const CRPCConvertParam vRPCConvertParams[] =
{"listunspent", 1},
{"listunspent", 2},
{"listunspent", 3},
{"logging", 0},
{"logging", 1},
{"getblock", 1},
{"getblockheader", 1},
{"gettransaction", 1},
Expand Down
68 changes: 68 additions & 0 deletions src/rpc/misc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "base58.h"
#include "clientversion.h"
#include "httpserver.h"
#include "init.h"
#include "main.h"
#include "masternode-sync.h"
Expand Down Expand Up @@ -582,6 +583,73 @@ UniValue setmocktime(const UniValue& params, bool fHelp)
return NullUniValue;
}

uint32_t getCategoryMask(UniValue cats) {
cats = cats.get_array();
uint32_t mask = 0;
for (unsigned int i = 0; i < cats.size(); ++i) {
uint32_t flag = 0;
std::string cat = cats[i].get_str();
if (!GetLogCategory(&flag, &cat)) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown logging category " + cat);
}
mask |= flag;
}
return mask;
}

UniValue logging(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() > 2) {
throw std::runtime_error(
"logging [include,...] <exclude>\n"
"Gets and sets the logging configuration.\n"
"When called without an argument, returns the list of categories that are currently being debug logged.\n"
"When called with arguments, adds or removes categories from debug logging.\n"
"The valid logging categories are: " + ListLogCategories() + "\n"
"libevent logging is configured on startup and cannot be modified by this RPC during runtime."
"Arguments:\n"
"1. \"include\" (array of strings) add debug logging for these categories.\n"
"2. \"exclude\" (array of strings) remove debug logging for these categories.\n"
"\nResult: <categories> (string): a list of the logging categories that are active.\n"
"\nExamples:\n"
+ HelpExampleCli("logging", "\"[\\\"all\\\"]\" \"[\\\"http\\\"]\"")
+ HelpExampleRpc("logging", "[\"all\"], \"[libevent]\"")
);
}

uint32_t originalLogCategories = logCategories;
if (params.size() > 0 && params[0].isArray()) {
logCategories |= getCategoryMask(params[0]);
}

if (params.size() > 1 && params[1].isArray()) {
logCategories &= ~getCategoryMask(params[1]);
}

// Update libevent logging if BCLog::LIBEVENT has changed.
// If the library version doesn't allow it, UpdateHTTPServerLogging() returns false,
// in which case we should clear the BCLog::LIBEVENT flag.
// Throw an error if the user has explicitly asked to change only the libevent
// flag and it failed.
uint32_t changedLogCategories = originalLogCategories ^ logCategories;
if (changedLogCategories & BCLog::LIBEVENT) {
if (!UpdateHTTPServerLogging(logCategories & BCLog::LIBEVENT)) {
logCategories &= ~BCLog::LIBEVENT;
if (changedLogCategories == BCLog::LIBEVENT) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "libevent logging cannot be updated when using libevent before v2.1.1.");
}
}
}

UniValue result(UniValue::VOBJ);
std::vector<CLogCategoryActive> vLogCatActive = ListActiveLogCategories();
for (const auto& logCatActive : vLogCatActive) {
result.pushKV(logCatActive.category, logCatActive.active);
}

return result;
}

#ifdef ENABLE_WALLET
UniValue getstakingstatus(const UniValue& params, bool fHelp)
{
Expand Down
1 change: 1 addition & 0 deletions src/rpc/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ static const CRPCCommand vRPCCommands[] =

/* Utility functions */
{"util", "createmultisig", &createmultisig, true, true, false},
{"util", "logging", &logging, true, false, false},
{"util", "validateaddress", &validateaddress, true, false, false}, /* uses wallet if enabled */
{"util", "verifymessage", &verifymessage, true, false, false},
{"util", "estimatefee", &estimatefee, true, true, false},
Expand Down
1 change: 1 addition & 0 deletions src/rpc/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ extern UniValue mnfinalbudget(const UniValue& params, bool fHelp);
extern UniValue checkbudgets(const UniValue& params, bool fHelp);

extern UniValue getinfo(const UniValue& params, bool fHelp); // in rpc/misc.cpp
extern UniValue logging(const UniValue& params, bool fHelp);
extern UniValue mnsync(const UniValue& params, bool fHelp);
extern UniValue spork(const UniValue& params, bool fHelp);
extern UniValue validateaddress(const UniValue& params, bool fHelp);
Expand Down
18 changes: 16 additions & 2 deletions src/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,9 @@ bool fLogTimestamps = false;
bool fLogIPs = false;
volatile bool fReopenDebugLog = false;

/** Log categories bitfield. Leveldb/libevent need special handling if their flags are changed at runtime. */
/** Log categories bitfield. */
std::atomic<uint32_t> logCategories(0);


/** Init OpenSSL library multithreading support */
static RecursiveMutex** ppmutexOpenSSL;
void locking_callback(int mode, int i, const char* file, int line) NO_THREAD_SAFETY_ANALYSIS
Expand Down Expand Up @@ -319,6 +318,21 @@ static std::string LogTimestampStr(const std::string &str, bool *fStartedNewLine
return strStamped;
}

std::vector<CLogCategoryActive> ListActiveLogCategories()
{
std::vector<CLogCategoryActive> ret;
for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) {
// Omit the special cases.
if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) {
CLogCategoryActive catActive;
catActive.category = LogCategories[i].category;
catActive.active = LogAcceptCategory(LogCategories[i].flag);
ret.push_back(catActive);
}
}
return ret;
}

int LogPrintStr(const std::string& str)
{
int ret = 0; // Returns total number of characters written
Expand Down
10 changes: 9 additions & 1 deletion src/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ extern volatile bool fReopenDebugLog;
void SetupEnvironment();
bool SetupNetworking();

struct CLogCategoryActive
{
std::string category;
bool active;
};

namespace BCLog {
enum LogFlags : uint32_t {
NONE = 0,
Expand Down Expand Up @@ -95,8 +101,10 @@ static inline bool LogAcceptCategory(uint32_t category)
return (logCategories.load(std::memory_order_relaxed) & category) != 0;
}

/** Returns a string with the supported log categories */
/** Returns a string with the log categories. */
std::string ListLogCategories();
/** Returns a vector of the active log categories. */
std::vector<CLogCategoryActive> ListActiveLogCategories();
/** Return true if str parses as a log category and set the flags in f */
bool GetLogCategory(uint32_t *f, const std::string *str);
/** Send a string to the log output */
Expand Down