TASK 55 slice 1: the startup log is size-capped
The creator (2026-10-09): 'we need some sort of log-rotation so that we don't inadvertently fill up a user's drive.' startup.log was uncapped - 23MB and growing every few seconds, because FfmpegEncoder logs every non-progress ffmpeg stderr line. The policy lives in a new Helpers/LogRotation.cs as a pure function over a path (RollIfNeeded(logPath, maxBytes, retainedFiles)), so AppLog's 156 call sites keep their surface and the rule is testable on its own. At 5MB the live file rolls to startup.1.log and one rolled copy is kept - the FILE SET is bounded, not just the live file, so a long stream cannot grow the log without limit. A failed rotation degrades to the old behaviour instead of taking the app down. Pattern reference (cited): Serilog's file sink pairs fileSizeLimitBytes (the cap) with retainedFileCountLimit (how many rolled files to keep) - https://github.com/serilog/serilog-sinks-file Guard: ytLive.Tests/AppLogRotationTests.cs - the ONE test drives the real AppLog.Write against a temp dir (two internal seams: PathOverride, MaxBytes) and asserts the roll, the fresh live file, and that after a SECOND roll the directory still holds exactly startup.log + startup.1.log. Also fixed a stale catalog row: TASK 12 (master limiter) shipped 2026-08-15 and was still marked 'Queued'.
This commit is contained in:
@@ -2298,3 +2298,25 @@ so the honest report was "4 hits, all false positives", not "4 leaks".
|
||||
**Never print a matched value — but still say WHAT it is.** The report format that satisfies both:
|
||||
commit + path + line + rule + verdict. "README.md:79 | assigned-credential | placeholder" is
|
||||
actionable; the same report without the verdict forces the reader to go look at the secret.
|
||||
|
||||
## 2026-10-22 — CAP A LOGGER'S GROWTH WITHOUT TOUCHING ITS CALL SITES (RECIPE)
|
||||
|
||||
`AppLog` has **156 call sites** and one job; its log had no size cap (23 MB, growing every few
|
||||
seconds — ffmpeg logs every stderr line). The instinct is to add a cap parameter to the logger
|
||||
and change the callers. Don't.
|
||||
|
||||
**The recipe:**
|
||||
1. **Put the POLICY in a separate pure function over the path**, not in the logger:
|
||||
`LogRotation.RollIfNeeded(logPath, maxBytes, retainedFiles)`. The logger calls it before each
|
||||
append; its public surface (`AppLog.Write`) never changes, so no caller is touched.
|
||||
2. **Bound the FILE SET, not just the live file.** Rolling the live file alone (`startup.log` →
|
||||
`startup.1.log`) still grows forever if the rolled copy is never discarded. Shift the set:
|
||||
`name.1.log` → `name.2.log` → … → `name.N.log`, then the live file → `name.1.log`. Keep the
|
||||
rolled count explicit (`retainedFiles`) and assert the directory's file list in the test —
|
||||
that assertion is the one that proves the growth is bounded.
|
||||
3. **Test the logger, not just the helper** — otherwise the helper can be perfect and unwired.
|
||||
Two `internal` seams on the logger (`PathOverride`, `MaxBytes`) + the project's existing
|
||||
`InternalsVisibleTo("ytLive.Tests")` let the test drive the real `AppLog.Write` against a
|
||||
temp directory. Never let the test write to the real `%APPDATA%` log.
|
||||
4. **Never throw from the rotation**: a failed rotation must degrade to the old behaviour
|
||||
(the log keeps growing), never take the app down. Same rule as the logger itself.
|
||||
|
||||
Reference in New Issue
Block a user