Merge #16112: util: Log early messages

faa2a47cd7 logging: Add threadsafety comments (MarcoFalke)
0b282f9b00 Log early messages with -printtoconsole (Anthony Towns)
412987430c Replace OpenDebugLog() with StartLogging() (Anthony Towns)

Pull request description:

  Early log messages are dropped on the floor and they'd never make it to the console or debug log. This can be tested by running the test included in this pull request without re-compiling the `bitcoind`.

  Fix that by buffering early messages and flushing them as soon as all logging options have been initialized and logging has been started.

  This pull request is identical to  "Log early messages with -printtoconsole" (#13088)  by **ajtowns**, with the following changes:
  * Rebased
  * Added docstrings for `m_buffering` and `StartLogging`
  * Switch `CCriticalSection` (aka `RecursiveMutex`) to just `Mutex` in the last commit
  * Added tests

  Fixes #16098
  Fixes #13157
  Closes #13088

ACKs for commit faa2a4:
  ajtowns:
    utACK faa2a47cd7
  hebasto:
    ACK faa2a47cd7
  kristapsk:
    ACK faa2a47cd7 (ran added functional test before / after recompiling, didn't do additional testing)

Tree-SHA512: 685e2882642fe2a43ce171d42862582dadb840d03cda8236a994322c389ca2a1f3f431b179b2726c155c61793543bb340c568a5455d97f8b83bc7d307a85d387
This commit is contained in:
MarcoFalke 2019-06-18 12:32:18 -04:00
commit 0853d8d2fd
No known key found for this signature in database
GPG key ID: D2EA4850E7528B25
4 changed files with 62 additions and 40 deletions

View file

@ -852,12 +852,6 @@ void InitLogging()
{
LogInstance().m_print_to_file = !gArgs.IsArgNegated("-debuglogfile");
LogInstance().m_file_path = AbsPathForConfigVal(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE));
// Add newlines to the logfile to distinguish this execution from the last
// one; called before console logging is set up, so this is only sent to
// debug.log.
LogPrintf("\n\n\n\n\n");
LogInstance().m_print_to_console = gArgs.GetBoolArg("-printtoconsole", !gArgs.GetBoolArg("-daemon", false));
LogInstance().m_log_timestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
LogInstance().m_log_time_micros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
@ -1237,10 +1231,10 @@ bool AppInitMain(InitInterfaces& interfaces)
// and because this needs to happen before any other debug.log printing
LogInstance().ShrinkDebugFile();
}
if (!LogInstance().OpenDebugLog()) {
}
if (!LogInstance().StartLogging()) {
return InitError(strprintf("Could not open debug log file %s",
LogInstance().m_file_path.string()));
}
}
if (!LogInstance().m_log_timestamps)

View file

@ -39,24 +39,38 @@ static int FileWriteStr(const std::string &str, FILE *fp)
return fwrite(str.data(), 1, str.size(), fp);
}
bool BCLog::Logger::OpenDebugLog()
bool BCLog::Logger::StartLogging()
{
std::lock_guard<std::mutex> scoped_lock(m_file_mutex);
std::lock_guard<std::mutex> scoped_lock(m_cs);
assert(m_buffering);
assert(m_fileout == nullptr);
assert(!m_file_path.empty());
m_fileout = fsbridge::fopen(m_file_path, "a");
if (!m_fileout) {
return false;
if (m_print_to_file) {
assert(!m_file_path.empty());
m_fileout = fsbridge::fopen(m_file_path, "a");
if (!m_fileout) {
return false;
}
setbuf(m_fileout, nullptr); // unbuffered
// Add newlines to the logfile to distinguish this execution from the
// last one.
FileWriteStr("\n\n\n\n\n", m_fileout);
}
setbuf(m_fileout, nullptr); // unbuffered
// dump buffered messages from before we opened the log
m_buffering = false;
while (!m_msgs_before_open.empty()) {
FileWriteStr(m_msgs_before_open.front(), m_fileout);
const std::string& s = m_msgs_before_open.front();
if (m_print_to_file) FileWriteStr(s, m_fileout);
if (m_print_to_console) fwrite(s.data(), 1, s.size(), stdout);
m_msgs_before_open.pop_front();
}
if (m_print_to_console) fflush(stdout);
return true;
}
@ -202,8 +216,9 @@ std::string BCLog::Logger::LogTimestampStr(const std::string& str)
return strStamped;
}
void BCLog::Logger::LogPrintStr(const std::string &str)
void BCLog::Logger::LogPrintStr(const std::string& str)
{
std::lock_guard<std::mutex> scoped_lock(m_cs);
std::string str_prefixed = str;
if (m_log_threadnames && m_started_new_line) {
@ -214,32 +229,31 @@ void BCLog::Logger::LogPrintStr(const std::string &str)
m_started_new_line = !str.empty() && str[str.size()-1] == '\n';
if (m_buffering) {
// buffer if we haven't started logging yet
m_msgs_before_open.push_back(str_prefixed);
return;
}
if (m_print_to_console) {
// print to console
fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout);
fflush(stdout);
}
if (m_print_to_file) {
std::lock_guard<std::mutex> scoped_lock(m_file_mutex);
assert(m_fileout != nullptr);
// buffer if we haven't opened the log yet
if (m_fileout == nullptr) {
m_msgs_before_open.push_back(str_prefixed);
}
else
{
// reopen the log file, if requested
if (m_reopen_file) {
m_reopen_file = false;
FILE* new_fileout = fsbridge::fopen(m_file_path, "a");
if (new_fileout) {
setbuf(new_fileout, nullptr); // unbuffered
fclose(m_fileout);
m_fileout = new_fileout;
}
// reopen the log file, if requested
if (m_reopen_file) {
m_reopen_file = false;
FILE* new_fileout = fsbridge::fopen(m_file_path, "a");
if (new_fileout) {
setbuf(new_fileout, nullptr); // unbuffered
fclose(m_fileout);
m_fileout = new_fileout;
}
FileWriteStr(str_prefixed, m_fileout);
}
FileWriteStr(str_prefixed, m_fileout);
}
}

View file

@ -60,9 +60,10 @@ namespace BCLog {
class Logger
{
private:
FILE* m_fileout = nullptr;
std::mutex m_file_mutex;
std::list<std::string> m_msgs_before_open;
mutable std::mutex m_cs; // Can not use Mutex from sync.h because in debug mode it would cause a deadlock when a potential deadlock was detected
FILE* m_fileout = nullptr; // GUARDED_BY(m_cs)
std::list<std::string> m_msgs_before_open; // GUARDED_BY(m_cs)
bool m_buffering{true}; //!< Buffer messages before logging can be started. GUARDED_BY(m_cs)
/**
* m_started_new_line is a state variable that will suppress printing of
@ -88,12 +89,18 @@ namespace BCLog {
std::atomic<bool> m_reopen_file{false};
/** Send a string to the log output */
void LogPrintStr(const std::string &str);
void LogPrintStr(const std::string& str);
/** Returns whether logs will be written to any output */
bool Enabled() const { return m_print_to_console || m_print_to_file; }
bool Enabled() const
{
std::lock_guard<std::mutex> scoped_lock(m_cs);
return m_buffering || m_print_to_console || m_print_to_file;
}
/** Start logging (and flush all buffered messages) */
bool StartLogging();
bool OpenDebugLog();
void ShrinkDebugFile();
uint32_t GetCategoryMask() const { return m_categories.load(); }

View file

@ -61,9 +61,16 @@ class ConfArgsTest(BitcoinTestFramework):
with open(inc_conf_file2_path, 'w', encoding='utf-8') as conf:
conf.write('') # clear
def test_log_buffer(self):
with self.nodes[0].assert_debug_log(expected_msgs=['Warning: parsed potentially confusing double-negative -connect=0']):
self.start_node(0, extra_args=['-noconnect=0'])
self.stop_node(0)
def run_test(self):
self.stop_node(0)
self.test_log_buffer()
self.test_config_file_parser()
# Remove the -datadir argument so it doesn't override the config file