diff --git a/doc/release-notes.md b/doc/release-notes.md --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -5,3 +5,4 @@ This release includes the following features and fixes: - Add the `-walletdir` option to configure the directory in which the wallet files are stored. If a relative path is used, it is relative to tha data dir. + - Add the `-debuglogfile` option to change the path of the debug log file. diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -405,6 +405,12 @@ 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)"), @@ -1787,7 +1793,10 @@ } if (logger.m_print_to_file) { - logger.OpenDebugLog(); + if (!logger.OpenDebugLog()) { + return InitError(strprintf("Could not open debug log file %s", + logger.GetDebugLogPath().string())); + } } if (!logger.m_log_timestamps) { diff --git a/src/logging.h b/src/logging.h --- a/src/logging.h +++ b/src/logging.h @@ -21,6 +21,7 @@ static const bool DEFAULT_LOGTIMESTAMPS = true; extern bool fLogIPs; +extern const char *const DEFAULT_DEBUGLOGFILE; namespace BCLog { @@ -84,7 +85,8 @@ /** Send a string to the log output */ int LogPrintStr(const std::string &str); - void OpenDebugLog(); + fs::path GetDebugLogPath(); + bool OpenDebugLog(); void ShrinkDebugFile(); void EnableCategory(LogFlags category); diff --git a/src/logging.cpp b/src/logging.cpp --- a/src/logging.cpp +++ b/src/logging.cpp @@ -9,6 +9,7 @@ #include "utiltime.h" bool fLogIPs = DEFAULT_LOGIPS; +const char *const DEFAULT_DEBUGLOGFILE = "debug.log"; /** * NOTE: the logger instance is leaked on exit. This is ugly, but will be @@ -32,21 +33,35 @@ return fwrite(str.data(), 1, str.size(), fp); } -void BCLog::Logger::OpenDebugLog() { +fs::path BCLog::Logger::GetDebugLogPath() { + fs::path logfile(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE)); + if (logfile.is_absolute()) { + return logfile; + } else { + return GetDataDir() / logfile; + } +} + +bool BCLog::Logger::OpenDebugLog() { std::lock_guard scoped_lock(m_file_mutex); assert(m_fileout == nullptr); - fs::path pathDebug = GetDataDir() / "debug.log"; + fs::path pathDebug = GetDebugLogPath(); + m_fileout = fsbridge::fopen(pathDebug, "a"); - if (m_fileout) { - // Unbuffered. - setbuf(m_fileout, nullptr); - // Dump buffered messages from before we opened the log. - while (!m_msgs_before_open.empty()) { - FileWriteStr(m_msgs_before_open.front(), m_fileout); - m_msgs_before_open.pop_front(); - } + if (!m_fileout) { + return false; + } + + // Unbuffered. + setbuf(m_fileout, nullptr); + // Dump buffered messages from before we opened the log. + while (!m_msgs_before_open.empty()) { + FileWriteStr(m_msgs_before_open.front(), m_fileout); + m_msgs_before_open.pop_front(); } + + return true; } struct CLogCategoryDesc { @@ -161,7 +176,7 @@ // Reopen the log file, if requested. if (m_reopen_file) { m_reopen_file = false; - fs::path pathDebug = GetDataDir() / "debug.log"; + fs::path pathDebug = GetDebugLogPath(); if (fsbridge::freopen(pathDebug, "a", m_fileout) != nullptr) { // unbuffered. setbuf(m_fileout, nullptr); @@ -178,7 +193,7 @@ // 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/test/functional/feature_logging.py b/test/functional/feature_logging.py new file mode 100755 --- /dev/null +++ b/test/functional/feature_logging.py @@ -0,0 +1,62 @@ +#!/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={}".format(tempname)]) + 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={}".format(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={}".format(invalidname)]) + assert os.path.isfile(os.path.join(invdir, "foo.log")) + + # check that invalid log (absolute) will cause error + self.stop_node(0) + invdir = os.path.join(self.options.tmpdir, "foo") + invalidname = os.path.join(invdir, "foo.log") + self.assert_start_raises_init_error(0, ["-debuglogfile={}".format(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={}".format(invalidname)]) + assert os.path.isfile(os.path.join(invdir, "foo.log")) + + +if __name__ == '__main__': + LoggingTest().main() diff --git a/test/functional/timing.json b/test/functional/timing.json --- a/test/functional/timing.json +++ b/test/functional/timing.json @@ -83,6 +83,10 @@ "name": "feature_dersig.py", "time": 19 }, + { + "name": "feature_logging.py", + "time": 13 + }, { "name": "feature_maxuploadtarget.py", "time": 30