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

+11 -7
View File
@@ -106,7 +106,7 @@ func (c *client) run(ctx context.Context) {
// consumer), after a failed write or ping, or by the caller.
c.conn.CloseNow() //nolint:errcheck,gosec // finished with; it may already be closed
default:
c.hub.log.Debug("read error", "client", c.id, "error", err)
c.hub.logger().Debug("read error", "client", c.id, "error", err)
c.conn.CloseNow() //nolint:errcheck,gosec // the connection has failed anyway
}
@@ -178,7 +178,7 @@ func (c *client) writeLoop(ctx context.Context) {
err := c.conn.Write(writeCtx, websocket.MessageText, msg)
cancel()
if err != nil {
c.hub.log.Debug("write failed", "client", c.id, "error", err)
c.hub.logger().Debug("write failed", "client", c.id, "error", err)
c.cancel()
return
}
@@ -215,7 +215,7 @@ func (c *client) pingLoop(ctx context.Context) {
err := c.conn.Ping(pingCtx)
cancel()
if err != nil {
c.hub.log.Debug("ping failed", "client", c.id, "error", err)
c.hub.logger().Debug("ping failed", "client", c.id, "error", err)
c.conn.CloseNow() //nolint:errcheck,gosec // dropping the client anyway
c.cancel()
return
@@ -240,11 +240,11 @@ func (c *client) trySend(msg []byte) bool {
func (c *client) sendEnvelope(env Envelope) {
encoded, err := json.Marshal(env)
if err != nil {
c.hub.log.Error("marshal", "client", c.id, "error", err)
c.hub.logger().Error("marshal", "client", c.id, "error", err)
return
}
if !c.trySend(encoded) {
c.hub.log.Warn("dropping slow client", "client", c.id, "ip", c.ip)
c.hub.logger().Warn("dropping slow client", "client", c.id, "ip", c.ip)
go c.close(websocket.StatusPolicyViolation, "slow consumer")
}
}
@@ -273,14 +273,18 @@ func (c *client) close(code websocket.StatusCode, reason string) {
// level, with its size; at most logMessageLimit bytes of it are logged. It
// does nothing unless debug logging is enabled.
func (c *client) logMessage(ctx context.Context, event string, data []byte) {
if !c.hub.log.Enabled(ctx, slog.LevelDebug) {
logger := c.hub.logger()
if !logger.Enabled(ctx, slog.LevelDebug) {
return
}
logged, truncated := data, false
if len(logged) > logMessageLimit {
logged, truncated = logged[:logMessageLimit], true
}
c.hub.log.LogAttrs(ctx, slog.LevelDebug, event,
logger.LogAttrs(
ctx,
slog.LevelDebug,
event,
slog.String("client", c.id),
slog.Int("bytes", len(data)),
slog.String("message", string(logged)),