journslog: integrate with testing/slogtest
This commit is contained in:
parent
df6107a67e
commit
f4e1589ec3
|
|
@ -81,3 +81,11 @@ func WithAttrsFromTime(attrsFromTime func(time.Time) []slog.Attr) HandlerOption
|
|||
jh.attrsFromTime = attrsFromTime
|
||||
}
|
||||
}
|
||||
|
||||
// WithAttrsFromLevel supplies a function which can optionally map the given
|
||||
// log level onto zero or more attributes.
|
||||
func WithAttrsFromLevel(attrsFromLevel func(slog.Level) []slog.Attr) HandlerOption {
|
||||
return func(jh *JournalHandler) {
|
||||
jh.attrsFromLevel = attrsFromLevel
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,10 @@ type JournalHandler struct {
|
|||
// Time from an [slog.Record]. It may be nil.
|
||||
attrsFromTime func(time.Time) []slog.Attr
|
||||
|
||||
// attrsFromLevel may return zero or more attributes to represent the
|
||||
// log level. It may be nil.
|
||||
attrsFromLevel func(slog.Level) []slog.Attr
|
||||
|
||||
// groups are the ordered group names added by WithGroup. We need to
|
||||
// maintain the full list, not just a pre-rendered prefix string,
|
||||
// because of the API of replaceAttr.
|
||||
|
|
@ -174,6 +178,13 @@ func (jh *JournalHandler) Handle(ctx context.Context, rec slog.Record) error {
|
|||
}
|
||||
addAttrs = append(addAttrs, jh.attrsFromContext(ctx)...)
|
||||
}
|
||||
if jh.attrsFromLevel != nil {
|
||||
if addAttrs == nil {
|
||||
addAttrs = attrPoolGet()
|
||||
defer attrPoolPut(addAttrs)
|
||||
}
|
||||
addAttrs = append(addAttrs, jh.attrsFromLevel(rec.Level)...)
|
||||
}
|
||||
|
||||
// 2 for journal.Conn.Entry (MESSAGE and PRIORITY),
|
||||
// 3 for SRC_* (optional but we always allocate space)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,11 @@ import (
|
|||
"log/slog"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"testing/slogtest"
|
||||
"time"
|
||||
"uuid"
|
||||
|
||||
"src.lwithers.me.uk/go/journal"
|
||||
"src.lwithers.me.uk/go/journal/testsink"
|
||||
|
|
@ -82,3 +86,97 @@ type slowValue string
|
|||
func (s slowValue) LogValue() slog.Value {
|
||||
return slog.StringValue(string(s))
|
||||
}
|
||||
|
||||
// TestSlog integrates with [testing/slogtest].
|
||||
func TestSlog(t *testing.T) {
|
||||
tmpdir := t.TempDir()
|
||||
|
||||
attrsFromTime := func(t time.Time) []slog.Attr {
|
||||
return []slog.Attr{
|
||||
{
|
||||
Key: "TEST_TIME",
|
||||
Value: slog.StringValue(t.Format(time.RFC3339Nano)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
attrsFromLevel := func(l slog.Level) []slog.Attr {
|
||||
return []slog.Attr{
|
||||
{
|
||||
Key: "TEST_LEVEL",
|
||||
Value: slog.StringValue(l.String()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: this relies on [slogtest.Run] executing its test cases
|
||||
// completely sequentially
|
||||
var curSink *testsink.Sink
|
||||
|
||||
newHandler := func(t *testing.T) slog.Handler {
|
||||
sockpath := filepath.Join(tmpdir, uuid.New().String())
|
||||
sink, err := testsink.New(sockpath)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test sink: %v", err)
|
||||
}
|
||||
|
||||
conn, err := journal.Connect(sockpath)
|
||||
if err != nil {
|
||||
sink.Stop()
|
||||
t.Fatalf("failed to connect to test sink socket: %v", err)
|
||||
}
|
||||
|
||||
h, err := NewHandler(WithConn(conn), WithAttrsFromTime(attrsFromTime), WithAttrsFromLevel(attrsFromLevel), WithHandlerOptions(slog.HandlerOptions{
|
||||
Level: slog.LevelInfo,
|
||||
}))
|
||||
if err != nil {
|
||||
sink.Stop()
|
||||
t.Fatalf("failed to create handler: %v", err)
|
||||
}
|
||||
|
||||
curSink = sink
|
||||
return h
|
||||
}
|
||||
|
||||
result := func(t *testing.T) map[string]any {
|
||||
defer curSink.Stop()
|
||||
|
||||
m, err := curSink.Message(0)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to fetch message from test sink: %v", err)
|
||||
}
|
||||
|
||||
msg, attrs, err := m.Decode()
|
||||
if err != nil {
|
||||
t.Errorf("failed to decode message from test sink: %v", err)
|
||||
}
|
||||
|
||||
out := make(map[string]any)
|
||||
out[slog.MessageKey] = msg
|
||||
for i := range attrs {
|
||||
k, v := attrs[i].Key, attrs[i].Val
|
||||
switch k {
|
||||
case "TEST_TIME":
|
||||
out[slog.TimeKey] = v
|
||||
case "TEST_LEVEL":
|
||||
out[slog.LevelKey] = v
|
||||
default:
|
||||
parts := strings.Split(k, "_")
|
||||
Ng := len(parts) - 1 // number of groups
|
||||
g := out
|
||||
for i := range Ng {
|
||||
next, _ := g[parts[i]].(map[string]any)
|
||||
if next == nil {
|
||||
next = make(map[string]any)
|
||||
g[parts[i]] = next
|
||||
}
|
||||
g = next
|
||||
}
|
||||
g[strings.ToLower(parts[Ng])] = v
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
slogtest.Run(t, newHandler, result)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue