The webhook handler's client had no timeout, and slog calls the handler
inside the log call, so a server that accepted the connection and never
answered stopped that log call for good, and every later one. A request
still running after 5 seconds, reading the answer included, now fails
with a timeout error. The handler also reads the answer to the end
before closing it, so the connection is reused for the next record.
Two new tests point the handler at a server that never answers and at
one that sends its status and then stalls the answer, and check that
Handle returns a timeout error within the timeout. The README states
the timeout.
Model: opus-5-5
script/test is now the standard script: it builds only the test stage
of the Dockerfile, without the build cache, so tests no longer run on
the host. That stage calls go test -race -cover directly, with a
verbose rerun on failure, on the Debian-based golang image, which has
the C toolchain -race needs.
The test stage no longer copies from the lint stage, so script/test
builds the tests alone. A new final stage copies one file from each of
the two stages; that is what makes a plain docker build, and so
script/cibuild, run both. The README entry for script/test and TODO.md
say what now runs.
Model: opus-5-5
script/lint now builds only the lint stage of the Dockerfile, without
the build cache, so every run executes the linter; the image is tagged
simplelog-lint. The lint stage calls golangci-lint directly, since make
lint is itself a docker build of that stage. script/cibuild and
script/docker also build without the cache, so their check steps always
run. script/fmt no longer runs golangci-lint --fix, and script/bootstrap
no longer installs it. golangci-lint config verify is left out, on the
owner's ruling. The README, TODO.md and the script/cibuild comment say
what now runs.
Model: opus-5-5
The console and JSON handlers threw away the error from their write to
stdout, so a lost log line looked delivered. They now return it,
wrapped. The webhook handler also returns an error for a status outside
2xx, and no longer follows redirects, which could resend the request
without the record. The multiplex handler passes the record to every
handler and returns their errors joined with errors.Join instead of
stopping at the first.
Both stdout handlers gain an unexported writer, nil meaning os.Stdout at
write time, so tests can supply a sink that fails. The README says what
each handler returns and that the slog.Logger methods discard a
handler's error.
Model: opus-5-5
Merges main into next as a two-parent merge, so main becomes an ancestor
of next and the later merge of next into main does not conflict. This
brings in golangci-lint v2.12.2 with .golangci.yml, and the fix that
makes every handler emit slog attributes.
README.md and TODO.md conflicted. README.md keeps main's Attribute
output section, followed by next's Entrypoints section. TODO.md keeps
the completed steps from both sides, newest first. The other files
merged cleanly: the Makefile is still next's script/ shims, and the
Dockerfile has main's v2.12.2 lint image.
Model: opus-5-5
Handle never read the record's attributes, and WithAttrs and WithGroup
returned the receiver unchanged. The JSON and webhook handlers also marshaled
slog.Record itself, whose attributes are unexported.
Each handler now carries a handlerAttrs value, copied rather than mutated, so
sibling loggers cannot leak attributes into each other. JSON and webhook
output nest groups as objects; the console appends key=value pairs with dotted
group keys, quoted as slog.NewTextHandler quotes them, invalid UTF-8
included. Logged values are never written to. In JSON the record's own fields
win a key collision, and a repeated key keeps its last value unless both are
groups, which merge; the README documents both.
Deviation: DEL is quoted on the console; the stdlib leaves it bare.
Model: opus-5-5
Add the canonical .golangci.yml (v2 schema, all linters enabled with a
small documented disable list) and pin the Dockerfile lint stage to
golangci/golangci-lint:v2.12.2 by tag and digest, replacing the old
v1.64.8 digest-only pin.
Fix all findings surfaced by the v1 to v2 jump without changing any
exported signatures or behavior:
- add package and exported-symbol doc comments (revive)
- rename unused handler parameters to underscore (revive)
- check or explicitly discard error returns (errcheck, errchkjson)
- wrap errors with %w instead of %v (err113)
- use http.NewRequestWithContext instead of http.Post (noctx)
- replace fmt.Println with fmt.Fprintln(os.Stdout, ...) (forbidigo)
- name magic numbers as constants (mnd)
- add explicit slog.LevelDebug case (exhaustive)
- interface{} to any (modernize)
- move tests to the simplelog_test package (testpackage) and add
t.Parallel() (paralleltest)
- move NewWebhookHandler above its methods (funcorder)
- whitespace, line-length, and blank-line fixes (wsl_v5, whitespace,
nlreturn, lll, embeddedstructfieldcheck)
- nolint with justification for the intentional init/global design
(gochecknoinits, gochecknoglobals) and interface-returning
constructor (ireturn)