diff --git a/doc/release-notes.md b/doc/release-notes.md --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -11,3 +11,7 @@ as such, is subject to changes or removal in future releases. - The new RPC `getzmqnotifications` returns information about active ZMQ notifications. + - When running bitcoind without `-daemon`, logging to stdout is now the + default behavior. Setting `-printtoconsole=1` no longer implicitly disables + logging to debug.log. Instead, logging to file can be explicitly disabled by + setting `-debuglogfile=0`. diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -392,12 +392,12 @@ "Set database cache size in megabytes (%d to %d, default: %d)", nMinDbCache, nMaxDbCache, nDefaultDbCache), false, OptionsCategory::OPTIONS); - gArgs.AddArg( - "-debuglogfile=", - strprintf("Specify location of debug log file. Relative paths will be " - "prefixed by a net-specific datadir location. (default: %s)", - DEFAULT_DEBUGLOGFILE), - false, OptionsCategory::OPTIONS); + gArgs.AddArg("-debuglogfile=", + strprintf("Specify location of debug log file. Relative paths " + "will be prefixed by a net-specific datadir " + "location. (0 to disable; default: %s)", + DEFAULT_DEBUGLOGFILE), + false, OptionsCategory::OPTIONS); gArgs.AddArg("-feefilter", strprintf("Tell other nodes to filter invs to us by our " "mempool min fee (default: %d)", @@ -799,9 +799,11 @@ DEFAULT_MAX_TIP_AGE), true, OptionsCategory::DEBUG_TEST); - gArgs.AddArg("-printtoconsole", - "Send trace/debug info to console instead of debug.log file", - false, OptionsCategory::DEBUG_TEST); + gArgs.AddArg( + "-printtoconsole", + "Send trace/debug info to console instead of debug.log file (default: " + "1 when no -daemon. To disable logging to file, set debuglogfile=0)", + false, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-printpriority", strprintf("Log transaction priority and fee per kB when " "mining blocks (default: %d)", @@ -1350,9 +1352,22 @@ return strprintf(_("Cannot resolve -%s address: '%s'"), optname, strBind); } +/** + * Initialize global loggers. + * + * Note that this is called very early in the process lifetime, so you should be + * careful about what global state you rely on here. + */ void InitLogging() { + // 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"); + BCLog::Logger &logger = GetLogger(); - logger.m_print_to_console = gArgs.GetBoolArg("-printtoconsole", false); + logger.m_print_to_console = gArgs.GetBoolArg( + "-printtoconsole", !gArgs.GetBoolArg("-daemon", false)); + logger.m_print_to_file = !gArgs.IsArgNegated("-debuglogfile"); logger.m_log_timestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS); logger.m_log_time_micros = @@ -1360,7 +1375,6 @@ 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"); LogPrintf("%s version %s\n", CLIENT_NAME, FormatFullVersion()); } @@ -1829,15 +1843,15 @@ #endif BCLog::Logger &logger = GetLogger(); - - bool default_shrinkdebugfile = logger.DefaultShrinkDebugFile(); - if (gArgs.GetBoolArg("-shrinkdebugfile", default_shrinkdebugfile)) { - // 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. - logger.ShrinkDebugFile(); - } - if (logger.m_print_to_file) { + bool default_shrinkdebugfile = logger.DefaultShrinkDebugFile(); + if (gArgs.GetBoolArg("-shrinkdebugfile", default_shrinkdebugfile)) { + // 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. + logger.ShrinkDebugFile(); + } + if (!logger.OpenDebugLog()) { return InitError(strprintf("Could not open debug log file %s", logger.GetDebugLogPath().string())); diff --git a/src/logging.cpp b/src/logging.cpp --- a/src/logging.cpp +++ b/src/logging.cpp @@ -186,7 +186,8 @@ // Print to console. fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout); fflush(stdout); - } else if (m_print_to_file) { + } + if (m_print_to_file) { std::lock_guard scoped_lock(m_file_mutex); // Buffer if we haven't opened the log yet. @@ -216,10 +217,17 @@ // Scroll debug.log if it's getting too big. fs::path pathLog = GetDebugLogPath(); FILE *file = fsbridge::fopen(pathLog, "r"); + + // Special files (e.g. device nodes) may not have a size. + size_t log_size = 0; + try { + log_size = fs::file_size(pathLog); + } catch (boost::filesystem::filesystem_error &) { + } + // If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE // trim it down by saving only the last RECENT_DEBUG_HISTORY_SIZE bytes. - if (file && - fs::file_size(pathLog) > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10)) { + if (file && log_size > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10)) { // Restart the file with some of the end. std::vector vch(RECENT_DEBUG_HISTORY_SIZE, 0); if (fseek(file, -((long)vch.size()), SEEK_END)) { diff --git a/test/functional/feature_logging.py b/test/functional/feature_logging.py --- a/test/functional/feature_logging.py +++ b/test/functional/feature_logging.py @@ -15,15 +15,17 @@ self.num_nodes = 1 self.setup_clean_chain = True + def relative_log_path(self, name): + return os.path.join(self.nodes[0].datadir, "regtest", name) + def run_test(self): # test default log file name - assert os.path.isfile(os.path.join( - self.nodes[0].datadir, "regtest", "debug.log")) + default_log_path = self.relative_log_path("debug.log") + assert os.path.isfile(default_log_path) # test alternative log file name in datadir self.restart_node(0, ["-debuglogfile=foo.log"]) - assert os.path.isfile(os.path.join( - self.nodes[0].datadir, "regtest", "foo.log")) + assert os.path.isfile(self.relative_log_path("foo.log")) # test alternative log file name outside datadir tempname = os.path.join(self.options.tmpdir, "foo.log") @@ -31,7 +33,7 @@ assert os.path.isfile(tempname) # check that invalid log (relative) will cause error - invdir = os.path.join(self.nodes[0].datadir, "regtest", "foo") + invdir = self.relative_log_path("foo") invalidname = os.path.join("foo", "foo.log") self.stop_node(0) exp_stderr = r"Error: Could not open debug log file \S+$" @@ -59,6 +61,17 @@ self.start_node(0, ["-debuglogfile={}".format(invalidname)]) assert os.path.isfile(os.path.join(invdir, "foo.log")) + # check that -nodebuglogfile disables logging + self.stop_node(0) + os.unlink(default_log_path) + assert not os.path.isfile(default_log_path) + self.start_node(0, ["-nodebuglogfile"]) + assert not os.path.isfile(default_log_path) + + # just sanity check no crash here + self.stop_node(0) + self.start_node(0, ["-debuglogfile={}".format(os.devnull)]) + if __name__ == '__main__': LoggingTest().main() diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -83,8 +83,16 @@ # Note that common args are set in the config file (see # initialize_datadir) self.extra_args = extra_args - self.default_args = ["-datadir=" + self.datadir, "-logtimemicros", "-debug", "-debugexclude=libevent", - "-debugexclude=leveldb", "-mocktime=" + str(mocktime), "-uacomment=" + self.name] + self.default_args = [ + "-datadir=" + self.datadir, + "-logtimemicros", + "-debug", + "-debugexclude=libevent", + "-debugexclude=leveldb", + "-mocktime=" + str(mocktime), + "-uacomment=" + self.name, + "-noprinttoconsole", + ] if not os.path.isfile(bitcoin_cli): raise FileNotFoundError(