Skip to content

Commit 3c16201

Browse files
committed
improve error handling, add functional test
1 parent c82eece commit 3c16201

File tree

4 files changed

+73
-9
lines changed

4 files changed

+73
-9
lines changed

src/init/common.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ void SetLoggingOptions(const ArgsManager& args)
9292
#endif
9393
LogInstance().m_log_sourcelocations = args.GetBoolArg("-logsourcelocations", DEFAULT_LOGSOURCELOCATIONS);
9494
LogInstance().m_rotate_keep = args.GetArg("-debuglogrotatekeep", DEFAULT_DEBUGLOGROTATEKEEP);
95+
LogInstance().m_rotate_requested = args.IsArgSet("-debuglogrotatekeep") && LogInstance().m_rotate_keep > 0;
9596
LogInstance().m_file_limit = args.GetArg("-debugloglimit", DEFAULT_DEBUGLOGMB);
9697

9798
fLogIPs = args.GetBoolArg("-logips", DEFAULT_LOGIPS);
@@ -130,9 +131,11 @@ bool StartLogging(const ArgsManager& args)
130131
LogInstance().ShrinkDebugFile();
131132
}
132133
}
133-
if (!LogInstance().StartLogging()) {
134-
return InitError(strprintf(Untranslated("Could not open debug log file %s"),
135-
LogInstance().m_file_path.string()));
134+
{
135+
const std::string err_msg = LogInstance().StartLogging();
136+
if (!err_msg.empty()) {
137+
return InitError(Untranslated(err_msg));
138+
}
136139
}
137140

138141
if (!LogInstance().m_log_timestamps)

src/logging.cpp

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ void BCLog::Logger::StartRotate()
7373
RemoveRotate();
7474
}
7575

76-
bool BCLog::Logger::StartLogging()
76+
const std::string BCLog::Logger::StartLogging()
7777
{
7878
StdLockGuard scoped_lock(m_cs);
7979

@@ -84,18 +84,42 @@ bool BCLog::Logger::StartLogging()
8484
assert(!m_file_path.empty());
8585
m_fileout = fsbridge::fopen(m_file_path, "a");
8686
if (!m_fileout) {
87-
return false;
87+
return strprintf("Could not open debug log file %s", m_file_path.string());
8888
}
89+
8990
// Special files (e.g. device nodes) may not have a size.
9091
try {
9192
m_file_size = fs::file_size(m_file_path);
9293
} catch (const fs::filesystem_error&) {
9394
// We can't do log rotation if it's not a regular file.
95+
if (m_rotate_requested) {
96+
return strprintf(
97+
"The debug log file %s does not appear to be a regular file; "
98+
"this prevents log rotation. Please disable log rotation by "
99+
"specifying -debuglogrotatekeep=0 or specify a regular file "
100+
"as the debug log file using -debuglogfile.",
101+
m_file_path.string());
102+
}
94103
m_rotate_keep = 0;
95104
}
96105
// It gets complicated to add a timestamp to an arbitrary file name,
97106
// so require "debug.log".
98-
if (m_file_path.filename() != "debug.log") m_rotate_keep = 0;
107+
if (m_file_path.filename() != "debug.log") {
108+
if (m_rotate_requested) {
109+
return strprintf(
110+
"The debug log file %s is not named \"debug.log\"; "
111+
"this prevents log rotation. Please disable log rotation by "
112+
"specifying -debuglogrotatekeep=0 or specify a debug log file "
113+
"path that ends in \"/debug.log\" using -debuglogfile.",
114+
m_file_path.string());
115+
}
116+
m_rotate_keep = 0;
117+
}
118+
if (m_file_limit == 0) {
119+
return strprintf(
120+
"The debug log file %s size limit in MB must be greater than zero.",
121+
m_file_path.string());
122+
}
99123
if (m_rotate_keep > 0) StartRotate();
100124

101125
setbuf(m_fileout, nullptr); // unbuffered
@@ -120,7 +144,7 @@ bool BCLog::Logger::StartLogging()
120144
}
121145
if (m_print_to_console) fflush(stdout);
122146

123-
return true;
147+
return {};
124148
}
125149

126150
void BCLog::Logger::DisconnectTestLogger()

src/logging.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ namespace BCLog {
109109
int m_file_limit;
110110
/** The number of old (rotated) debug.log files to retain */
111111
int m_rotate_keep;
112+
/** Whether log rotation was explicitly requested */
113+
bool m_rotate_requested;
112114
std::atomic<bool> m_reopen_file{false};
113115

114116
/** Send a string to the log output */
@@ -136,8 +138,8 @@ namespace BCLog {
136138
m_print_callbacks.erase(it);
137139
}
138140

139-
/** Start logging (and flush all buffered messages) */
140-
bool StartLogging();
141+
/** Start logging (and flush all buffered messages), returns error string (empty if none ) */
142+
const std::string StartLogging();
141143
/** Initialize debug.log rotation (find existing rotated debug.log files) */
142144
void StartRotate() EXCLUSIVE_LOCKS_REQUIRED(m_cs);
143145
/** Remove (delete) excess debug.log rotation files */

test/functional/feature_logging.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from test_framework.test_framework import BitcoinTestFramework
1010
from test_framework.test_node import ErrorMatch
11+
from test_framework.util import assert_equal
1112

1213

1314
class LoggingTest(BitcoinTestFramework):
@@ -69,6 +70,40 @@ def run_test(self):
6970
# just sanity check no crash here
7071
self.restart_node(0, ["-debuglogfile=%s" % os.devnull])
7172

73+
# a nonstandard name can't be combined with log rotation
74+
self.stop_node(0)
75+
args = ["-debuglogfile=notdebug.log", "-debuglogrotatekeep=2"]
76+
exp_stderr = r"Error: .* this prevents log rotation"
77+
self.nodes[0].assert_start_raises_init_error(args, exp_stderr, match=ErrorMatch.PARTIAL_REGEX)
78+
assert not os.path.isfile(os.path.join(invdir, "notdebug.log"))
79+
80+
# It's not necessary to test -debuglogrotate=0, because all functional tests run that way
81+
# (and some of them break if the debug log file does get rotated. So just test rotation.
82+
self.stop_node(0)
83+
self.start_node(0, ["-debuglogrotatekeep=2", "-debugloglimit=1"])
84+
self.log.info("Generating a bunch of log messages")
85+
# Each getblockcount RPC logs 251 bytes, so 13k of them generates
86+
# more than 3 MB of logging, which will test that one of the
87+
# rotated log files gets deleted.
88+
for _ in range(13000):
89+
self.nodes[0].getblockcount()
90+
n_debugfiles = 0
91+
with os.scandir(os.path.join(self.nodes[0].datadir, 'regtest')) as it:
92+
for entry in it:
93+
if not entry.name.startswith('debug'):
94+
continue
95+
if not entry.name.endswith('.log'):
96+
continue
97+
# the name should be either debug.log or like debug-2021-07-09T01:49:59Z.log
98+
assert len(entry.name) == 9 or len(entry.name) == 30
99+
# rotated files should have sizes slightly more than 1 MB
100+
if len(entry.name) == 30:
101+
assert entry.stat().st_size > 1000000
102+
assert entry.stat().st_size < int(1000000 * 1.01)
103+
n_debugfiles += 1
104+
# There should be debug.log and two rotated files
105+
assert_equal(n_debugfiles, 3)
106+
72107

73108
if __name__ == '__main__':
74109
LoggingTest().main()

0 commit comments

Comments
 (0)