Skip to content

Commit 27d7605

Browse files
committed
Buffer log messages and explicitly open logs
Prevents stomping on debug logs in datadirs that are locked by other instances and lost parameter interaction messages that can get wiped by ShrinkDebugFile(). The log is now opened explicitly and all emitted messages are buffered until this open occurs. The version message and log cut have also been moved to the earliest possible sensible location.
1 parent 086cb33 commit 27d7605

File tree

3 files changed

+85
-28
lines changed

3 files changed

+85
-28
lines changed

src/init.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,9 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
667667
fLogTimestamps = GetBoolArg("-logtimestamps", true);
668668
fLogIPs = GetBoolArg("-logips", false);
669669

670+
LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
671+
LogPrintf("Bitcoin version %s (%s)\n", FormatFullVersion(), CLIENT_DATE);
672+
670673
// when specifying an explicit binding address, you want to listen on it
671674
// even when -connect or -proxy is specified
672675
if (mapArgs.count("-bind")) {
@@ -909,8 +912,10 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
909912
#endif
910913
if (GetBoolArg("-shrinkdebugfile", !fDebug))
911914
ShrinkDebugFile();
912-
LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
913-
LogPrintf("Bitcoin version %s (%s)\n", FormatFullVersion(), CLIENT_DATE);
915+
916+
if (fPrintToDebugLog)
917+
OpenDebugLog();
918+
914919
LogPrintf("Using OpenSSL version %s\n", SSLeay_version(SSLEAY_VERSION));
915920
#ifdef ENABLE_WALLET
916921
LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0));

src/util.cpp

Lines changed: 77 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -167,23 +167,51 @@ instance_of_cinit;
167167
*/
168168

169169
static boost::once_flag debugPrintInitFlag = BOOST_ONCE_INIT;
170+
170171
/**
171-
* We use boost::call_once() to make sure these are initialized
172-
* in a thread-safe manner the first time called:
172+
* We use boost::call_once() to make sure mutexDebugLog and
173+
* vMsgsBeforeOpenLog are initialized in a thread-safe manner.
174+
*
175+
* NOTE: fileout, mutexDebugLog and sometimes vMsgsBeforeOpenLog
176+
* are leaked on exit. This is ugly, but will be cleaned up by
177+
* the OS/libc. When the shutdown sequence is fully audited and
178+
* tested, explicit destruction of these objects can be implemented.
173179
*/
174180
static FILE* fileout = NULL;
175181
static boost::mutex* mutexDebugLog = NULL;
182+
static list<string> *vMsgsBeforeOpenLog;
183+
184+
static int FileWriteStr(const std::string &str, FILE *fp)
185+
{
186+
return fwrite(str.data(), 1, str.size(), fp);
187+
}
176188

177189
static void DebugPrintInit()
178190
{
179-
assert(fileout == NULL);
180191
assert(mutexDebugLog == NULL);
192+
mutexDebugLog = new boost::mutex();
193+
vMsgsBeforeOpenLog = new list<string>;
194+
}
195+
196+
void OpenDebugLog()
197+
{
198+
boost::call_once(&DebugPrintInit, debugPrintInitFlag);
199+
boost::mutex::scoped_lock scoped_lock(*mutexDebugLog);
181200

201+
assert(fileout == NULL);
202+
assert(vMsgsBeforeOpenLog);
182203
boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
183204
fileout = fopen(pathDebug.string().c_str(), "a");
184205
if (fileout) setbuf(fileout, NULL); // unbuffered
185206

186-
mutexDebugLog = new boost::mutex();
207+
// dump buffered messages from before we opened the log
208+
while (!vMsgsBeforeOpenLog->empty()) {
209+
FileWriteStr(vMsgsBeforeOpenLog->front(), fileout);
210+
vMsgsBeforeOpenLog->pop_front();
211+
}
212+
213+
delete vMsgsBeforeOpenLog;
214+
vMsgsBeforeOpenLog = NULL;
187215
}
188216

189217
bool LogAcceptCategory(const char* category)
@@ -215,44 +243,67 @@ bool LogAcceptCategory(const char* category)
215243
return true;
216244
}
217245

246+
/**
247+
* fStartedNewLine is a state variable held by the calling context that will
248+
* suppress printing of the timestamp when multiple calls are made that don't
249+
* end in a newline. Initialize it to true, and hold it, in the calling context.
250+
*/
251+
static std::string LogTimestampStr(const std::string &str, bool *fStartedNewLine)
252+
{
253+
string strStamped;
254+
255+
if (!fLogTimestamps)
256+
return str;
257+
258+
if (*fStartedNewLine)
259+
strStamped = DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime()) + ' ' + str;
260+
else
261+
strStamped = str;
262+
263+
if (!str.empty() && str[str.size()-1] == '\n')
264+
*fStartedNewLine = true;
265+
else
266+
*fStartedNewLine = false;
267+
268+
return strStamped;
269+
}
270+
218271
int LogPrintStr(const std::string &str)
219272
{
220273
int ret = 0; // Returns total number of characters written
274+
static bool fStartedNewLine = true;
221275
if (fPrintToConsole)
222276
{
223277
// print to console
224278
ret = fwrite(str.data(), 1, str.size(), stdout);
225279
fflush(stdout);
226280
}
227-
else if (fPrintToDebugLog && AreBaseParamsConfigured())
281+
else if (fPrintToDebugLog)
228282
{
229-
static bool fStartedNewLine = true;
230283
boost::call_once(&DebugPrintInit, debugPrintInitFlag);
231-
232-
if (fileout == NULL)
233-
return ret;
234-
235284
boost::mutex::scoped_lock scoped_lock(*mutexDebugLog);
236285

237-
// reopen the log file, if requested
238-
if (fReopenDebugLog) {
239-
fReopenDebugLog = false;
240-
boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
241-
if (freopen(pathDebug.string().c_str(),"a",fileout) != NULL)
242-
setbuf(fileout, NULL); // unbuffered
243-
}
286+
string strTimestamped = LogTimestampStr(str, &fStartedNewLine);
244287

245-
// Debug print useful for profiling
246-
if (fLogTimestamps && fStartedNewLine)
247-
ret += fprintf(fileout, "%s ", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", GetTime()).c_str());
248-
if (!str.empty() && str[str.size()-1] == '\n')
249-
fStartedNewLine = true;
288+
// buffer if we haven't opened the log yet
289+
if (fileout == NULL) {
290+
assert(vMsgsBeforeOpenLog);
291+
ret = strTimestamped.length();
292+
vMsgsBeforeOpenLog->push_back(strTimestamped);
293+
}
250294
else
251-
fStartedNewLine = false;
252-
253-
ret = fwrite(str.data(), 1, str.size(), fileout);
295+
{
296+
// reopen the log file, if requested
297+
if (fReopenDebugLog) {
298+
fReopenDebugLog = false;
299+
boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
300+
if (freopen(pathDebug.string().c_str(),"a",fileout) != NULL)
301+
setbuf(fileout, NULL); // unbuffered
302+
}
303+
304+
ret = FileWriteStr(strTimestamped, fileout);
305+
}
254306
}
255-
256307
return ret;
257308
}
258309

src/util.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ void ReadConfigFile(std::map<std::string, std::string>& mapSettingsRet, std::map
125125
boost::filesystem::path GetSpecialFolderPath(int nFolder, bool fCreate = true);
126126
#endif
127127
boost::filesystem::path GetTempPath();
128+
void OpenDebugLog();
128129
void ShrinkDebugFile();
129130
void runCommand(const std::string& strCommand);
130131

0 commit comments

Comments
 (0)