diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -1758,11 +1758,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(); + GetLogger().ShrinkDebugFile(); } if (GetLogger().fPrintToDebugLog) { - OpenDebugLog(); + GetLogger().OpenDebugLog(); } if (!GetLogger().fLogTimestamps) { diff --git a/src/logging.h b/src/logging.h --- a/src/logging.h +++ b/src/logging.h @@ -9,15 +9,17 @@ #include #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; @@ -51,6 +53,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. @@ -68,8 +74,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 @@ -100,7 +111,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 @@ -11,9 +11,6 @@ #include -#include -#include - bool fLogIPs = DEFAULT_LOGIPS; /** @@ -22,60 +19,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 { @@ -141,6 +103,12 @@ return ret; } +BCLog::Logger::~Logger() { + if (fileout) { + fclose(fileout); + } +} + std::string BCLog::Logger::LogTimestampStr(const std::string &str) { std::string strStamped; @@ -175,14 +143,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) { @@ -200,7 +166,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.