Files
bdeshiandClaude Opus 5 c3125e7ad2 log every request, and make error mean something again
Item 1 of the order of work, and two gaps rather than one polish item.

There was no access log. Nothing recorded that a request happened, and the
Dockerfile ships the binary alone with the site root mounted (ADR-0010), so a bare
deployment produced none at all — which mattered because offline log analysis is
this project's answer to analytics: no counter on the read path, no third-party
script. web.Logged now writes one Info line per request with method, path, status,
bytes and duration.

And slog was never configured. conventions.md makes importing `log` instead of
log/slog a hard failure while nothing ever set a level, a handler or a format.
Two flags now do, rejected at startup if unusable, because a logger quietly less
verbose than asked for hides exactly the lines somebody changed the flag to see.
JSON is the half that matters: it is what makes a log parseable.

The re-levelling was the larger half. Counts were 38 error, 7 warn, 4 info, 0
debug; they are now 7, 40, 6, 0. Almost every one of those errors was ADR-0029's
"logged, not fatal" category — a misspelled directive, an asset path climbing out
of its bundle, an unreadable picture — where the engine coped and the reader still
got a good page. Error used as "somebody should see this" means an operator cannot
tell a broken build from a typo. The seven that remain are the five requests that
answer 500 and the two inside fatal.

A successful rebuild now says so. It swapped silently before, so an operator could
see a failed rebuild and never a successful one, which leaves the failures with
nothing to be read against.

No wrapper package: log/slog is the module, and a layer over it would be an
abstraction with one caller. Duration comes from content.Now, since the clock is
confined to one file and verify.sh enforces it by filename. The recorder does not
forward Flusher or ReaderFrom — nothing here streams, so the cost is one io.Copy
fast path on static files, and implementing interfaces no caller needs is the
speculation rule 6 forbids.

Two things this change owed and paid. content.go's comment still said content
problems were "logged at error level", which the re-levelling made false. And the
new flags pushed runServe past the function-length advisory, so the site-opening
block became opened() — a warning that fires on correct code gets acted on, not
tolerated, and that is the whole reason the advisory exists.

Evidence, demo site, JSON: rebuilt bundles=31, then serving, then one request line
each for a 200, a 404 and robots.txt with real byte counts and durations. At
-log-level warn, request lines disappear. An invalid level exits with the reason.

12 files. Core 2913 → 2959 of 3400.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 16:35:29 +06:00
..
2026-07-31 02:40:03 +06:00