1 Commits
Author SHA1 Message Date
clawbot 5d0ae5989e Log fx through slog, snake_case health check keys (closes #27)
check / check (push) Successful in 3m40s
fx wrote its own steps of starting and stopping as plain text to
stderr. It now logs them with its own slog event logger through the
backend's logger, so off a terminal every line from start to stop is
JSON. A config file that is found but cannot be read now makes
config.New return the error instead of panicking. A test runs the
server as a child process and checks that all it writes is JSON, on a
normal start and stop and with such a config file. The backend logs
its name, version and architecture once at start.

The health check's uptime keys are now uptime_seconds and
uptime_human, and its type and method take the names
GO_HTTP_SERVER_CONVENTIONS.md gives. Its path, content type, status
and code are unchanged.

Model: opus-5-5
2026-10-03 23:59:50 +00:00
2 changed files with 15 additions and 40 deletions
+9 -10
View File
@@ -25,16 +25,15 @@ latest run passes.
- 2026-10-03: the backend's logs are one stream (issue #27): fx logs its own
steps of starting and stopping through the backend's logger, so off a terminal
every line the backend's own logger and fx write is JSON, where fx used to
write plain text to stderr. A config file that is found but cannot be read now
stops the start with its error, logged as JSON like a bad setting, where it
used to end in a Go panic. A test runs the server as a child process and
checks both. The backend logs its name, version and architecture once at
start. The health check's uptime keys are now `uptime_seconds` and
`uptime_human`; its path, content type, `"status":"ok"` and 200 are unchanged.
`SENTRY_DSN`, `METRICS_USERNAME` and `METRICS_PASSWORD` are still read and
still unused, and left out of `backend/README.md`, until issues #94 and #95
wire them up
every line from start to stop is JSON, where fx used to write plain text to
stderr. A config file that is found but cannot be read now stops the start
with its error, logged as JSON like a bad setting, where it used to end in a
Go panic. A test runs the server as a child process and checks both. The
backend logs its name, version and architecture once at start. The health
check's uptime keys are now `uptime_seconds` and `uptime_human`; its path,
content type, `"status":"ok"` and 200 are unchanged. `SENTRY_DSN`,
`METRICS_USERNAME` and `METRICS_PASSWORD` are still read and still unused, and
left out of `backend/README.md`, until issues #94 and #95 wire them up
- 2026-10-03: the frontend has a real linter (issue #47, and item 2 of issue
#28): `eslint` with its recommended rules, set in `eslint.config.js`, runs in
a new `frontend-lint` stage of `Dockerfile`, which the frontend stage waits
+6 -30
View File
@@ -8,7 +8,6 @@ import (
"net/http"
"os"
"os/exec"
"os/signal"
"path/filepath"
"strings"
"syscall"
@@ -26,8 +25,6 @@ const childTimeout = 10 * time.Second
func TestMain(m *testing.M) {
if os.Getenv(runMainEnv) != "" {
// A SIGTERM that comes before fx catches it is dropped, not fatal.
signal.Notify(make(chan os.Signal, 1), syscall.SIGTERM)
main()
return
@@ -49,25 +46,12 @@ func TestOutputIsJSON(t *testing.T) {
waitForHealthcheck(ctx, t, port)
// The child drops a SIGTERM that comes before fx catches it (see
// TestMain), so send one every 100ms until the test ends. ctx
// bounds the wait: when it ends, the child is killed.
stop := make(chan struct{})
defer close(stop)
err := child.Process.Signal(syscall.SIGTERM)
if err != nil {
t.Fatal(err)
}
go func() {
for {
_ = child.Process.Signal(syscall.SIGTERM)
select {
case <-stop:
return
case <-time.After(100 * time.Millisecond):
}
}
}()
err := child.Wait()
err = child.Wait()
if err != nil {
t.Fatalf("server exit = %v, want success", err)
}
@@ -119,8 +103,7 @@ func TestMalformedConfigFileStopsTheStart(t *testing.T) {
// 127.0.0.1:port, with home as its HOME and working directory and its
// data directory in home, so it touches nothing outside home. Its
// stdout and stderr go to the two buffers returned, which hold all of
// it once child.Wait returns. The child is killed when ctx ends, and
// killed and reaped when the test ends if nothing waited for it.
// it once child.Wait returns. The child is killed when ctx ends.
func startServer(
ctx context.Context,
t *testing.T,
@@ -152,13 +135,6 @@ func startServer(
t.Fatal(err)
}
t.Cleanup(func() {
if child.ProcessState == nil {
_ = child.Process.Kill()
_ = child.Wait()
}
})
return child, &stdout, &stderr
}