fix(log): auto-rotate the other 3 internal log files (debug, rot, doodstream)

3.3.2 fixed fileuploader.log unbounded growth, but three siblings kept
growing without limit:

- upload-debug.log     (verbose, every IPC + progress event log line)
- account-rotation.log (every rotation decision)
- doodstream-debug.log (per-hoster trace from lib/doodstream-upload.js)

A multi-month dev install or a heavy production user could fill the
log dir with multi-GB files and slow every appendFile.

Wire all three through the same lib/log-rotation.js helper:
- upload-debug.log     → 25 MB cap, 2 numbered backups (~75 MB worst)
- account-rotation.log → 10 MB cap, 2 numbered backups (~30 MB worst)
- doodstream-debug.log → 10 MB cap, 1 numbered backup  (~20 MB worst)

The rotation check runs once per flush call (each is debounced or
already a once-per-event path), so the statSync overhead is
microscopic. _flushDebugLog passes a noop logger to avoid recursing
into itself; _flushRotLog and _debugLog (doodstream) use the normal
debugLog so any rotation surprises end up in upload-debug.log.

126/126 tests still green.
This commit is contained in:
Administrator 2026-04-28 11:11:24 +02:00
parent 6e68748ca0
commit b1fe0cfefb
3 changed files with 26 additions and 2 deletions

View File

@ -7,10 +7,18 @@ const BASE_URL = 'https://doodstream.com';
const USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'; const USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36';
const UPLOAD_TIMEOUT = 1800000; // 30 min const UPLOAD_TIMEOUT = 1800000; // 30 min
// Cap doodstream's per-hoster debug log alongside the main log files so
// dev-mode sessions don't accumulate gigabytes of upload trace.
const { maybeRotateLogFile } = require('./log-rotation');
const _DOODSTREAM_LOG_PATH = path.join(__dirname, '..', 'doodstream-debug.log');
const _DOODSTREAM_LOG_MAX_BYTES = 10 * 1024 * 1024;
const _DOODSTREAM_LOG_MAX_BACKUPS = 1;
function _debugLog(msg) { function _debugLog(msg) {
try { try {
maybeRotateLogFile(_DOODSTREAM_LOG_PATH, _DOODSTREAM_LOG_MAX_BYTES, _DOODSTREAM_LOG_MAX_BACKUPS);
const ts = new Date().toISOString(); const ts = new Date().toISOString();
fs.appendFileSync(path.join(__dirname, '..', 'doodstream-debug.log'), `[${ts}] ${msg}\n`); fs.appendFileSync(_DOODSTREAM_LOG_PATH, `[${ts}] ${msg}\n`);
} catch {} } catch {}
} }

17
main.js
View File

@ -76,12 +76,24 @@ const _debugLogBuffer = [];
let _debugLogFlushTimer = null; let _debugLogFlushTimer = null;
let _debugLogWriting = false; let _debugLogWriting = false;
// 25 MB cap for upload-debug.log + 10 MB for account-rotation.log. Each
// keeps 2 numbered backups, so the on-disk worst case is bounded:
// upload-debug ~75 MB, account-rotation ~30 MB. Reuses the same
// lib/log-rotation.js helper that fileuploader.log already uses.
const DEBUG_LOG_MAX_BYTES = 25 * 1024 * 1024;
const ROT_LOG_MAX_BYTES = 10 * 1024 * 1024;
const INTERNAL_LOG_MAX_BACKUPS = 2;
function _flushDebugLog() { function _flushDebugLog() {
if (_debugLogWriting || _debugLogBuffer.length === 0) return; if (_debugLogWriting || _debugLogBuffer.length === 0) return;
const chunk = _debugLogBuffer.join(''); const chunk = _debugLogBuffer.join('');
_debugLogBuffer.length = 0; _debugLogBuffer.length = 0;
_debugLogWriting = true; _debugLogWriting = true;
fs.appendFile(getDebugLogPath(), chunk, 'utf-8', () => { const target = getDebugLogPath();
// Pass a noop logger here — debugLog() is THIS file's writer, recursing
// into it would deadlock the buffer/timer state.
maybeRotateLogFile(target, DEBUG_LOG_MAX_BYTES, INTERNAL_LOG_MAX_BACKUPS, () => {});
fs.appendFile(target, chunk, 'utf-8', () => {
_debugLogWriting = false; _debugLogWriting = false;
// If more lines arrived during the write, flush them next tick. // If more lines arrived during the write, flush them next tick.
if (_debugLogBuffer.length) setImmediate(_flushDebugLog); if (_debugLogBuffer.length) setImmediate(_flushDebugLog);
@ -128,6 +140,9 @@ function _flushRotLog() {
try { try {
fs.mkdirSync(path.dirname(tryTargets[i]), { recursive: true }); fs.mkdirSync(path.dirname(tryTargets[i]), { recursive: true });
} catch {} } catch {}
// Cap account-rotation.log so a long-running install can't keep
// growing it indefinitely (rotation events fire on every account-fail).
maybeRotateLogFile(tryTargets[i], ROT_LOG_MAX_BYTES, INTERNAL_LOG_MAX_BACKUPS, debugLog);
fs.appendFile(tryTargets[i], chunk, 'utf-8', (err) => { fs.appendFile(tryTargets[i], chunk, 'utf-8', (err) => {
if (err) return write(i + 1); if (err) return write(i + 1);
_rotLogWriting = false; _rotLogWriting = false;

View File

@ -17,6 +17,7 @@
- ✅ 3.3.13 — `save-global-settings-sync` reportet jetzt `returnValue=false` bei Fehlern + debugLog statt silent swallow; TOCTOU bei .bak-Refresh in beiden Pfaden (main.js + lib/config-store.js _atomicWrite) entkoppelt: bak-Read-Failure failt nicht mehr den ganzen Save (deep-audit findings HIGH-2 + MED-4) - ✅ 3.3.13 — `save-global-settings-sync` reportet jetzt `returnValue=false` bei Fehlern + debugLog statt silent swallow; TOCTOU bei .bak-Refresh in beiden Pfaden (main.js + lib/config-store.js _atomicWrite) entkoppelt: bak-Read-Failure failt nicht mehr den ganzen Save (deep-audit findings HIGH-2 + MED-4)
- ✅ 3.3.14 — Parser-null-payload guard: `uploadFile` normalisiert payload zu `{}` falls `JSON.parse('null')` o.ä.; `parseDoodstreamResult` + `parseByseResult` haben defensive guards für direct callers + 7 neue Unit-Tests (null/non-object, malformed entries, fileRejected/accountError flips, valid filecode happy path) - ✅ 3.3.14 — Parser-null-payload guard: `uploadFile` normalisiert payload zu `{}` falls `JSON.parse('null')` o.ä.; `parseDoodstreamResult` + `parseByseResult` haben defensive guards für direct callers + 7 neue Unit-Tests (null/non-object, malformed entries, fileRejected/accountError flips, valid filecode happy path)
- ✅ 3.3.15 — Cancellation latency fix: nach `_sleep(800)` in der rotation-while-loop wird `signal.aborted`/`stopAfterActive` re-checkt bevor das ganze override-resolution-Setup läuft (deep-audit MED-5) - ✅ 3.3.15 — Cancellation latency fix: nach `_sleep(800)` in der rotation-while-loop wird `signal.aborted`/`stopAfterActive` re-checkt bevor das ganze override-resolution-Setup läuft (deep-audit MED-5)
- ✅ 3.3.16 — Auto-Rotation für die anderen 3 internen Logs (`upload-debug.log` 25 MB, `account-rotation.log` 10 MB, `doodstream-debug.log` 10 MB), je 2 Backups — alle nutzen `lib/log-rotation.js` (zuvor nur `fileuploader.log` rotiert)
## Open items (priorisiert) ## Open items (priorisiert)