diff --git a/doc/developer-notes.md b/doc/developer-notes.md index 8fe61e0f31..1f3aef04a6 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -390,8 +390,10 @@ Run configure with the `--enable-gprof` option, then make. If the code is behaving strangely, take a look in the `debug.log` file in the data directory; error and debugging messages are written there. -The `-debug=...` command-line option controls debugging; running with just `-debug` or `-debug=1` will turn -on all categories (and give you a very large `debug.log` file). +Debug logging can be enabled on startup with the `-debug` and `-loglevel` +configuration options and toggled while dashd is running with the `logging` +RPC. For instance, launching dashd with `-debug` or `-debug=1` will turn on +all log categories and `-loglevel=trace` will turn on all log severity levels. The Qt code routes `qDebug()` output to `debug.log` under category "qt": run with `-debug=qt` to see it. diff --git a/src/init.cpp b/src/init.cpp index f82252d439..002818135b 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1212,6 +1212,7 @@ bool AppInitParameterInteraction(const ArgsManager& args) // ********************************************************* Step 3: parameter-to-internal-flags init::SetLoggingCategories(args); + init::SetLoggingLevel(args); fCheckBlockIndex = args.GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks()); fCheckpointsEnabled = args.GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED); diff --git a/src/init/common.cpp b/src/init/common.cpp index 8be70f7caf..ecf1095b90 100644 --- a/src/init/common.cpp +++ b/src/init/common.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -57,11 +58,12 @@ bool SanityChecks() void AddLoggingArgs(ArgsManager& argsman) { argsman.AddArg("-debuglogfile=", strprintf("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (-nodebuglogfile to disable; default: %s)", DEFAULT_DEBUGLOGFILE), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); - argsman.AddArg("-debug=", "Output debugging information (default: -nodebug, supplying is optional). " - "If is not supplied or if = 1, output all debugging information. can be: " + LogInstance().LogCategoriesString() + ". This option can be specified multiple times to output multiple categories.", + argsman.AddArg("-debug=", "Output debug and trace logging (default: -nodebug, supplying is optional). " + "If is not supplied or if = 1, output all debug and trace logging. can be: " + LogInstance().LogCategoriesString() + ". This option can be specified multiple times to output multiple categories.", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); - argsman.AddArg("-debugexclude=", "Exclude debugging information for a category. Can be used in conjunction with -debug=1 to output debug logs for all categories except the specified category. This option can be specified multiple times to exclude multiple categories.", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); + argsman.AddArg("-debugexclude=", "Exclude debug and trace logging for a category. Can be used in conjunction with -debug=1 to output debug and trace logging for all categories except the specified category. This option can be specified multiple times to exclude multiple categories.", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-logips", strprintf("Include IP addresses in debug output (default: %u)", DEFAULT_LOGIPS), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); + argsman.AddArg("-loglevel=|:", strprintf("Set the global or per-category severity level for logging categories enabled with the -debug configuration option or the logging RPC: %s (default=%s); warning and error levels are always logged. If : is supplied, the setting will override the global one and may be specified multiple times to set multiple category-specific levels. can be: %s.", LogInstance().LogLevelsString(), LogInstance().LogLevelToStr(BCLog::DEFAULT_LOG_LEVEL), LogInstance().LogCategoriesString()), ArgsManager::DISALLOW_NEGATION | ArgsManager::DISALLOW_ELISION | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-logtimestamps", strprintf("Prepend debug output with timestamp (default: %u)", DEFAULT_LOGTIMESTAMPS), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); #ifdef HAVE_THREAD_LOCAL argsman.AddArg("-logthreadnames", strprintf("Prepend debug output with name of the originating thread (only available on platforms supporting thread_local) (default: %u)", DEFAULT_LOGTHREADNAMES), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); @@ -89,6 +91,26 @@ void SetLoggingOptions(const ArgsManager& args) fLogIPs = args.GetBoolArg("-logips", DEFAULT_LOGIPS); } +void SetLoggingLevel(const ArgsManager& args) +{ + if (args.IsArgSet("-loglevel")) { + for (const std::string& level_str : args.GetArgs("-loglevel")) { + if (level_str.find_first_of(':', 3) == std::string::npos) { + // user passed a global log level, i.e. -loglevel= + if (!LogInstance().SetLogLevel(level_str)) { + InitWarning(strprintf(_("Unsupported global logging level -loglevel=%s. Valid values: %s."), level_str, LogInstance().LogLevelsString())); + } + } else { + // user passed a category-specific log level, i.e. -loglevel=: + const auto& toks = SplitString(level_str, ':'); + if (!(toks.size() == 2 && LogInstance().SetCategoryLogLevel(toks[0], toks[1]))) { + InitWarning(strprintf(_("Unsupported category-specific logging level -loglevel=%s. Expected -loglevel=:. Valid categories: %s. Valid loglevels: %s."), level_str, LogInstance().LogCategoriesString(), LogInstance().LogLevelsString())); + } + } + } + } +} + void SetLoggingCategories(const ArgsManager& args) { if (args.IsArgSet("-debug")) { diff --git a/src/init/common.h b/src/init/common.h index fc4bc1b280..bcd46f41d3 100644 --- a/src/init/common.h +++ b/src/init/common.h @@ -21,6 +21,7 @@ bool SanityChecks(); void AddLoggingArgs(ArgsManager& args); void SetLoggingOptions(const ArgsManager& args); void SetLoggingCategories(const ArgsManager& args); +void SetLoggingLevel(const ArgsManager& args); bool StartLogging(const ArgsManager& args); void LogPackageVersion(); } // namespace init diff --git a/src/logging.cpp b/src/logging.cpp index 68516b20f9..ab59bd5599 100644 --- a/src/logging.cpp +++ b/src/logging.cpp @@ -5,15 +5,17 @@ #include #include +#include #include #include -#include #include #include #include +#include const char * const DEFAULT_DEBUGLOGFILE = "debug.log"; +constexpr auto MAX_USER_SETABLE_SEVERITY_LEVEL{BCLog::Level::Info}; BCLog::Logger& LogInstance() { @@ -122,6 +124,19 @@ bool BCLog::Logger::WillLogCategory(BCLog::LogFlags category) const return (m_categories.load(std::memory_order_relaxed) & category) != 0; } +bool BCLog::Logger::WillLogCategoryLevel(BCLog::LogFlags category, BCLog::Level level) const +{ + // Log messages at Warning and Error level unconditionally, so that + // important troubleshooting information doesn't get lost. + if (level >= BCLog::Level::Warning) return true; + + if (!WillLogCategory(category)) return false; + + StdLockGuard scoped_lock(m_cs); + const auto it{m_category_log_levels.find(category)}; + return level >= (it == m_category_log_levels.end() ? LogLevel() : it->second); +} + bool BCLog::Logger::DefaultShrinkDebugFile() const { return m_categories == BCLog::NONE; @@ -135,7 +150,7 @@ struct CLogCategoryDesc { const CLogCategoryDesc LogCategories[] = { {BCLog::NONE, "0"}, - {BCLog::NONE, "none"}, + {BCLog::NONE, ""}, {BCLog::NET, "net"}, {BCLog::TOR, "tor"}, {BCLog::MEMPOOL, "mempool"}, @@ -201,11 +216,11 @@ bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str) return false; } -std::string LogLevelToStr(BCLog::Level level) +std::string BCLog::Logger::LogLevelToStr(BCLog::Level level) const { switch (level) { - case BCLog::Level::None: - return "none"; + case BCLog::Level::Trace: + return "trace"; case BCLog::Level::Debug: return "debug"; case BCLog::Level::Info: @@ -214,6 +229,8 @@ std::string LogLevelToStr(BCLog::Level level) return "warning"; case BCLog::Level::Error: return "error"; + case BCLog::Level::None: + return ""; } assert(false); } @@ -223,7 +240,7 @@ std::string LogCategoryToStr(BCLog::LogFlags category) // Each log category string representation should sync with LogCategories switch (category) { case BCLog::LogFlags::NONE: - return "none"; + return ""; case BCLog::LogFlags::NET: return "net"; case BCLog::LogFlags::TOR: @@ -318,6 +335,25 @@ std::string LogCategoryToStr(BCLog::LogFlags category) assert(false); } +static std::optional GetLogLevel(const std::string& level_str) +{ + if (level_str == "trace") { + return BCLog::Level::Trace; + } else if (level_str == "debug") { + return BCLog::Level::Debug; + } else if (level_str == "info") { + return BCLog::Level::Info; + } else if (level_str == "warning") { + return BCLog::Level::Warning; + } else if (level_str == "error") { + return BCLog::Level::Error; + } else if (level_str == "none") { + return BCLog::Level::None; + } else { + return std::nullopt; + } +} + std::vector BCLog::Logger::LogCategoriesList(bool enabled_only) const { // Sort log categories by alphabetical order. @@ -338,6 +374,18 @@ std::vector BCLog::Logger::LogCategoriesList(bool enabled_only) con return ret; } +/** Log severity levels that can be selected by the user. */ +static constexpr std::array LogLevelsList() +{ + return {BCLog::Level::Info, BCLog::Level::Debug, BCLog::Level::Trace}; +} + +std::string BCLog::Logger::LogLevelsString() const +{ + const auto& levels = LogLevelsList(); + return Join(std::vector{levels.begin(), levels.end()}, ", ", [this](BCLog::Level level) { return LogLevelToStr(level); }); +} + std::string BCLog::Logger::LogTimestampStr(const std::string& str) { std::string strStamped; @@ -385,7 +433,7 @@ namespace BCLog { } } // namespace BCLog -void BCLog::Logger::LogPrintStr(const std::string& str, const std::string& logging_function, const std::string& source_file, const int source_line, const BCLog::LogFlags category, const BCLog::Level level) +void BCLog::Logger::LogPrintStr(const std::string& str, const std::string& logging_function, const std::string& source_file, int source_line, BCLog::LogFlags category, BCLog::Level level) { StdLockGuard scoped_lock(m_cs); std::string str_prefixed = LogEscapeMessage(str); @@ -494,3 +542,24 @@ void BCLog::Logger::ShrinkDebugFile() else if (file != nullptr) fclose(file); } + +bool BCLog::Logger::SetLogLevel(const std::string& level_str) +{ + const auto level = GetLogLevel(level_str); + if (!level.has_value() || level.value() > MAX_USER_SETABLE_SEVERITY_LEVEL) return false; + m_log_level = level.value(); + return true; +} + +bool BCLog::Logger::SetCategoryLogLevel(const std::string& category_str, const std::string& level_str) +{ + BCLog::LogFlags flag; + if (!GetLogCategory(flag, category_str)) return false; + + const auto level = GetLogLevel(level_str); + if (!level.has_value() || level.value() > MAX_USER_SETABLE_SEVERITY_LEVEL) return false; + + StdLockGuard scoped_lock(m_cs); + m_category_log_levels[flag] = level.value(); + return true; +} diff --git a/src/logging.h b/src/logging.h index 69d07b5e09..a8d5453047 100644 --- a/src/logging.h +++ b/src/logging.h @@ -7,8 +7,8 @@ #define BITCOIN_LOGGING_H #include -#include #include +#include #include #include @@ -17,6 +17,7 @@ #include #include #include +#include #include static const bool DEFAULT_LOGTIMEMICROS = false; @@ -92,12 +93,14 @@ namespace BCLog { ALL = ~(uint64_t)0, }; enum class Level { - Debug = 0, - None = 1, - Info = 2, - Warning = 3, - Error = 4, + Trace = 0, // High-volume or detailed logging for development/debugging + Debug, // Reasonably noisy logging, but still usable in production + Info, // Default + Warning, + Error, + None, // Internal use only }; + constexpr auto DEFAULT_LOG_LEVEL{Level::Debug}; class Logger { @@ -115,6 +118,13 @@ namespace BCLog { */ std::atomic_bool m_started_new_line{true}; + //! Category-specific log level. Overrides `m_log_level`. + std::unordered_map m_category_log_levels GUARDED_BY(m_cs); + + //! If there is no category-specific log level, all logs with a severity + //! level lower than `m_log_level` will be ignored. + std::atomic m_log_level{DEFAULT_LOG_LEVEL}; + /** Log categories bitfield. */ std::atomic m_categories{0}; @@ -137,7 +147,7 @@ namespace BCLog { std::atomic m_reopen_file{false}; /** Send a string to the log output */ - void LogPrintStr(const std::string& str, const std::string& logging_function, const std::string& source_file, const int source_line, const BCLog::LogFlags category, const BCLog::Level level); + void LogPrintStr(const std::string& str, const std::string& logging_function, const std::string& source_file, int source_line, BCLog::LogFlags category, BCLog::Level level); /** Returns whether logs will be written to any output */ bool Enabled() const @@ -168,6 +178,22 @@ namespace BCLog { void ShrinkDebugFile(); + std::unordered_map CategoryLevels() const + { + StdLockGuard scoped_lock(m_cs); + return m_category_log_levels; + } + void SetCategoryLogLevel(const std::unordered_map& levels) + { + StdLockGuard scoped_lock(m_cs); + m_category_log_levels = levels; + } + bool SetCategoryLogLevel(const std::string& category_str, const std::string& level_str); + + Level LogLevel() const { return m_log_level.load(); } + void SetLogLevel(Level level) { m_log_level = level; } + bool SetLogLevel(const std::string& level); + uint64_t GetCategoryMask() const { return m_categories.load(); } void EnableCategory(LogFlags flag); @@ -176,6 +202,8 @@ namespace BCLog { bool DisableCategory(const std::string& str); bool WillLogCategory(LogFlags category) const; + bool WillLogCategoryLevel(LogFlags category, Level level) const; + /** Returns a vector of the log categories in alphabetical order. */ std::vector LogCategoriesList(bool enabled_only = false) const; /** Returns a string with the log categories in alphabetical order. */ @@ -184,6 +212,12 @@ namespace BCLog { return Join(LogCategoriesList(enabled_only), ", ", [&](const LogCategory& i) { return i.category; }); }; + //! Returns a string with all user-selectable log levels. + std::string LogLevelsString() const; + + //! Returns the string representation of a log level. + std::string LogLevelToStr(BCLog::Level level) const; + bool DefaultShrinkDebugFile() const; }; @@ -194,12 +228,7 @@ BCLog::Logger& LogInstance(); /** Return true if log accepts specified category, at the specified level. */ static inline bool LogAcceptCategory(BCLog::LogFlags category, BCLog::Level level) { - // Log messages at Warning and Error level unconditionally, so that - // important troubleshooting information doesn't get lost. - if (level >= BCLog::Level::Warning) { - return true; - } - return LogInstance().WillLogCategory(category); + return LogInstance().WillLogCategoryLevel(category, level); } /** Return true if str parses as a log category and set the flag */ diff --git a/src/test/i2p_tests.cpp b/src/test/i2p_tests.cpp index 79b34931d3..95a0fb6a04 100644 --- a/src/test/i2p_tests.cpp +++ b/src/test/i2p_tests.cpp @@ -3,6 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include #include #include #include @@ -20,6 +21,8 @@ BOOST_FIXTURE_TEST_SUITE(i2p_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(unlimited_recv) { + const auto prev_log_level{LogInstance().LogLevel()}; + LogInstance().SetLogLevel(BCLog::Level::Trace); auto CreateSockOrig = CreateSock; // Mock CreateSock() to create MockSock. @@ -40,6 +43,7 @@ BOOST_AUTO_TEST_CASE(unlimited_recv) } CreateSock = CreateSockOrig; + LogInstance().SetLogLevel(prev_log_level); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/logging_tests.cpp b/src/test/logging_tests.cpp index 1f86cc33fd..69257af721 100644 --- a/src/test/logging_tests.cpp +++ b/src/test/logging_tests.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include #include #include #include @@ -10,6 +11,7 @@ #include #include #include +#include #include #include @@ -17,6 +19,12 @@ BOOST_FIXTURE_TEST_SUITE(logging_tests, BasicTestingSetup) +static void ResetLogger() +{ + LogInstance().SetLogLevel(BCLog::DEFAULT_LOG_LEVEL); + LogInstance().SetCategoryLogLevel({}); +} + struct LogSetup : public BasicTestingSetup { fs::path prev_log_path; fs::path tmp_log_path; @@ -25,6 +33,8 @@ struct LogSetup : public BasicTestingSetup { bool prev_log_timestamps; bool prev_log_threadnames; bool prev_log_sourcelocations; + std::unordered_map prev_category_levels; + BCLog::Level prev_log_level; LogSetup() : prev_log_path{LogInstance().m_file_path}, tmp_log_path{m_args.GetDataDirBase() / "tmp_debug.log"}, @@ -32,14 +42,21 @@ struct LogSetup : public BasicTestingSetup { prev_print_to_file{LogInstance().m_print_to_file}, prev_log_timestamps{LogInstance().m_log_timestamps}, prev_log_threadnames{LogInstance().m_log_threadnames}, - prev_log_sourcelocations{LogInstance().m_log_sourcelocations} + prev_log_sourcelocations{LogInstance().m_log_sourcelocations}, + prev_category_levels{LogInstance().CategoryLevels()}, + prev_log_level{LogInstance().LogLevel()} { LogInstance().m_file_path = tmp_log_path; LogInstance().m_reopen_file = true; LogInstance().m_print_to_file = true; LogInstance().m_log_timestamps = false; LogInstance().m_log_threadnames = false; - LogInstance().m_log_sourcelocations = true; + + // Prevent tests from failing when the line number of the logs changes. + LogInstance().m_log_sourcelocations = false; + + LogInstance().SetLogLevel(BCLog::Level::Debug); + LogInstance().SetCategoryLogLevel({}); } ~LogSetup() @@ -51,6 +68,8 @@ struct LogSetup : public BasicTestingSetup { LogInstance().m_log_timestamps = prev_log_timestamps; LogInstance().m_log_threadnames = prev_log_threadnames; LogInstance().m_log_sourcelocations = prev_log_sourcelocations; + LogInstance().SetLogLevel(prev_log_level); + LogInstance().SetCategoryLogLevel(prev_category_levels); } }; @@ -74,6 +93,7 @@ BOOST_AUTO_TEST_CASE(logging_timer) BOOST_FIXTURE_TEST_CASE(logging_LogPrintf_, LogSetup) { + LogInstance().m_log_sourcelocations = true; LogPrintf_("fn1", "src1", 1, BCLog::LogFlags::NET, BCLog::Level::Debug, "foo1: %s", "bar1\n"); LogPrintf_("fn2", "src2", 2, BCLog::LogFlags::NET, BCLog::Level::None, "foo2: %s", "bar2\n"); LogPrintf_("fn3", "src3", 3, BCLog::LogFlags::NONE, BCLog::Level::Debug, "foo3: %s", "bar3\n"); @@ -94,9 +114,6 @@ BOOST_FIXTURE_TEST_CASE(logging_LogPrintf_, LogSetup) BOOST_FIXTURE_TEST_CASE(logging_LogPrintMacros, LogSetup) { - // Prevent tests from failing when the line number of the following log calls changes. - LogInstance().m_log_sourcelocations = false; - LogPrintf("foo5: %s\n", "bar5"); LogPrint(BCLog::NET, "foo6: %s\n", "bar6"); LogPrintLevel(BCLog::NET, BCLog::Level::Debug, "foo7: %s\n", "bar7"); @@ -120,16 +137,14 @@ BOOST_FIXTURE_TEST_CASE(logging_LogPrintMacros, LogSetup) BOOST_FIXTURE_TEST_CASE(logging_LogPrintMacros_CategoryName, LogSetup) { - // Prevent tests from failing when the line number of the following log calls changes. - LogInstance().m_log_sourcelocations = false; LogInstance().EnableCategory(BCLog::LogFlags::ALL); - const auto concated_categery_names = LogInstance().LogCategoriesString(); + const auto concatenated_category_names = LogInstance().LogCategoriesString(); std::vector> expected_category_names; - const auto category_names = SplitString(concated_categery_names, ','); + const auto category_names = SplitString(concatenated_category_names, ','); for (const auto& category_name : category_names) { - BCLog::LogFlags category = BCLog::NONE; + BCLog::LogFlags category; const auto trimmed_category_name = TrimString(category_name); - BOOST_TEST(GetLogCategory(category, trimmed_category_name)); + BOOST_REQUIRE(GetLogCategory(category, trimmed_category_name)); expected_category_names.emplace_back(category, trimmed_category_name); } @@ -150,4 +165,92 @@ BOOST_FIXTURE_TEST_CASE(logging_LogPrintMacros_CategoryName, LogSetup) BOOST_CHECK_EQUAL_COLLECTIONS(log_lines.begin(), log_lines.end(), expected.begin(), expected.end()); } +BOOST_FIXTURE_TEST_CASE(logging_SeverityLevels, LogSetup) +{ + LogInstance().EnableCategory(BCLog::LogFlags::ALL); + + LogInstance().SetLogLevel(BCLog::Level::Debug); + LogInstance().SetCategoryLogLevel(/*category_str=*/"net", /*level_str=*/"info"); + + // Global log level + LogPrintLevel(BCLog::HTTP, BCLog::Level::Info, "foo1: %s\n", "bar1"); + LogPrintLevel(BCLog::MEMPOOL, BCLog::Level::Trace, "foo2: %s. This log level is lower than the global one.\n", "bar2"); + LogPrintLevel(BCLog::VALIDATION, BCLog::Level::Warning, "foo3: %s\n", "bar3"); + LogPrintLevel(BCLog::RPC, BCLog::Level::Error, "foo4: %s\n", "bar4"); + + // Category-specific log level + LogPrintLevel(BCLog::NET, BCLog::Level::Warning, "foo5: %s\n", "bar5"); + LogPrintLevel(BCLog::NET, BCLog::Level::Debug, "foo6: %s. This log level is the same as the global one but lower than the category-specific one, which takes precedence. \n", "bar6"); + LogPrintLevel(BCLog::NET, BCLog::Level::Error, "foo7: %s\n", "bar7"); + + std::vector expected = { + "[http:info] foo1: bar1", + "[validation:warning] foo3: bar3", + "[rpc:error] foo4: bar4", + "[net:warning] foo5: bar5", + "[net:error] foo7: bar7", + }; + std::ifstream file{tmp_log_path}; + std::vector log_lines; + for (std::string log; std::getline(file, log);) { + log_lines.push_back(log); + } + BOOST_CHECK_EQUAL_COLLECTIONS(log_lines.begin(), log_lines.end(), expected.begin(), expected.end()); +} + +BOOST_FIXTURE_TEST_CASE(logging_Conf, LogSetup) +{ + // Set global log level + { + ResetLogger(); + ArgsManager args; + args.AddArg("-loglevel", "...", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); + const char* argv_test[] = {"dashd", "-loglevel=debug"}; + std::string err; + BOOST_REQUIRE(args.ParseParameters(2, argv_test, err)); + init::SetLoggingLevel(args); + BOOST_CHECK_EQUAL(LogInstance().LogLevel(), BCLog::Level::Debug); + } + + // Set category-specific log level + { + ResetLogger(); + ArgsManager args; + args.AddArg("-loglevel", "...", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); + const char* argv_test[] = {"dashd", "-loglevel=net:trace"}; + std::string err; + BOOST_REQUIRE(args.ParseParameters(2, argv_test, err)); + init::SetLoggingLevel(args); + BOOST_CHECK_EQUAL(LogInstance().LogLevel(), BCLog::DEFAULT_LOG_LEVEL); + + const auto& category_levels{LogInstance().CategoryLevels()}; + const auto net_it{category_levels.find(BCLog::LogFlags::NET)}; + BOOST_REQUIRE(net_it != category_levels.end()); + BOOST_CHECK_EQUAL(net_it->second, BCLog::Level::Trace); + } + + // Set both global log level and category-specific log level + { + ResetLogger(); + ArgsManager args; + args.AddArg("-loglevel", "...", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); + const char* argv_test[] = {"dashd", "-loglevel=debug", "-loglevel=net:trace", "-loglevel=http:info"}; + std::string err; + BOOST_REQUIRE(args.ParseParameters(4, argv_test, err)); + init::SetLoggingLevel(args); + BOOST_CHECK_EQUAL(LogInstance().LogLevel(), BCLog::Level::Debug); + + const auto& category_levels{LogInstance().CategoryLevels()}; + BOOST_CHECK_EQUAL(category_levels.size(), 2); + + const auto net_it{category_levels.find(BCLog::LogFlags::NET)}; + BOOST_CHECK(net_it != category_levels.end()); + BOOST_CHECK_EQUAL(net_it->second, BCLog::Level::Trace); + + const auto http_it{category_levels.find(BCLog::LogFlags::HTTP)}; + BOOST_CHECK(http_it != category_levels.end()); + BOOST_CHECK_EQUAL(http_it->second, BCLog::Level::Info); + } +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 487bb1bb56..25b355ef71 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -149,6 +149,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve "-logsourcelocations", "-logtimemicros", "-logthreadnames", + "-loglevel=trace", "-debug", "-debugexclude=libevent", "-debugexclude=leveldb", diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 0f2bd413eb..6863e72b40 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -125,6 +125,8 @@ class TestNode(): self.args.append("-logthreadnames") if self.version_is_at_least(21000000): self.args.append("-logsourcelocations") + if self.version_is_at_least(22010000): + self.args.append("-loglevel=trace") # Default behavior from global -v2transport flag is added to args to persist it over restarts. # May be overwritten in individual tests, using extra_args.