Skip to content

Rotate the audit log by size (#161) - #163

Merged
jcombs-pointblue merged 1 commit into
mainfrom
audit-rotation
Oct 23, 2026
Merged

jcombs-pointblue merged 1 commit into
mainfrom
audit-rotation

Conversation

@jcombs-pointblue

Copy link
Copy Markdown
Contributor

Closes #161.

Rotation:

  • When: before a line would take audit.log past audit.maxBytes (default 10MB; KB, MB and GB accepted), the file moves to audit.log.1 and the older ones move up by one.
  • How many are kept: files past audit.keep (default 10) are deleted, so the log takes at most about 110 MB.
  • Opting out: audit.keep=0 deletes nothing; audit.maxBytes=0 never rotates (the old behaviour).
  • Lines stay whole: a line is never split between files.
  • Lowering audit.keep: the extra files go at the next rotation.
  • Upgrading: an existing large audit.log is rotated on the first line written after the upgrade. Nothing is deleted at that point.

Reading: Audit.read() (the Audit view, GET /api/audit) now goes newest file first, reading each file backwards from its end in 64 KB blocks. It stops at limit or since. Before, it read the whole log with readAllLines on every request.

Fixed along the way: the since filter compared times as text. Instant.toString() writes 3, 6 or 9 fraction digits, so .123456Z sorted before .1234Z, which could end the list too early. The new concurrent-writers test caught it.

  • Now: times are compared as instants.
  • Partly typed times: the page's filter box sends a time as you type, which the old text comparison treated as a prefix. A partial time like 2026-10-07 or 2026-10-07T09:30 now means the start of that period, in UTC. The box's placeholder says UTC.
  • Unreadable values: something that doesn't read as a time is ignored.

Docs:

  • hosted-mode.md: the two keys, plus a short "The audit log" section.
  • hosted-install.md: ship the numbered files; use audit.keep=0 if nothing may be deleted.
  • security.md: the audit row.
  • CHANGELOG: listed as a change in default behaviour.

Tests:

  • New AuditTest (8 tests):
    • rotation at the threshold, with every line whole;
    • keep (including 0, and lowering it later);
    • maxBytes=0;
    • reading across files with limit, since and filters;
    • mixed 3/6-digit times;
    • partial since values;
    • backward reading across blocks with long and non-ASCII lines;
    • 8 concurrent writers across rotations with no line lost;
    • size units.
  • bin/smoke-hosted.sh runs the server with audit.maxBytes=2KB and audit.keep=0:
    • it checks the server really rotates, every file stays under 2 KB and every line is whole;
    • it checks /api/audit reads past the current file;
    • the end-of-run audit check now reads all the files;
    • a newest-first check that compared times as text now compares them as times.
  • Results: vitest (54), mvn package and bin/smoke.sh pass; bin/smoke-hosted.sh passes with the new checks.

🤖 Generated with Claude Code

audit.log moves to audit.log.1 before a line would pass audit.maxBytes (default 10MB), older
files up by one, none kept past audit.keep (default 10; 0 keeps all, maxBytes 0 never rotates).
A line is never split. read() goes newest file first, each from its end in blocks, and stops at
the limit or since, instead of reading the whole log per request.

Also fixes since: times have 3, 6 or 9 fraction digits, so comparing them as text could end the
list too early; they compare as instants now, and a partly typed time is the start of that period.

Closes #161

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jcombs-pointblue
jcombs-pointblue merged commit 137aba0 into main Oct 23, 2026
2 checks passed
@jcombs-pointblue
jcombs-pointblue deleted the audit-rotation branch October 23, 2026 18:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rotate the audit log

1 participant