merge bitcoin#25614: Severity-based logging, step 2

This commit is contained in:
Kittywhiskers Van Gogh 2024-11-20 16:28:40 +00:00
parent 21470fdeb3
commit 52a1263989
No known key found for this signature in database
GPG Key ID: 30CD0C065E5C4AAD
10 changed files with 270 additions and 36 deletions

View File

@ -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; 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. error and debugging messages are written there.
The `-debug=...` command-line option controls debugging; running with just `-debug` or `-debug=1` will turn Debug logging can be enabled on startup with the `-debug` and `-loglevel`
on all categories (and give you a very large `debug.log` file). 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` The Qt code routes `qDebug()` output to `debug.log` under category "qt": run with `-debug=qt`
to see it. to see it.

View File

@ -1212,6 +1212,7 @@ bool AppInitParameterInteraction(const ArgsManager& args)
// ********************************************************* Step 3: parameter-to-internal-flags // ********************************************************* Step 3: parameter-to-internal-flags
init::SetLoggingCategories(args); init::SetLoggingCategories(args);
init::SetLoggingLevel(args);
fCheckBlockIndex = args.GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks()); fCheckBlockIndex = args.GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks());
fCheckpointsEnabled = args.GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED); fCheckpointsEnabled = args.GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED);

View File

@ -13,6 +13,7 @@
#include <logging.h> #include <logging.h>
#include <node/ui_interface.h> #include <node/ui_interface.h>
#include <random.h> #include <random.h>
#include <util/string.h>
#include <util/system.h> #include <util/system.h>
#include <util/time.h> #include <util/time.h>
#include <util/translation.h> #include <util/translation.h>
@ -57,11 +58,12 @@ bool SanityChecks()
void AddLoggingArgs(ArgsManager& argsman) void AddLoggingArgs(ArgsManager& argsman)
{ {
argsman.AddArg("-debuglogfile=<file>", 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("-debuglogfile=<file>", 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=<category>", "Output debugging information (default: -nodebug, supplying <category> is optional). " argsman.AddArg("-debug=<category>", "Output debug and trace logging (default: -nodebug, supplying <category> is optional). "
"If <category> is not supplied or if <category> = 1, output all debugging information. <category> can be: " + LogInstance().LogCategoriesString() + ". This option can be specified multiple times to output multiple categories.", "If <category> is not supplied or if <category> = 1, output all debug and trace logging. <category> can be: " + LogInstance().LogCategoriesString() + ". This option can be specified multiple times to output multiple categories.",
ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-debugexclude=<category>", "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=<category>", "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("-logips", strprintf("Include IP addresses in debug output (default: %u)", DEFAULT_LOGIPS), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-loglevel=<level>|<category>:<level>", 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 <category>:<level> is supplied, the setting will override the global one and may be specified multiple times to set multiple category-specific levels. <category> 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); argsman.AddArg("-logtimestamps", strprintf("Prepend debug output with timestamp (default: %u)", DEFAULT_LOGTIMESTAMPS), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
#ifdef HAVE_THREAD_LOCAL #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); 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); 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=<level>
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=<category>:<level>
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=<category>:<loglevel>. Valid categories: %s. Valid loglevels: %s."), level_str, LogInstance().LogCategoriesString(), LogInstance().LogLevelsString()));
}
}
}
}
}
void SetLoggingCategories(const ArgsManager& args) void SetLoggingCategories(const ArgsManager& args)
{ {
if (args.IsArgSet("-debug")) { if (args.IsArgSet("-debug")) {

View File

@ -21,6 +21,7 @@ bool SanityChecks();
void AddLoggingArgs(ArgsManager& args); void AddLoggingArgs(ArgsManager& args);
void SetLoggingOptions(const ArgsManager& args); void SetLoggingOptions(const ArgsManager& args);
void SetLoggingCategories(const ArgsManager& args); void SetLoggingCategories(const ArgsManager& args);
void SetLoggingLevel(const ArgsManager& args);
bool StartLogging(const ArgsManager& args); bool StartLogging(const ArgsManager& args);
void LogPackageVersion(); void LogPackageVersion();
} // namespace init } // namespace init

View File

@ -5,15 +5,17 @@
#include <fs.h> #include <fs.h>
#include <logging.h> #include <logging.h>
#include <util/string.h>
#include <util/system.h> #include <util/system.h>
#include <util/threadnames.h> #include <util/threadnames.h>
#include <util/string.h>
#include <util/time.h> #include <util/time.h>
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <optional>
const char * const DEFAULT_DEBUGLOGFILE = "debug.log"; const char * const DEFAULT_DEBUGLOGFILE = "debug.log";
constexpr auto MAX_USER_SETABLE_SEVERITY_LEVEL{BCLog::Level::Info};
BCLog::Logger& LogInstance() 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; 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 bool BCLog::Logger::DefaultShrinkDebugFile() const
{ {
return m_categories == BCLog::NONE; return m_categories == BCLog::NONE;
@ -135,7 +150,7 @@ struct CLogCategoryDesc {
const CLogCategoryDesc LogCategories[] = const CLogCategoryDesc LogCategories[] =
{ {
{BCLog::NONE, "0"}, {BCLog::NONE, "0"},
{BCLog::NONE, "none"}, {BCLog::NONE, ""},
{BCLog::NET, "net"}, {BCLog::NET, "net"},
{BCLog::TOR, "tor"}, {BCLog::TOR, "tor"},
{BCLog::MEMPOOL, "mempool"}, {BCLog::MEMPOOL, "mempool"},
@ -201,11 +216,11 @@ bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str)
return false; return false;
} }
std::string LogLevelToStr(BCLog::Level level) std::string BCLog::Logger::LogLevelToStr(BCLog::Level level) const
{ {
switch (level) { switch (level) {
case BCLog::Level::None: case BCLog::Level::Trace:
return "none"; return "trace";
case BCLog::Level::Debug: case BCLog::Level::Debug:
return "debug"; return "debug";
case BCLog::Level::Info: case BCLog::Level::Info:
@ -214,6 +229,8 @@ std::string LogLevelToStr(BCLog::Level level)
return "warning"; return "warning";
case BCLog::Level::Error: case BCLog::Level::Error:
return "error"; return "error";
case BCLog::Level::None:
return "";
} }
assert(false); assert(false);
} }
@ -223,7 +240,7 @@ std::string LogCategoryToStr(BCLog::LogFlags category)
// Each log category string representation should sync with LogCategories // Each log category string representation should sync with LogCategories
switch (category) { switch (category) {
case BCLog::LogFlags::NONE: case BCLog::LogFlags::NONE:
return "none"; return "";
case BCLog::LogFlags::NET: case BCLog::LogFlags::NET:
return "net"; return "net";
case BCLog::LogFlags::TOR: case BCLog::LogFlags::TOR:
@ -318,6 +335,25 @@ std::string LogCategoryToStr(BCLog::LogFlags category)
assert(false); assert(false);
} }
static std::optional<BCLog::Level> 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<LogCategory> BCLog::Logger::LogCategoriesList(bool enabled_only) const std::vector<LogCategory> BCLog::Logger::LogCategoriesList(bool enabled_only) const
{ {
// Sort log categories by alphabetical order. // Sort log categories by alphabetical order.
@ -338,6 +374,18 @@ std::vector<LogCategory> BCLog::Logger::LogCategoriesList(bool enabled_only) con
return ret; return ret;
} }
/** Log severity levels that can be selected by the user. */
static constexpr std::array<BCLog::Level, 3> 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<BCLog::Level>{levels.begin(), levels.end()}, ", ", [this](BCLog::Level level) { return LogLevelToStr(level); });
}
std::string BCLog::Logger::LogTimestampStr(const std::string& str) std::string BCLog::Logger::LogTimestampStr(const std::string& str)
{ {
std::string strStamped; std::string strStamped;
@ -385,7 +433,7 @@ namespace BCLog {
} }
} // 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); StdLockGuard scoped_lock(m_cs);
std::string str_prefixed = LogEscapeMessage(str); std::string str_prefixed = LogEscapeMessage(str);
@ -494,3 +542,24 @@ void BCLog::Logger::ShrinkDebugFile()
else if (file != nullptr) else if (file != nullptr)
fclose(file); 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;
}

View File

@ -7,8 +7,8 @@
#define BITCOIN_LOGGING_H #define BITCOIN_LOGGING_H
#include <fs.h> #include <fs.h>
#include <tinyformat.h>
#include <threadsafety.h> #include <threadsafety.h>
#include <tinyformat.h>
#include <util/string.h> #include <util/string.h>
#include <atomic> #include <atomic>
@ -17,6 +17,7 @@
#include <list> #include <list>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <unordered_map>
#include <vector> #include <vector>
static const bool DEFAULT_LOGTIMEMICROS = false; static const bool DEFAULT_LOGTIMEMICROS = false;
@ -92,12 +93,14 @@ namespace BCLog {
ALL = ~(uint64_t)0, ALL = ~(uint64_t)0,
}; };
enum class Level { enum class Level {
Debug = 0, Trace = 0, // High-volume or detailed logging for development/debugging
None = 1, Debug, // Reasonably noisy logging, but still usable in production
Info = 2, Info, // Default
Warning = 3, Warning,
Error = 4, Error,
None, // Internal use only
}; };
constexpr auto DEFAULT_LOG_LEVEL{Level::Debug};
class Logger class Logger
{ {
@ -115,6 +118,13 @@ namespace BCLog {
*/ */
std::atomic_bool m_started_new_line{true}; std::atomic_bool m_started_new_line{true};
//! Category-specific log level. Overrides `m_log_level`.
std::unordered_map<LogFlags, Level> 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<Level> m_log_level{DEFAULT_LOG_LEVEL};
/** Log categories bitfield. */ /** Log categories bitfield. */
std::atomic<uint64_t> m_categories{0}; std::atomic<uint64_t> m_categories{0};
@ -137,7 +147,7 @@ namespace BCLog {
std::atomic<bool> m_reopen_file{false}; std::atomic<bool> m_reopen_file{false};
/** Send a string to the log output */ /** 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 */ /** Returns whether logs will be written to any output */
bool Enabled() const bool Enabled() const
@ -168,6 +178,22 @@ namespace BCLog {
void ShrinkDebugFile(); void ShrinkDebugFile();
std::unordered_map<LogFlags, Level> CategoryLevels() const
{
StdLockGuard scoped_lock(m_cs);
return m_category_log_levels;
}
void SetCategoryLogLevel(const std::unordered_map<LogFlags, Level>& 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(); } uint64_t GetCategoryMask() const { return m_categories.load(); }
void EnableCategory(LogFlags flag); void EnableCategory(LogFlags flag);
@ -176,6 +202,8 @@ namespace BCLog {
bool DisableCategory(const std::string& str); bool DisableCategory(const std::string& str);
bool WillLogCategory(LogFlags category) const; bool WillLogCategory(LogFlags category) const;
bool WillLogCategoryLevel(LogFlags category, Level level) const;
/** Returns a vector of the log categories in alphabetical order. */ /** Returns a vector of the log categories in alphabetical order. */
std::vector<LogCategory> LogCategoriesList(bool enabled_only = false) const; std::vector<LogCategory> LogCategoriesList(bool enabled_only = false) const;
/** Returns a string with the log categories in alphabetical order. */ /** 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; }); 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; bool DefaultShrinkDebugFile() const;
}; };
@ -194,12 +228,7 @@ BCLog::Logger& LogInstance();
/** Return true if log accepts specified category, at the specified level. */ /** Return true if log accepts specified category, at the specified level. */
static inline bool LogAcceptCategory(BCLog::LogFlags category, BCLog::Level level) static inline bool LogAcceptCategory(BCLog::LogFlags category, BCLog::Level level)
{ {
// Log messages at Warning and Error level unconditionally, so that return LogInstance().WillLogCategoryLevel(category, level);
// important troubleshooting information doesn't get lost.
if (level >= BCLog::Level::Warning) {
return true;
}
return LogInstance().WillLogCategory(category);
} }
/** Return true if str parses as a log category and set the flag */ /** Return true if str parses as a log category and set the flag */

View File

@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <i2p.h> #include <i2p.h>
#include <logging.h>
#include <netaddress.h> #include <netaddress.h>
#include <netbase.h> #include <netbase.h>
#include <test/util/logging.h> #include <test/util/logging.h>
@ -20,6 +21,8 @@ BOOST_FIXTURE_TEST_SUITE(i2p_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(unlimited_recv) BOOST_AUTO_TEST_CASE(unlimited_recv)
{ {
const auto prev_log_level{LogInstance().LogLevel()};
LogInstance().SetLogLevel(BCLog::Level::Trace);
auto CreateSockOrig = CreateSock; auto CreateSockOrig = CreateSock;
// Mock CreateSock() to create MockSock. // Mock CreateSock() to create MockSock.
@ -40,6 +43,7 @@ BOOST_AUTO_TEST_CASE(unlimited_recv)
} }
CreateSock = CreateSockOrig; CreateSock = CreateSockOrig;
LogInstance().SetLogLevel(prev_log_level);
} }
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()

View File

@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying // Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <init/common.h>
#include <logging.h> #include <logging.h>
#include <logging/timer.h> #include <logging/timer.h>
#include <test/util/setup_common.h> #include <test/util/setup_common.h>
@ -10,6 +11,7 @@
#include <chrono> #include <chrono>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <unordered_map>
#include <utility> #include <utility>
#include <vector> #include <vector>
@ -17,6 +19,12 @@
BOOST_FIXTURE_TEST_SUITE(logging_tests, BasicTestingSetup) BOOST_FIXTURE_TEST_SUITE(logging_tests, BasicTestingSetup)
static void ResetLogger()
{
LogInstance().SetLogLevel(BCLog::DEFAULT_LOG_LEVEL);
LogInstance().SetCategoryLogLevel({});
}
struct LogSetup : public BasicTestingSetup { struct LogSetup : public BasicTestingSetup {
fs::path prev_log_path; fs::path prev_log_path;
fs::path tmp_log_path; fs::path tmp_log_path;
@ -25,6 +33,8 @@ struct LogSetup : public BasicTestingSetup {
bool prev_log_timestamps; bool prev_log_timestamps;
bool prev_log_threadnames; bool prev_log_threadnames;
bool prev_log_sourcelocations; bool prev_log_sourcelocations;
std::unordered_map<BCLog::LogFlags, BCLog::Level> prev_category_levels;
BCLog::Level prev_log_level;
LogSetup() : prev_log_path{LogInstance().m_file_path}, LogSetup() : prev_log_path{LogInstance().m_file_path},
tmp_log_path{m_args.GetDataDirBase() / "tmp_debug.log"}, 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_print_to_file{LogInstance().m_print_to_file},
prev_log_timestamps{LogInstance().m_log_timestamps}, prev_log_timestamps{LogInstance().m_log_timestamps},
prev_log_threadnames{LogInstance().m_log_threadnames}, 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_file_path = tmp_log_path;
LogInstance().m_reopen_file = true; LogInstance().m_reopen_file = true;
LogInstance().m_print_to_file = true; LogInstance().m_print_to_file = true;
LogInstance().m_log_timestamps = false; LogInstance().m_log_timestamps = false;
LogInstance().m_log_threadnames = 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() ~LogSetup()
@ -51,6 +68,8 @@ struct LogSetup : public BasicTestingSetup {
LogInstance().m_log_timestamps = prev_log_timestamps; LogInstance().m_log_timestamps = prev_log_timestamps;
LogInstance().m_log_threadnames = prev_log_threadnames; LogInstance().m_log_threadnames = prev_log_threadnames;
LogInstance().m_log_sourcelocations = prev_log_sourcelocations; 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) 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_("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_("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"); 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) 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"); LogPrintf("foo5: %s\n", "bar5");
LogPrint(BCLog::NET, "foo6: %s\n", "bar6"); LogPrint(BCLog::NET, "foo6: %s\n", "bar6");
LogPrintLevel(BCLog::NET, BCLog::Level::Debug, "foo7: %s\n", "bar7"); 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) 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); LogInstance().EnableCategory(BCLog::LogFlags::ALL);
const auto concated_categery_names = LogInstance().LogCategoriesString(); const auto concatenated_category_names = LogInstance().LogCategoriesString();
std::vector<std::pair<BCLog::LogFlags, std::string>> expected_category_names; std::vector<std::pair<BCLog::LogFlags, std::string>> 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) { for (const auto& category_name : category_names) {
BCLog::LogFlags category = BCLog::NONE; BCLog::LogFlags category;
const auto trimmed_category_name = TrimString(category_name); 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); 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_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<std::string> 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<std::string> 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() BOOST_AUTO_TEST_SUITE_END()

View File

@ -149,6 +149,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve
"-logsourcelocations", "-logsourcelocations",
"-logtimemicros", "-logtimemicros",
"-logthreadnames", "-logthreadnames",
"-loglevel=trace",
"-debug", "-debug",
"-debugexclude=libevent", "-debugexclude=libevent",
"-debugexclude=leveldb", "-debugexclude=leveldb",

View File

@ -125,6 +125,8 @@ class TestNode():
self.args.append("-logthreadnames") self.args.append("-logthreadnames")
if self.version_is_at_least(21000000): if self.version_is_at_least(21000000):
self.args.append("-logsourcelocations") 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. # Default behavior from global -v2transport flag is added to args to persist it over restarts.
# May be overwritten in individual tests, using extra_args. # May be overwritten in individual tests, using extra_args.