Files
simplelog/json_handler.go
T
clawbot 151dd42b4b
check / check (push) Successful in 35s
Return sink write errors from every handler (closes #22)
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
2026-10-06 08:45:17 +02:00

71 lines
1.7 KiB
Go

package simplelog
import (
"context"
"encoding/json"
"fmt"
"io"
"log/slog"
"os"
)
// JSONHandler writes each log record to stdout as a JSON document.
type JSONHandler 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
}
// NewJSONHandler returns a new JSONHandler.
func NewJSONHandler() *JSONHandler {
return &JSONHandler{}
}
// Handle marshals the record, with its attributes, to one JSON object and
// writes it to stdout. A failed write is returned as an error.
func (j *JSONHandler) Handle(_ context.Context, record slog.Record) error {
jsonData, err := json.Marshal(recordToMap(record, j.attrs))
if err != nil {
return err
}
out := j.out
if out == nil {
out = os.Stdout
}
_, err = fmt.Fprintln(out, string(jsonData))
if err != nil {
return fmt.Errorf("error writing log record: %w", err)
}
return nil
}
// Enabled reports whether the handler processes records at the given
// level; it always returns true.
func (j *JSONHandler) Enabled(_ context.Context, _ slog.Level) bool {
return true
}
// WithAttrs returns a new handler that also emits attrs, qualified by the
// groups open now. The receiver is not modified.
func (j *JSONHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
if len(attrs) == 0 {
return j
}
return &JSONHandler{out: j.out, attrs: j.attrs.withAttrs(attrs)}
}
// WithGroup returns a new handler that nests later attributes in an
// object named after the group. An empty name returns the receiver.
func (j *JSONHandler) WithGroup(name string) slog.Handler {
if name == "" {
return j
}
return &JSONHandler{out: j.out, attrs: j.attrs.withGroup(name)}
}