Logging: structured slog messages throughout, and WithLogger options so callers choose the logger

Log messages are lower-case event names with their details as key/value
attributes ("error" for errors), in place of formatted strings.

Loggable package take a WithLogger option as part of their "New*" call,
defaulting to slog.Default(). Each tags its lines with its `component`.

Some debug loggers print directly rather than using slog when in
terminal logging mode.
This commit is contained in:
bwilliams committed 2026-10-15 16:10:52 +01:00
1 parent 0b5fde9563
commit 043e307663
49 files changed
+598 -248

No files matched your search

+17 -8
View File
@@ -6,7 +6,7 @@ import (
"encoding/json"
"errors"
"fmt"
"log"
"log/slog"
"math/rand"
"os"
"path/filepath"
@@ -33,19 +33,27 @@ type Store struct {
// removing and picking one at random are all constant time.
open []string
openPos map[string]int
opts options
}
// OpenStore loads the releases stored at path, creating the file and its
// directory if needed, and opens it for appending. Lines that can't be read
// are skipped, releases are put back in date order if needed, and a record
// left incomplete by a crash is ended so new ones start on a fresh line.
func OpenStore(path string) (*Store, error) {
s := &Store{latest: map[string]int{}, lastNum: map[int]int{}, openPos: map[string]int{}}
// It logs to slog.Default() unless given WithLogger.
func OpenStore(path string, opts ...Option) (*Store, error) {
s := &Store{
latest: map[string]int{},
lastNum: map[int]int{},
openPos: map[string]int{},
opts: applyOptions(opts),
}
if err := os.MkdirAll(filepath.Dir(path), 0o750); err != nil {
return nil, err
}
releases, err := readReleases(path)
releases, err := readReleases(s.opts.logger("store"), path)
if err != nil {
return nil, err
}
@@ -67,9 +75,10 @@ func OpenStore(path string) (*Store, error) {
return s, nil
}
// readReleases reads the releases stored at path, skipping lines that are
// blank or can't be decoded. A missing file holds no releases.
func readReleases(path string) ([]ocds.Release, error) {
// readReleases reads the releases stored at path, skipping blank lines and
// lines that can't be decoded (which are logged to logger). A missing file
// holds no releases.
func readReleases(logger *slog.Logger, path string) ([]ocds.Release, error) {
in, err := os.Open(filepath.Clean(path))
if os.IsNotExist(err) {
return nil, nil
@@ -89,7 +98,7 @@ func readReleases(path string) ([]ocds.Release, error) {
}
var r ocds.Release
if err := json.Unmarshal(b, &r); err != nil {
log.Printf("store: skipping unreadable line %d: %v", line, err)
logger.Warn("skipping unreadable line", "line", line, "error", err)
continue
}
releases = append(releases, r)