diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp --- a/src/bench/bench_bitcoin.cpp +++ b/src/bench/bench_bitcoin.cpp @@ -15,7 +15,9 @@ RandomInit(); ECC_Start(); SetupEnvironment(); - fPrintToDebugLog = false; // don't want to write to debug.log file + + // don't want to write to debug.log file + GetLogger().fPrintToDebugLog = false; benchmark::BenchRunner::RunAll(); diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -277,7 +277,7 @@ } void HandleSIGHUP(int) { - fReopenDebugLog = true; + GetLogger().fReopenDebugLog = true; } static bool Bind(CConnman &connman, const CService &addr, unsigned int flags) { @@ -1232,9 +1232,13 @@ } void InitLogging() { - fPrintToConsole = gArgs.GetBoolArg("-printtoconsole", false); - fLogTimestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS); - fLogTimeMicros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS); + BCLog::Logger &logger = GetLogger(); + logger.fPrintToConsole = gArgs.GetBoolArg("-printtoconsole", false); + logger.fLogTimestamps = + gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS); + logger.fLogTimeMicros = + gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS); + fLogIPs = gArgs.GetBoolArg("-logips", DEFAULT_LOGIPS); LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); @@ -1751,17 +1755,20 @@ #ifndef WIN32 CreatePidFile(GetPidFile(), getpid()); #endif + + BCLog::Logger &logger = GetLogger(); + if (gArgs.GetBoolArg("-shrinkdebugfile", logCategories != BCLog::NONE)) { // Do this first since it both loads a bunch of debug.log into memory, // and because this needs to happen before any other debug.log printing. ShrinkDebugFile(); } - if (fPrintToDebugLog) { + if (logger.fPrintToDebugLog) { OpenDebugLog(); } - if (!fLogTimestamps) { + if (!logger.fLogTimestamps) { LogPrintf("Startup time: %s\n", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime())); } diff --git a/src/logging.h b/src/logging.h --- a/src/logging.h +++ b/src/logging.h @@ -15,17 +15,13 @@ static const bool DEFAULT_LOGIPS = false; static const bool DEFAULT_LOGTIMESTAMPS = true; -extern bool fPrintToConsole; -extern bool fPrintToDebugLog; - -extern bool fLogTimestamps; -extern bool fLogTimeMicros; extern bool fLogIPs; extern std::atomic fReopenDebugLog; extern std::atomic logCategories; namespace BCLog { + enum LogFlags : uint32_t { NONE = 0, NET = (1 << 0), @@ -51,7 +47,33 @@ LEVELDB = (1 << 20), ALL = ~uint32_t(0), }; -} + +class Logger { +private: + /** + * fStartedNewLine is a state variable that will suppress printing of the + * timestamp when multiple calls are made that don't end in a newline. + */ + std::atomic_bool fStartedNewLine{true}; + + std::string LogTimestampStr(const std::string &str); + +public: + bool fPrintToConsole = false; + bool fPrintToDebugLog = true; + + bool fLogTimestamps = DEFAULT_LOGTIMESTAMPS; + bool fLogTimeMicros = DEFAULT_LOGTIMEMICROS; + + std::atomic fReopenDebugLog{false}; + + /** Send a string to the log output */ + int LogPrintStr(const std::string &str); +}; + +} // namespace BCLog + +BCLog::Logger &GetLogger(); /** Return true if log accepts specified category */ static inline bool LogAcceptCategory(uint32_t category) { @@ -64,19 +86,16 @@ /** 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 */ -int LogPrintStr(const std::string &str); - #define LogPrint(category, ...) \ do { \ if (LogAcceptCategory((category))) { \ - LogPrintStr(tfm::format(__VA_ARGS__)); \ + GetLogger().LogPrintStr(tfm::format(__VA_ARGS__)); \ } \ } while (0) #define LogPrintf(...) \ do { \ - LogPrintStr(tfm::format(__VA_ARGS__)); \ + GetLogger().LogPrintStr(tfm::format(__VA_ARGS__)); \ } while (0) void OpenDebugLog(); diff --git a/src/logging.cpp b/src/logging.cpp --- a/src/logging.cpp +++ b/src/logging.cpp @@ -12,13 +12,25 @@ #include #include -bool fPrintToConsole = false; -bool fPrintToDebugLog = true; - -bool fLogTimestamps = DEFAULT_LOGTIMESTAMPS; -bool fLogTimeMicros = DEFAULT_LOGTIMEMICROS; bool fLogIPs = DEFAULT_LOGIPS; -std::atomic fReopenDebugLog(false); + +/** + * NOTE: the logger instance is leaked on exit. This is ugly, but will be + * cleaned up by the OS/libc. Defining a logger as a global object doesn't work + * since the order of destruction of static/global objects is undefined. + * Consider if the logger gets destroyed, and then some later destructor calls + * LogPrintf, maybe indirectly, and you get a core dump at shutdown trying to + * access the logger. When the shutdown sequence is fully audited and tested, + * explicit destruction of these objects can be implemented by changing this + * from a raw pointer to a std::unique_ptr. + * + * This method of initialization was originally introduced in + * ee3374234c60aba2cc4c5cd5cac1c0aefc2d817c. + */ +BCLog::Logger &GetLogger() { + static BCLog::Logger *const logger = new BCLog::Logger(); + return *logger; +} /** * Log categories bitfield. Leveldb/libevent need special handling if their @@ -145,18 +157,12 @@ return ret; } -/** - * fStartedNewLine is a state variable held by the calling context that will - * suppress printing of the timestamp when multiple calls are made that don't - * end in a newline. Initialize it to true, and hold it, in the calling context. - */ -static std::string LogTimestampStr(const std::string &str, - std::atomic_bool *fStartedNewLine) { +std::string BCLog::Logger::LogTimestampStr(const std::string &str) { std::string strStamped; if (!fLogTimestamps) return str; - if (*fStartedNewLine) { + if (fStartedNewLine) { int64_t nTimeMicros = GetLogTimeMicros(); strStamped = DateTimeStrFormat("%Y-%m-%d %H:%M:%S", nTimeMicros / 1000000); @@ -167,19 +173,18 @@ strStamped = str; if (!str.empty() && str[str.size() - 1] == '\n') - *fStartedNewLine = true; + fStartedNewLine = true; else - *fStartedNewLine = false; + fStartedNewLine = false; return strStamped; } -int LogPrintStr(const std::string &str) { +int BCLog::Logger::LogPrintStr(const std::string &str) { // Returns total number of characters written. int ret = 0; - static std::atomic_bool fStartedNewLine(true); - std::string strTimestamped = LogTimestampStr(str, &fStartedNewLine); + std::string strTimestamped = LogTimestampStr(str); if (fPrintToConsole) { // Print to console. diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -11,6 +11,7 @@ #include "crypto/sha256.h" #include "fs.h" #include "key.h" +#include "logging.h" #include "miner.h" #include "net_processing.h" #include "pubkey.h" @@ -38,7 +39,6 @@ uint256 insecure_rand_seed = GetRandHash(); FastRandomContext insecure_rand_ctx(insecure_rand_seed); -extern bool fPrintToConsole; extern void noui_connect(); BasicTestingSetup::BasicTestingSetup(const std::string &chainName) { @@ -49,8 +49,10 @@ SetupNetworking(); InitSignatureCache(); InitScriptExecutionCache(); + // Don't want to write to debug.log file. - fPrintToDebugLog = false; + GetLogger().fPrintToDebugLog = false; + fCheckBlockIndex = true; SelectParams(chainName); noui_connect(); diff --git a/src/util.h b/src/util.h --- a/src/util.h +++ b/src/util.h @@ -60,7 +60,7 @@ bool SetupNetworking(); template bool error(const char *fmt, const Args &... args) { - LogPrintStr("ERROR: " + tfm::format(fmt, args...) + "\n"); + LogPrintf("ERROR: " + tfm::format(fmt, args...) + "\n"); return false; }