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

+4 -5
View File
@@ -52,17 +52,16 @@ opportunities.
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
cli.SetupInitialLogging(cmd)
slog.Debug("root.PersistentPreRun")
slog.Debug("command persistent pre-run", "command", "root")
slog.Debug(
"command: \"" +
strings.Join(strings.Split(cmd.CommandPath(), " ")[1:], " ") +
"\"",
"command",
"path", strings.Join(strings.Split(cmd.CommandPath(), " ")[1:], " "),
)
return nil
},
PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
slog.Debug("root.PersistentPostRun")
slog.Debug("command persistent post-run", "command", "root")
return opts.Finish()
},
+12 -13
View File
@@ -24,6 +24,7 @@ import (
"errors"
"fmt"
"log"
"log/slog"
"net/http"
"os"
"os/signal"
@@ -206,15 +207,13 @@ func run(ctx context.Context, o *options) (err error) {
if err != nil {
return err
}
log.Printf(
"catalog: %d buyers, %d suppliers, %d subjects, %d methods, %d classifications",
len(
cat.Buyers,
),
len(cat.Suppliers),
len(cat.Subjects),
len(cat.Methods),
len(cat.Classifications),
slog.Info(
"catalog loaded",
"buyers", len(cat.Buyers),
"suppliers", len(cat.Suppliers),
"subjects", len(cat.Subjects),
"methods", len(cat.Methods),
"classifications", len(cat.Classifications),
)
// ----- store -----
@@ -223,7 +222,7 @@ func run(ctx context.Context, o *options) (err error) {
if err := os.Remove(storePath); err != nil && !errors.Is(err, os.ErrNotExist) {
return err
}
log.Print("store: reset")
slog.Info("store reset", "path", storePath)
}
store, err := mock.OpenStore(storePath)
if err != nil {
@@ -238,7 +237,7 @@ func run(ctx context.Context, o *options) (err error) {
return err
}
log.Printf("store: %d releases (%d generated to catch up)", store.Len(), n)
slog.Info("store ready", "releases", store.Len(), "generated", n)
// The generator and server share a context that also ends if the server
// fails, so the generator is never left running after run returns.
@@ -286,11 +285,11 @@ func run(ctx context.Context, o *options) (err error) {
)
defer cancelShutdown()
if err := httpSrv.Shutdown(shutdownCtx); err != nil {
log.Printf("shutdown: %v", err)
slog.Error("shutdown failed", "error", err)
}
}()
log.Printf("FTS mock listening on %s%s (query times in %s)", o.addr, mock.BasePath, loc)
slog.Info("mock listening", "addr", o.addr, "path", mock.BasePath, "time_zone", loc.String())
serveErr := httpSrv.ListenAndServe()
cancel() // stop the worker (and the shutdown goroutine) if the server failed