From ae1c6a7b49d9b4f8cf7218c0baec84f348eb5ef6 Mon Sep 17 00:00:00 2001 From: bitflash-maintainer Date: Wed, 2 Sep 2026 05:26:51 +0000 Subject: log: cap debug.log in-process, and ship a logrotate policy debug.log grew without bound (production: 1.95 GiB, 51-129 MB/day). Nothing shrank it once the process was up -- there is no ShrinkDebugFile in this tree, and upstream's only ran at startup. OutputDebugStringF now rolls the file to debug.log.old when it passes -maxdebugfilesize (MB, default 1024, 0 disables). The write path already opens and closes the file per line under one lock, so the rename-and-reopen is a few lines inside that lock and no fd can be stranded mid-roll. contrib/logrotate/bitflash-debug.logrotate is the longer-term policy (size 1G, rotate 4, compress, missingok, notifempty, copytruncate); copytruncate keeps the writer's append handle valid across rotation. Closes #34 --- src/main_gui.cpp | 15 +++++++++++++++ src/util.cpp | 5 +++++ src/util.h | 29 +++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+) (limited to 'src') diff --git a/src/main_gui.cpp b/src/main_gui.cpp index 211e1e2..05f5ecf 100644 --- a/src/main_gui.cpp +++ b/src/main_gui.cpp @@ -111,6 +111,7 @@ static void PrintUsage() printf(" /help, -help, --help, /?\n"); printf(" /datadir=PATH\n"); printf(" /debug\n"); + printf(" /maxdebugfilesize=N (roll debug.log at N MB, default 1024; 0 = never)\n"); printf(" /gen\n"); printf(" /nogui or /daemon\n"); printf(" /selftest=wallet-keypool, wallet-hd, wallet-format, wallet-crypto, wallet-encrypt,\n"); @@ -191,6 +192,20 @@ static void ParseStartupArguments(int argc, char* argv[]) if (arg(argc,argv,"/debug") || arg(argc,argv,"-debug")) fDebug = true; + // /maxdebugfilesize=N -- roll debug.log to debug.log.old once it passes + // N MB, checked on each write. Default is 1024 (see util.cpp); 0 turns + // the check off and leaves rotation entirely to logrotate. + string strMaxDebugSize = argval2(argc, argv, "/maxdebugfilesize", "-maxdebugfilesize"); + if (!strMaxDebugSize.empty()) + { + int64 nMB = atoi64(strMaxDebugSize.c_str()); + if (nMB < 0) + fprintf(stderr, "Ignoring /maxdebugfilesize=%s: not a size in MB\n", + strMaxDebugSize.c_str()); + else + nMaxDebugFileSize = nMB * 1024 * 1024; + } + if (arg(argc,argv,"/gen") || arg(argc,argv,"-gen")) { fGenerateBitcoins = 1; diff --git a/src/util.cpp b/src/util.cpp index 0cd0133..52cda84 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -8,6 +8,11 @@ bool fDebug = false; +// In-process cap for debug.log, in bytes. Default matches the 1G trigger of +// the shipped logrotate policy so a long-running node stays bounded even +// without it; -maxdebugfilesize=N (MB) tunes it, 0 disables. +int64 nMaxDebugFileSize = 1024 * 1024 * (int64)1024; + // --------------------------------------------------------------------------- // Categorized, burst-collapsing logging -- see the LogPrint doc comment in // util.h for the problem this solves. diff --git a/src/util.h b/src/util.h index d72518b..f2ea23f 100644 --- a/src/util.h +++ b/src/util.h @@ -87,6 +87,11 @@ inline T& REF(const T& val) extern bool fDebug; +// debug.log is rolled to debug.log.old once it grows past this many bytes +// (set from -maxdebugfilesize, in MB; 0 disables the check). See the roll +// site in OutputDebugStringF() below. +extern int64 nMaxDebugFileSize; + // --------------------------------------------------------------------------- // Categorized, burst-collapsing logging // @@ -391,6 +396,30 @@ inline int OutputDebugStringF(const char* pszFormat, ...) strDebugFile = "debug.log"; } FILE* fileout = fopen(strDebugFile.c_str(), "a"); + if (fileout && nMaxDebugFileSize > 0) + { + // Roll an overgrown debug.log before adding to it. Nothing in the + // tree shrinks this file once the process is running (there is no + // ShrinkDebugFile, and even Bitcoin's only ran at startup), so a + // node that stays up for months grows the log without bound -- + // production hit ~2 GiB. The file is opened and closed per line, + // so a rename is safe here: the next line simply reopens the + // path. One generation is kept as debug.log.old; logrotate + // (contrib/logrotate/) handles anything longer-term. + int nSize = GetFilesize(fileout); + if (nSize >= 0 && (int64)nSize > nMaxDebugFileSize) + { + fclose(fileout); + string strOldFile = strDebugFile + ".old"; + remove(strOldFile.c_str()); // rename() won't clobber on Windows + rename(strDebugFile.c_str(), strOldFile.c_str()); + fileout = fopen(strDebugFile.c_str(), "a"); + if (fileout) + fprintf(fileout, + "debug.log grew past %lld MB; previous contents moved to debug.log.old (-maxdebugfilesize)\n", + (long long)(nMaxDebugFileSize / 1024 / 1024)); + } + } if (fileout) { va_list arg_ptr; -- cgit v1.2.3