| | |
| | |
| | |
| |
|
| | package slog |
| |
|
| | import ( |
| | "bytes" |
| | "context" |
| | "errors" |
| | "testing" |
| | "time" |
| | ) |
| |
|
| | |
| | |
| | type mockFailingHandler struct { |
| | Handler |
| | err error |
| | } |
| |
|
| | func (h *mockFailingHandler) Handle(ctx context.Context, r Record) error { |
| | _ = h.Handler.Handle(ctx, r) |
| | return h.err |
| | } |
| |
|
| | func TestMultiHandler(t *testing.T) { |
| | t.Run("Handle sends log to all handlers", func(t *testing.T) { |
| | var buf1, buf2 bytes.Buffer |
| | h1 := NewTextHandler(&buf1, nil) |
| | h2 := NewJSONHandler(&buf2, nil) |
| |
|
| | multi := NewMultiHandler(h1, h2) |
| | logger := New(multi) |
| |
|
| | logger.Info("hello world", "user", "test") |
| |
|
| | checkLogOutput(t, buf1.String(), "time="+textTimeRE+` level=INFO msg="hello world" user=test`) |
| | checkLogOutput(t, buf2.String(), `{"time":"`+jsonTimeRE+`","level":"INFO","msg":"hello world","user":"test"}`) |
| | }) |
| |
|
| | t.Run("Enabled returns true if any handler is enabled", func(t *testing.T) { |
| | h1 := NewTextHandler(&bytes.Buffer{}, &HandlerOptions{Level: LevelError}) |
| | h2 := NewTextHandler(&bytes.Buffer{}, &HandlerOptions{Level: LevelInfo}) |
| |
|
| | multi := NewMultiHandler(h1, h2) |
| |
|
| | if !multi.Enabled(context.Background(), LevelInfo) { |
| | t.Error("Enabled should be true for INFO level, but got false") |
| | } |
| | if !multi.Enabled(context.Background(), LevelError) { |
| | t.Error("Enabled should be true for ERROR level, but got false") |
| | } |
| | }) |
| |
|
| | t.Run("Enabled returns false if no handlers are enabled", func(t *testing.T) { |
| | h1 := NewTextHandler(&bytes.Buffer{}, &HandlerOptions{Level: LevelError}) |
| | h2 := NewTextHandler(&bytes.Buffer{}, &HandlerOptions{Level: LevelInfo}) |
| |
|
| | multi := NewMultiHandler(h1, h2) |
| |
|
| | if multi.Enabled(context.Background(), LevelDebug) { |
| | t.Error("Enabled should be false for DEBUG level, but got true") |
| | } |
| | }) |
| |
|
| | t.Run("WithAttrs propagates attributes to all handlers", func(t *testing.T) { |
| | var buf1, buf2 bytes.Buffer |
| | h1 := NewTextHandler(&buf1, nil) |
| | h2 := NewJSONHandler(&buf2, nil) |
| |
|
| | multi := NewMultiHandler(h1, h2).WithAttrs([]Attr{String("request_id", "123")}) |
| | logger := New(multi) |
| |
|
| | logger.Info("request processed") |
| |
|
| | checkLogOutput(t, buf1.String(), "time="+textTimeRE+` level=INFO msg="request processed" request_id=123`) |
| | checkLogOutput(t, buf2.String(), `{"time":"`+jsonTimeRE+`","level":"INFO","msg":"request processed","request_id":"123"}`) |
| | }) |
| |
|
| | t.Run("WithGroup propagates group to all handlers", func(t *testing.T) { |
| | var buf1, buf2 bytes.Buffer |
| | h1 := NewTextHandler(&buf1, &HandlerOptions{AddSource: false}) |
| | h2 := NewJSONHandler(&buf2, &HandlerOptions{AddSource: false}) |
| |
|
| | multi := NewMultiHandler(h1, h2).WithGroup("req") |
| | logger := New(multi) |
| |
|
| | logger.Info("user login", "user_id", 42) |
| |
|
| | checkLogOutput(t, buf1.String(), "time="+textTimeRE+` level=INFO msg="user login" req.user_id=42`) |
| | checkLogOutput(t, buf2.String(), `{"time":"`+jsonTimeRE+`","level":"INFO","msg":"user login","req":{"user_id":42}}`) |
| | }) |
| |
|
| | t.Run("Handle propagates errors from handlers", func(t *testing.T) { |
| | errFail := errors.New("mock failing") |
| |
|
| | var buf1, buf2 bytes.Buffer |
| | h1 := NewTextHandler(&buf1, nil) |
| | h2 := &mockFailingHandler{Handler: NewJSONHandler(&buf2, nil), err: errFail} |
| |
|
| | multi := NewMultiHandler(h2, h1) |
| |
|
| | err := multi.Handle(context.Background(), NewRecord(time.Now(), LevelInfo, "test message", 0)) |
| | if !errors.Is(err, errFail) { |
| | t.Errorf("Expected error: %v, but got: %v", errFail, err) |
| | } |
| |
|
| | checkLogOutput(t, buf1.String(), "time="+textTimeRE+` level=INFO msg="test message"`) |
| | checkLogOutput(t, buf2.String(), `{"time":"`+jsonTimeRE+`","level":"INFO","msg":"test message"}`) |
| | }) |
| |
|
| | t.Run("Handle with no handlers", func(t *testing.T) { |
| | multi := NewMultiHandler() |
| | logger := New(multi) |
| |
|
| | logger.Info("nothing") |
| |
|
| | err := multi.Handle(context.Background(), NewRecord(time.Now(), LevelInfo, "test", 0)) |
| | if err != nil { |
| | t.Errorf("Handle with no sub-handlers should return nil, but got: %v", err) |
| | } |
| | }) |
| | } |
| |
|
| | |
| | func TestNewMultiHandlerCopy(t *testing.T) { |
| | var buf1 bytes.Buffer |
| | h1 := NewTextHandler(&buf1, nil) |
| | slice := []Handler{h1} |
| | multi := NewMultiHandler(slice...) |
| | slice[0] = nil |
| |
|
| | err := multi.Handle(context.Background(), NewRecord(time.Now(), LevelInfo, "test message", 0)) |
| | if err != nil { |
| | t.Errorf("Expected nil error, but got: %v", err) |
| | } |
| | checkLogOutput(t, buf1.String(), "time="+textTimeRE+` level=INFO msg="test message"`) |
| | } |
| |
|