diff --git a/src/httpserver.h b/src/httpserver.h --- a/src/httpserver.h +++ b/src/httpserver.h @@ -40,6 +40,12 @@ /** 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 diff --git a/src/httpserver.cpp b/src/httpserver.cpp --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include // For HTTP status codes #include @@ -379,15 +380,14 @@ // 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); + // 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( + GetLogger().WillLogCategory(BCLog::LIBEVENT))) { + GetLogger().DisableCategory(BCLog::LIBEVENT); } -#endif + #ifdef WIN32 evthread_use_windows_threads(); #else @@ -434,6 +434,20 @@ 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 threadResult; static std::vector g_thread_http_workers; diff --git a/src/logging.h b/src/logging.h --- a/src/logging.h +++ b/src/logging.h @@ -23,6 +23,11 @@ extern bool fLogIPs; extern const char *const DEFAULT_DEBUGLOGFILE; +struct CLogCategoryActive { + std::string category; + bool active; +}; + namespace BCLog { enum LogFlags : uint32_t { @@ -64,8 +69,7 @@ std::atomic_bool m_started_new_line{true}; /** - * Log categories bitfield. Leveldb/libevent need special handling if their - * flags are changed at runtime. + * Log categories bitfield. */ std::atomic m_categories{0}; @@ -89,6 +93,8 @@ bool OpenDebugLog(); void ShrinkDebugFile(); + uint32_t GetCategoryMask() const { return m_categories.load(); } + void EnableCategory(LogFlags category); bool EnableCategory(const std::string &str); void DisableCategory(LogFlags category); @@ -110,9 +116,12 @@ return GetLogger().WillLogCategory(category); } -/** 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 ListActiveLogCategories(); + /** Return true if str parses as a log category and set the flag */ bool GetLogCategory(BCLog::LogFlags &flag, const std::string &str); diff --git a/src/logging.cpp b/src/logging.cpp --- a/src/logging.cpp +++ b/src/logging.cpp @@ -125,6 +125,21 @@ return ret; } +std::vector ListActiveLogCategories() { + std::vector ret; + for (const CLogCategoryDesc &category_desc : LogCategories) { + // Omit the special cases. + if (category_desc.flag != BCLog::NONE && + category_desc.flag != BCLog::ALL) { + CLogCategoryActive catActive; + catActive.category = category_desc.category; + catActive.active = LogAcceptCategory(category_desc.flag); + ret.push_back(catActive); + } + } + return ret; +} + BCLog::Logger::~Logger() { if (m_fileout) { fclose(m_fileout); diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -127,6 +127,8 @@ {"getmempoolancestors", 1, "verbose"}, {"getmempooldescendants", 1, "verbose"}, {"disconnectnode", 1, "nodeid"}, + {"logging", 0, "include"}, + {"logging", 1, "exclude"}, // Echo with conversion (For testing only) {"echojson", 0, "arg0"}, {"echojson", 1, "arg1"}, diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -7,8 +7,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -396,6 +398,93 @@ } } +static void EnableOrDisableLogCategories(UniValue cats, bool enable) { + cats = cats.get_array(); + for (size_t i = 0; i < cats.size(); ++i) { + std::string cat = cats[i].get_str(); + + bool success; + if (enable) { + success = GetLogger().EnableCategory(cat); + } else { + success = GetLogger().DisableCategory(cat); + } + + if (!success) { + throw JSONRPCError(RPC_INVALID_PARAMETER, + "unknown logging category " + cat); + } + } +} + +static UniValue logging(const Config &config, const JSONRPCRequest &request) { + if (request.fHelp || request.params.size() > 2) { + throw std::runtime_error( + "logging [include,...] \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.\n" + "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:\n" + " (string): a list of the logging categories that are " + "active.\n" + "\nExamples:\n" + + HelpExampleCli("logging", "\"[\\\"all\\\"]\" \"[\\\"http\\\"]\"") + + HelpExampleRpc("logging", "[\"all\"], \"[libevent]\"")); + } + + uint32_t original_log_categories = GetLogger().GetCategoryMask(); + if (request.params.size() > 0 && request.params[0].isArray()) { + EnableOrDisableLogCategories(request.params[0], true); + } + + if (request.params.size() > 1 && request.params[1].isArray()) { + EnableOrDisableLogCategories(request.params[1], false); + } + + uint32_t updated_log_categories = GetLogger().GetCategoryMask(); + uint32_t changed_log_categories = + original_log_categories ^ updated_log_categories; + + /** + * 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. + */ + if (changed_log_categories & BCLog::LIBEVENT) { + if (!UpdateHTTPServerLogging( + GetLogger().WillLogCategory(BCLog::LIBEVENT))) { + GetLogger().DisableCategory(BCLog::LIBEVENT); + if (changed_log_categories == 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 vLogCatActive = ListActiveLogCategories(); + for (const auto &logCatActive : vLogCatActive) { + result.pushKV(logCatActive.category, logCatActive.active); + } + + return result; +} + static UniValue echo(const Config &config, const JSONRPCRequest &request) { if (request.fHelp) { throw std::runtime_error( @@ -439,6 +528,7 @@ { "hidden", "echo", echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, { "hidden", "echojson", echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, { "hidden", "getinfo", getinfo_deprecated, {}}, + { "hidden", "logging", logging, {"include", "exclude"}}, }; // clang-format on