package logger import ( "log/slog" "strings" "testing" ) func TestHumanLevel(t *testing.T) { tests := []struct { level slog.Level want string }{ {slog.LevelDebug, "debug: "}, {slog.LevelInfo, ""}, {slog.LevelWarn, "warning: "}, {slog.LevelError, "error: "}, {slog.LevelError + 4, "error: "}, } for _, tc := range tests { if got := humanLevel(tc.level); got != tc.want { t.Errorf("humanLevel(%v) = %q, want %q", tc.level, got, tc.want) } } } func TestHumanHandlerInfoNoPrefix(t *testing.T) { var buf strings.Builder h := newHumanHandler(&buf, slog.LevelDebug) slog.New(h).Info("hello world") if got := strings.TrimRight(buf.String(), "\n"); got != "hello world" { t.Errorf("got %q, want %q", got, "hello world") } } func TestHumanHandlerPrefixes(t *testing.T) { tests := []struct { fn func(*slog.Logger, string, ...any) want string }{ {(*slog.Logger).Warn, "warning: "}, {(*slog.Logger).Error, "error: "}, } for _, tc := range tests { var buf strings.Builder log := slog.New(newHumanHandler(&buf, slog.LevelDebug)) tc.fn(log, "msg") if !strings.HasPrefix(buf.String(), tc.want) { t.Errorf("got %q, want prefix %q", buf.String(), tc.want) } } } func TestHumanHandlerAttrs(t *testing.T) { var buf strings.Builder slog.New(newHumanHandler(&buf, slog.LevelDebug)).Info("started", "port", 8080) want := "started port=8080\n" if got := buf.String(); got != want { t.Errorf("got %q, want %q", got, want) } } func TestHumanHandlerStringQuoting(t *testing.T) { var buf strings.Builder slog.New(newHumanHandler(&buf, slog.LevelDebug)).Warn("check", "msg", "Hello, Gopher!") if !strings.Contains(buf.String(), `msg="Hello, Gopher!"`) { t.Errorf("expected quoted value in %q", buf.String()) } } func TestHumanHandlerWithAttrs(t *testing.T) { var buf strings.Builder h := newHumanHandler(&buf, slog.LevelDebug).WithAttrs([]slog.Attr{slog.String("rid", "abc")}) slog.New(h).Info("request") if !strings.Contains(buf.String(), "rid=abc") { t.Errorf("pre-set attr missing: %q", buf.String()) } } func TestHumanHandlerLevelFiltering(t *testing.T) { var buf strings.Builder log := slog.New(newHumanHandler(&buf, slog.LevelWarn)) log.Info("dropped") log.Warn("kept") if strings.Contains(buf.String(), "dropped") { t.Error("INFO line should have been filtered at WARN level") } if !strings.Contains(buf.String(), "kept") { t.Error("WARN line should appear") } } func TestMultiHandlerFanOut(t *testing.T) { var screen, file strings.Builder // screen: INFO+, file: DEBUG+ m := multiHandler{ newHumanHandler(&screen, slog.LevelInfo), slog.NewJSONHandler(&file, &slog.HandlerOptions{Level: slog.LevelDebug}), } log := slog.New(m) log.Debug("trace", "k", "v") log.Info("event", "k", "v") if strings.Contains(screen.String(), "trace") { t.Error("DEBUG should not appear on screen") } if !strings.Contains(screen.String(), "event") { t.Error("INFO should appear on screen") } if !strings.Contains(file.String(), "trace") { t.Error("DEBUG should appear in file") } if !strings.Contains(file.String(), "event") { t.Error("INFO should appear in file") } }