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:
parent
3487bc8fcf
commit
d96c6afce0
37
main.js
37
main.js
@ -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;
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user