Two modes for interactive CLI use — driven by debugFile presence: - Normal (debugFile=""): human text on screen, INFO and above. - Debug (debugFile set): same screen + full JSON trace to file. Auto-detects TTY; falls back to 12-factor JSON when piped/redirected. IsInteractive() exposes TTY detection for call sites that need it. Terminal format: INFO has no prefix (program's normal voice); WARN prints "warning: …"; ERROR prints "error: …"; DEBUG "debug: …". Breaking: NewDevelopment removed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
122 lines
3.1 KiB
Go
122 lines
3.1 KiB
Go
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")
|
|
}
|
|
}
|