feat(log): auto-rotate fileuploader.log at 50 MB

A long-running install can otherwise grow the upload log into the
gigabyte range, eating disk and slowing every appendFile. Add a
size-checked rotation right before each flush:

- statSync the resolved log target (cheap, ENOENT skips silently).
- If size exceeds 50 MB, drop the oldest backup (.3), shift .2→.3
  and .1→.2, then rename the live file to .1 and let appendFile
  create a fresh primary on the next call.
- Max 3 backups (~200 MB worst case, bounded). debugLog records
  each rotation for diagnostics.
- Pure additive: skips when file is small or doesn't exist; no
  effect on the daily-log mode (already date-rotated).
This commit is contained in:
Administrator 2026-04-28 03:40:06 +02:00
parent 3487bc8fcf
commit d96c6afce0
2 changed files with 39 additions and 2 deletions

37
main.js
View File

@ -296,6 +296,40 @@ function _resolveUploadLogTarget() {
} }
} }
// Cap the upload log file size. Beyond this we rotate to .1 (and shift
// older numbered backups up) so a multi-month-running install can't fill
// the disk. 50 MB ≈ ~600k log lines, plenty for human inspection.
const UPLOAD_LOG_MAX_BYTES = 50 * 1024 * 1024;
const UPLOAD_LOG_MAX_BACKUPS = 3;
function _maybeRotateUploadLog(filePath) {
let size = 0;
try {
const st = fs.statSync(filePath);
size = st.size;
} catch (err) {
// ENOENT is the common case (file doesn't exist yet) — nothing to rotate.
if (err && err.code !== 'ENOENT') {
debugLog(`uploadLog stat for rotation failed: ${err.message}`);
}
return;
}
if (size <= UPLOAD_LOG_MAX_BYTES) return;
const ext = path.extname(filePath);
const base = filePath.slice(0, filePath.length - ext.length);
// Drop the oldest backup if it exists, then shift each numbered backup up.
try { fs.unlinkSync(`${base}.${UPLOAD_LOG_MAX_BACKUPS}${ext}`); } catch {}
for (let i = UPLOAD_LOG_MAX_BACKUPS - 1; i >= 1; i--) {
try { fs.renameSync(`${base}.${i}${ext}`, `${base}.${i + 1}${ext}`); } catch {}
}
try {
fs.renameSync(filePath, `${base}.1${ext}`);
debugLog(`uploadLog rotated (${(size / 1024 / 1024).toFixed(1)} MB) → ${base}.1${ext}`);
} catch (err) {
debugLog(`uploadLog rotation rename failed: ${err.message}`);
}
}
function _flushUploadLog() { function _flushUploadLog() {
if (_uploadLogWriting || _uploadLogBuffer.length === 0) return; if (_uploadLogWriting || _uploadLogBuffer.length === 0) return;
const target = _resolveUploadLogTarget(); const target = _resolveUploadLogTarget();
@ -305,6 +339,9 @@ function _flushUploadLog() {
// the dir already exists; recreates it otherwise. Without this, every // the dir already exists; recreates it otherwise. Without this, every
// subsequent flush silently fails with ENOENT and entries are lost. // subsequent flush silently fails with ENOENT and entries are lost.
try { fs.mkdirSync(path.dirname(target.path), { recursive: true }); } catch {} try { fs.mkdirSync(path.dirname(target.path), { recursive: true }); } catch {}
// Cheap size check + rotation right before the append, so we never grow
// a single log file beyond the cap regardless of session length.
_maybeRotateUploadLog(target.path);
const chunk = _uploadLogBuffer.join(''); const chunk = _uploadLogBuffer.join('');
_uploadLogBuffer.length = 0; _uploadLogBuffer.length = 0;
_uploadLogWriting = true; _uploadLogWriting = true;

View File

@ -2,12 +2,12 @@
## Released ## Released
- ✅ 3.3.0 — Performance-Fixes (queue-cap, sort-throttle, history-delegation, recent-cap) + Log-Recovery - ✅ 3.3.0 — Performance-Fixes (queue-cap, sort-throttle, history-delegation, recent-cap) + Log-Recovery
- ✅ 3.3.1 — `removeFromQueueOnDone` coalesced via microtask (kein O(N²) mehr bei done-Bursts)
- ✅ 3.3.2 — `fileuploader.log` Auto-Rotation bei 50 MB (max 3 Backups: .1 .2 .3)
## Open items (priorisiert) ## Open items (priorisiert)
### Stabilität ### Stabilität
- [ ] **removeFromQueueOnDone O(N²)** in `handleProgress` (renderer/app.js) — bei 500 gleichzeitig fertig werdenden Jobs 500× ein O(N)-`queueJobs.filter()` aufgerufen. Coalesce über microtask + Set.
- [ ] **fileuploader.log wächst unbounded** — automatische Rotation bei >50MB (rename → .1, neue Datei).
- [ ] **`_jobLogCollector`** (main.js) — wird nur bei start-upload geleert, nicht bei batch-done. Bei vielen Batches ohne neuen start-upload wächst es. Cleanup bei batch-done für jobs die nicht mehr in queueJobs sind. - [ ] **`_jobLogCollector`** (main.js) — wird nur bei start-upload geleert, nicht bei batch-done. Bei vielen Batches ohne neuen start-upload wächst es. Cleanup bei batch-done für jobs die nicht mehr in queueJobs sind.
### Performance ### Performance