diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -1764,11 +1764,11 @@ 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(); + logger.ShrinkDebugFile(); } if (logger.fPrintToDebugLog) { - OpenDebugLog(); + logger.OpenDebugLog(); } if (!logger.fLogTimestamps) { diff --git a/src/logging.h b/src/logging.h --- a/src/logging.h +++ b/src/logging.h @@ -9,14 +9,16 @@ #include #include +#include #include +#include + static const bool DEFAULT_LOGTIMEMICROS = false; static const bool DEFAULT_LOGIPS = false; static const bool DEFAULT_LOGTIMESTAMPS = true; extern bool fLogIPs; -extern std::atomic fReopenDebugLog; extern std::atomic logCategories; @@ -50,6 +52,10 @@ class Logger { private: + FILE *fileout = nullptr; + boost::mutex mutexDebugLog; + std::list vMsgsBeforeOpenLog; + /** * fStartedNewLine is a state variable that will suppress printing of the * timestamp when multiple calls are made that don't end in a newline. @@ -67,8 +73,13 @@ std::atomic fReopenDebugLog{false}; + ~Logger(); + /** Send a string to the log output */ int LogPrintStr(const std::string &str); + + void OpenDebugLog(); + void ShrinkDebugFile(); }; } // namespace BCLog @@ -98,7 +109,4 @@ GetLogger().LogPrintStr(tfm::format(__VA_ARGS__)); \ } while (0) -void OpenDebugLog(); -void ShrinkDebugFile(); - #endif // BITCOIN_LOGGING_H diff --git a/src/logging.cpp b/src/logging.cpp --- a/src/logging.cpp +++ b/src/logging.cpp @@ -9,9 +9,6 @@ #include "utilstrencodings.h" #include "utiltime.h" -#include -#include - bool fLogIPs = DEFAULT_LOGIPS; /** @@ -38,60 +35,25 @@ */ std::atomic logCategories(0); -/** - * LogPrintf() has been broken a couple of times now by well-meaning people - * adding mutexes in the most straightforward way. It breaks because it may be - * called by global destructors during shutdown. Since the order of destruction - * of static/global objects is undefined, defining a mutex as a global object - * doesn't work (the mutex gets destroyed, and then some later destructor calls - * OutputDebugStringF, maybe indirectly, and you get a core dump at shutdown - * trying to lock the mutex). - */ -static boost::once_flag debugPrintInitFlag = BOOST_ONCE_INIT; - -/** - * We use boost::call_once() to make sure mutexDebugLog and vMsgsBeforeOpenLog - * are initialized in a thread-safe manner. - * - * NOTE: fileout, mutexDebugLog and sometimes vMsgsBeforeOpenLog are leaked on - * exit. This is ugly, but will be cleaned up by the OS/libc. When the shutdown - * sequence is fully audited and tested, explicit destruction of these objects - * can be implemented. - */ -static FILE *fileout = nullptr; -static boost::mutex *mutexDebugLog = nullptr; -static std::list *vMsgsBeforeOpenLog; - static int FileWriteStr(const std::string &str, FILE *fp) { return fwrite(str.data(), 1, str.size(), fp); } -static void DebugPrintInit() { - assert(mutexDebugLog == nullptr); - mutexDebugLog = new boost::mutex(); - vMsgsBeforeOpenLog = new std::list; -} - -void OpenDebugLog() { - boost::call_once(&DebugPrintInit, debugPrintInitFlag); - boost::mutex::scoped_lock scoped_lock(*mutexDebugLog); +void BCLog::Logger::OpenDebugLog() { + boost::mutex::scoped_lock scoped_lock(mutexDebugLog); assert(fileout == nullptr); - assert(vMsgsBeforeOpenLog); fs::path pathDebug = GetDataDir() / "debug.log"; fileout = fsbridge::fopen(pathDebug, "a"); if (fileout) { // Unbuffered. setbuf(fileout, nullptr); // Dump buffered messages from before we opened the log. - while (!vMsgsBeforeOpenLog->empty()) { - FileWriteStr(vMsgsBeforeOpenLog->front(), fileout); - vMsgsBeforeOpenLog->pop_front(); + while (!vMsgsBeforeOpenLog.empty()) { + FileWriteStr(vMsgsBeforeOpenLog.front(), fileout); + vMsgsBeforeOpenLog.pop_front(); } } - - delete vMsgsBeforeOpenLog; - vMsgsBeforeOpenLog = nullptr; } struct CLogCategoryDesc { @@ -157,6 +119,12 @@ return ret; } +BCLog::Logger::~Logger() { + if (fileout) { + fclose(fileout); + } +} + std::string BCLog::Logger::LogTimestampStr(const std::string &str) { std::string strStamped; @@ -191,14 +159,12 @@ ret = fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout); fflush(stdout); } else if (fPrintToDebugLog) { - boost::call_once(&DebugPrintInit, debugPrintInitFlag); - boost::mutex::scoped_lock scoped_lock(*mutexDebugLog); + boost::mutex::scoped_lock scoped_lock(mutexDebugLog); // Buffer if we haven't opened the log yet. if (fileout == nullptr) { - assert(vMsgsBeforeOpenLog); ret = strTimestamped.length(); - vMsgsBeforeOpenLog->push_back(strTimestamped); + vMsgsBeforeOpenLog.push_back(strTimestamped); } else { // Reopen the log file, if requested. if (fReopenDebugLog) { @@ -216,7 +182,7 @@ return ret; } -void ShrinkDebugFile() { +void BCLog::Logger::ShrinkDebugFile() { // Amount of debug.log to save at end when shrinking (must fit in memory) constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000; // Scroll debug.log if it's getting too big.