From cf5f432c69418adb51f0f4a0bdbd6ba3112ee11a Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 28 Nov 2017 10:31:52 +0100 Subject: [PATCH 1/4] Add `-debuglogfile` option This patch adds an option to configure the name and/or directory of the debug log. The user can specify either a relative path, in which case the path is relative to the data directory. They can also specify an absolute path to put the log anywhere else in the file system. --- src/init.cpp | 8 ++++++-- src/util.cpp | 37 ++++++++++++++++++++++++++----------- src/util.h | 4 +++- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 935686299..871a58526 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -342,6 +342,7 @@ std::string HelpMessage(HelpMessageMode mode) if (showDebug) strUsage += HelpMessageOpt("-feefilter", strprintf("Tell other nodes to filter invs to us by our mempool min fee (default: %u)", DEFAULT_FEEFILTER)); strUsage += HelpMessageOpt("-loadblock=", _("Imports blocks from external blk000??.dat file on startup")); + strUsage += HelpMessageOpt("-debuglogfile=", strprintf(_("Specify location of debug log file: this can be an absolute path or a path relative to the data directory (default: %s)"), DEFAULT_DEBUGLOGFILE)); strUsage += HelpMessageOpt("-maxorphantx=", strprintf(_("Keep at most unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS)); strUsage += HelpMessageOpt("-maxmempool=", strprintf(_("Keep the transaction memory pool below megabytes (default: %u)"), DEFAULT_MAX_MEMPOOL_SIZE)); strUsage += HelpMessageOpt("-mempoolexpiry=", strprintf(_("Do not keep transactions in the mempool longer than hours (default: %u)"), DEFAULT_MEMPOOL_EXPIRY)); @@ -1209,8 +1210,11 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) ShrinkDebugFile(); } - if (fPrintToDebugLog) - OpenDebugLog(); + if (fPrintToDebugLog) { + if (!OpenDebugLog()) { + return InitError(strprintf("Could not open debug log file %s", GetDebugLogPath().string())); + } + } if (!fLogTimestamps) LogPrintf("Startup time: %s\n", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime())); diff --git a/src/util.cpp b/src/util.cpp index b2023b832..6631c236f 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -89,6 +89,7 @@ const int64_t nStartupTime = GetTime(); const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf"; const char * const BITCOIN_PID_FILENAME = "bitcoind.pid"; +const char * const DEFAULT_DEBUGLOGFILE = "debug.log"; ArgsManager gArgs; bool fPrintToConsole = false; @@ -189,26 +190,40 @@ static void DebugPrintInit() vMsgsBeforeOpenLog = new std::list; } -void OpenDebugLog() +fs::path GetDebugLogPath() +{ + fs::path logfile(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE)); + if (logfile.is_absolute()) { + return logfile; + } else { + return GetDataDir() / logfile; + } +} + +bool OpenDebugLog() { boost::call_once(&DebugPrintInit, debugPrintInitFlag); boost::mutex::scoped_lock scoped_lock(*mutexDebugLog); assert(fileout == nullptr); assert(vMsgsBeforeOpenLog); - fs::path pathDebug = GetDataDir() / "debug.log"; + fs::path pathDebug = GetDebugLogPath(); + fileout = fsbridge::fopen(pathDebug, "a"); - if (fileout) { - setbuf(fileout, nullptr); // unbuffered - // dump buffered messages from before we opened the log - while (!vMsgsBeforeOpenLog->empty()) { - FileWriteStr(vMsgsBeforeOpenLog->front(), fileout); - vMsgsBeforeOpenLog->pop_front(); - } + if (!fileout) { + return false; + } + + setbuf(fileout, nullptr); // unbuffered + // dump buffered messages from before we opened the log + while (!vMsgsBeforeOpenLog->empty()) { + FileWriteStr(vMsgsBeforeOpenLog->front(), fileout); + vMsgsBeforeOpenLog->pop_front(); } delete vMsgsBeforeOpenLog; vMsgsBeforeOpenLog = nullptr; + return true; } struct CLogCategoryDesc @@ -355,7 +370,7 @@ int LogPrintStr(const std::string &str) // reopen the log file, if requested if (fReopenDebugLog) { fReopenDebugLog = false; - fs::path pathDebug = GetDataDir() / "debug.log"; + fs::path pathDebug = GetDebugLogPath(); if (fsbridge::freopen(pathDebug,"a",fileout) != nullptr) setbuf(fileout, nullptr); // unbuffered } @@ -774,7 +789,7 @@ void 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 - fs::path pathLog = GetDataDir() / "debug.log"; + fs::path pathLog = GetDebugLogPath(); FILE* file = fsbridge::fopen(pathLog, "r"); // 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 diff --git a/src/util.h b/src/util.h index 08de43d29..6687b865d 100644 --- a/src/util.h +++ b/src/util.h @@ -36,6 +36,7 @@ int64_t GetStartupTime(); static const bool DEFAULT_LOGTIMEMICROS = false; static const bool DEFAULT_LOGIPS = false; static const bool DEFAULT_LOGTIMESTAMPS = true; +extern const char * const DEFAULT_DEBUGLOGFILE; /** Signals for translation. */ class CTranslationInterface @@ -180,7 +181,8 @@ void CreatePidFile(const fs::path &path, pid_t pid); #ifdef WIN32 fs::path GetSpecialFolderPath(int nFolder, bool fCreate = true); #endif -void OpenDebugLog(); +fs::path GetDebugLogPath(); +bool OpenDebugLog(); void ShrinkDebugFile(); void runCommand(const std::string& strCommand); From 23232422e507a90922c0870611cade5c789bb83a Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 28 Nov 2017 15:38:46 +0100 Subject: [PATCH 2/4] test: Add test for `-debuglogfile` --- test/functional/feature_logging.py | 42 ++++++++++++++++++++++++++++++ test/functional/test_runner.py | 1 + 2 files changed, 43 insertions(+) create mode 100755 test/functional/feature_logging.py diff --git a/test/functional/feature_logging.py b/test/functional/feature_logging.py new file mode 100755 index 000000000..24591e176 --- /dev/null +++ b/test/functional/feature_logging.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test debug logging.""" + +import os + +from test_framework.test_framework import BitcoinTestFramework + +class LoggingTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 1 + self.setup_clean_chain = True + + def run_test(self): + # test default log file name + assert os.path.isfile(os.path.join(self.nodes[0].datadir, "regtest", "debug.log")) + + # 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")) + + # test alternative log file name outside datadir + tempname = os.path.join(self.options.tmpdir, "foo.log") + self.restart_node(0, ["-debuglogfile=%s" % tempname]) + assert os.path.isfile(tempname) + + # check that invalid log (relative) will cause error + self.stop_node(0) + self.assert_start_raises_init_error(0, ["-debuglogfile=ssdksjdf/sdasdfa/sdfsdfsfd"], + "Error: Could not open debug log file") + + # check that invalid log (absolute) will cause error + self.stop_node(0) + invalidname = os.path.join(self.options.tmpdir, "foo/foo.log") + self.assert_start_raises_init_error(0, ["-debuglogfile=%s" % invalidname], + "Error: Could not open debug log file") + + +if __name__ == '__main__': + LoggingTest().main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index d953e1585..2202d65b5 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -126,6 +126,7 @@ BASE_SCRIPTS= [ 'p2p-fingerprint.py', 'uacomment.py', 'p2p-acceptblock.py', + 'feature_logging.py', ] EXTENDED_SCRIPTS = [ From 4158734946eb00a6944244f75a8a5b39e43c302a Mon Sep 17 00:00:00 2001 From: "Wladimir J. van der Laan" Date: Tue, 28 Nov 2017 15:43:31 +0100 Subject: [PATCH 3/4] doc: Update release notes for `-debuglogfile` --- doc/release-notes.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/release-notes.md b/doc/release-notes.md index 2c63b1f88..43009b081 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -90,6 +90,9 @@ Low-level RPC changes * `getmininginfo` - The wallet RPC `getreceivedbyaddress` will return an error if called with an address not in the wallet. +Changed command-line options +----------------------------- +- `-debuglogfile=` can be used to specify an alternative debug logging file. Credits ======= From 5a7c09aebf8b0229e9f320135472275d244a7a35 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Fri, 1 Dec 2017 11:49:28 +1000 Subject: [PATCH 4/4] test: Add tests for `-debuglogfile` with subdirs --- test/functional/feature_logging.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/test/functional/feature_logging.py b/test/functional/feature_logging.py index 24591e176..da4e7b039 100755 --- a/test/functional/feature_logging.py +++ b/test/functional/feature_logging.py @@ -27,15 +27,32 @@ class LoggingTest(BitcoinTestFramework): assert os.path.isfile(tempname) # check that invalid log (relative) will cause error + invdir = os.path.join(self.nodes[0].datadir, "regtest", "foo") + invalidname = os.path.join("foo", "foo.log") self.stop_node(0) - self.assert_start_raises_init_error(0, ["-debuglogfile=ssdksjdf/sdasdfa/sdfsdfsfd"], + self.assert_start_raises_init_error(0, ["-debuglogfile=%s" % (invalidname)], "Error: Could not open debug log file") + assert not os.path.isfile(os.path.join(invdir, "foo.log")) + + # check that invalid log (relative) works after path exists + self.stop_node(0) + os.mkdir(invdir) + self.start_node(0, ["-debuglogfile=%s" % (invalidname)]) + assert os.path.isfile(os.path.join(invdir, "foo.log")) # check that invalid log (absolute) will cause error self.stop_node(0) - invalidname = os.path.join(self.options.tmpdir, "foo/foo.log") + invdir = os.path.join(self.options.tmpdir, "foo") + invalidname = os.path.join(invdir, "foo.log") self.assert_start_raises_init_error(0, ["-debuglogfile=%s" % invalidname], "Error: Could not open debug log file") + assert not os.path.isfile(os.path.join(invdir, "foo.log")) + + # check that invalid log (absolute) works after path exists + self.stop_node(0) + os.mkdir(invdir) + self.start_node(0, ["-debuglogfile=%s" % (invalidname)]) + assert os.path.isfile(os.path.join(invdir, "foo.log")) if __name__ == '__main__':