Return sink write errors from every handler (closes #22)
check / check (push) Successful in 43s
check / check (pull_request) Successful in 51s

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
This commit is contained in:
2026-10-06 06:30:53 +00:00
committed by sneak
parent b28c0dca0c
commit 912d2722f0
7 changed files with 259 additions and 16 deletions
+17 -5
View File
@@ -3,6 +3,7 @@ package simplelog
import (
"context"
"fmt"
"io"
"log/slog"
"os"
"runtime"
@@ -17,6 +18,9 @@ const callerSkipFrames = 4
// ConsoleHandler writes human-readable, colored log lines to stdout.
type ConsoleHandler struct {
// out is where records are written. Nil means os.Stdout, looked up on
// every write so that a reassigned os.Stdout is followed.
out io.Writer
attrs handlerAttrs
}
@@ -27,7 +31,7 @@ func NewConsoleHandler() *ConsoleHandler {
// Handle writes the record to stdout as a colored, timestamped line
// including the caller file and line, followed by the attributes as
// key=value pairs.
// key=value pairs. A failed write is returned as an error.
func (c *ConsoleHandler) Handle(
_ context.Context,
record slog.Record,
@@ -56,8 +60,13 @@ func (c *ConsoleHandler) Handle(
line = 0
}
_, _ = fmt.Fprintln(
os.Stdout,
out := c.out
if out == nil {
out = os.Stdout
}
_, err := fmt.Fprintln(
out,
colorFunc(
"%s [%s] %s:%d: %s%s",
timestamp,
@@ -68,6 +77,9 @@ func (c *ConsoleHandler) Handle(
attrsToText(c.attrs.forRecord(record)),
),
)
if err != nil {
return fmt.Errorf("error writing log record: %w", err)
}
return nil
}
@@ -88,7 +100,7 @@ func (c *ConsoleHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
return c
}
return &ConsoleHandler{attrs: c.attrs.withAttrs(attrs)}
return &ConsoleHandler{out: c.out, attrs: c.attrs.withAttrs(attrs)}
}
// WithGroup returns a new handler that qualifies later attributes with
@@ -98,5 +110,5 @@ func (c *ConsoleHandler) WithGroup(name string) slog.Handler {
return c
}
return &ConsoleHandler{attrs: c.attrs.withGroup(name)}
return &ConsoleHandler{out: c.out, attrs: c.attrs.withGroup(name)}
}