Source file
src/log/slog/multi_handler_test.go
1
2
3
4
5 package slog
6
7 import (
8 "bytes"
9 "context"
10 "errors"
11 "testing"
12 "time"
13 )
14
15
16
17 type mockFailingHandler struct {
18 Handler
19 err error
20 }
21
22 func (h *mockFailingHandler) Handle(ctx context.Context, r Record) error {
23 _ = h.Handler.Handle(ctx, r)
24 return h.err
25 }
26
27 func TestMultiHandler(t *testing.T) {
28 t.Run("Handle sends log to all handlers", func(t *testing.T) {
29 var buf1, buf2 bytes.Buffer
30 h1 := NewTextHandler(&buf1, nil)
31 h2 := NewJSONHandler(&buf2, nil)
32
33 multi := NewMultiHandler(h1, h2)
34 logger := New(multi)
35
36 logger.Info("hello world", "user", "test")
37
38 checkLogOutput(t, buf1.String(), "time="+textTimeRE+` level=INFO msg="hello world" user=test`)
39 checkLogOutput(t, buf2.String(), `{"time":"`+jsonTimeRE+`","level":"INFO","msg":"hello world","user":"test"}`)
40 })
41
42 t.Run("Enabled returns true if any handler is enabled", func(t *testing.T) {
43 h1 := NewTextHandler(&bytes.Buffer{}, &HandlerOptions{Level: LevelError})
44 h2 := NewTextHandler(&bytes.Buffer{}, &HandlerOptions{Level: LevelInfo})
45
46 multi := NewMultiHandler(h1, h2)
47
48 if !multi.Enabled(context.Background(), LevelInfo) {
49 t.Error("Enabled should be true for INFO level, but got false")
50 }
51 if !multi.Enabled(context.Background(), LevelError) {
52 t.Error("Enabled should be true for ERROR level, but got false")
53 }
54 })
55
56 t.Run("Enabled returns false if no handlers are enabled", func(t *testing.T) {
57 h1 := NewTextHandler(&bytes.Buffer{}, &HandlerOptions{Level: LevelError})
58 h2 := NewTextHandler(&bytes.Buffer{}, &HandlerOptions{Level: LevelInfo})
59
60 multi := NewMultiHandler(h1, h2)
61
62 if multi.Enabled(context.Background(), LevelDebug) {
63 t.Error("Enabled should be false for DEBUG level, but got true")
64 }
65 })
66
67 t.Run("WithAttrs propagates attributes to all handlers", func(t *testing.T) {
68 var buf1, buf2 bytes.Buffer
69 h1 := NewTextHandler(&buf1, nil)
70 h2 := NewJSONHandler(&buf2, nil)
71
72 multi := NewMultiHandler(h1, h2).WithAttrs([]Attr{String("request_id", "123")})
73 logger := New(multi)
74
75 logger.Info("request processed")
76
77 checkLogOutput(t, buf1.String(), "time="+textTimeRE+` level=INFO msg="request processed" request_id=123`)
78 checkLogOutput(t, buf2.String(), `{"time":"`+jsonTimeRE+`","level":"INFO","msg":"request processed","request_id":"123"}`)
79 })
80
81 t.Run("WithGroup propagates group to all handlers", func(t *testing.T) {
82 var buf1, buf2 bytes.Buffer
83 h1 := NewTextHandler(&buf1, &HandlerOptions{AddSource: false})
84 h2 := NewJSONHandler(&buf2, &HandlerOptions{AddSource: false})
85
86 multi := NewMultiHandler(h1, h2).WithGroup("req")
87 logger := New(multi)
88
89 logger.Info("user login", "user_id", 42)
90
91 checkLogOutput(t, buf1.String(), "time="+textTimeRE+` level=INFO msg="user login" req.user_id=42`)
92 checkLogOutput(t, buf2.String(), `{"time":"`+jsonTimeRE+`","level":"INFO","msg":"user login","req":{"user_id":42}}`)
93 })
94
95 t.Run("Handle propagates errors from handlers", func(t *testing.T) {
96 errFail := errors.New("mock failing")
97
98 var buf1, buf2 bytes.Buffer
99 h1 := NewTextHandler(&buf1, nil)
100 h2 := &mockFailingHandler{Handler: NewJSONHandler(&buf2, nil), err: errFail}
101
102 multi := NewMultiHandler(h2, h1)
103
104 err := multi.Handle(context.Background(), NewRecord(time.Now(), LevelInfo, "test message", 0))
105 if !errors.Is(err, errFail) {
106 t.Errorf("Expected error: %v, but got: %v", errFail, err)
107 }
108
109 checkLogOutput(t, buf1.String(), "time="+textTimeRE+` level=INFO msg="test message"`)
110 checkLogOutput(t, buf2.String(), `{"time":"`+jsonTimeRE+`","level":"INFO","msg":"test message"}`)
111 })
112
113 t.Run("Handle with no handlers", func(t *testing.T) {
114 multi := NewMultiHandler()
115 logger := New(multi)
116
117 logger.Info("nothing")
118
119 err := multi.Handle(context.Background(), NewRecord(time.Now(), LevelInfo, "test", 0))
120 if err != nil {
121 t.Errorf("Handle with no sub-handlers should return nil, but got: %v", err)
122 }
123 })
124 }
125
126
127 func TestNewMultiHandlerCopy(t *testing.T) {
128 var buf1 bytes.Buffer
129 h1 := NewTextHandler(&buf1, nil)
130 slice := []Handler{h1}
131 multi := NewMultiHandler(slice...)
132 slice[0] = nil
133
134 err := multi.Handle(context.Background(), NewRecord(time.Now(), LevelInfo, "test message", 0))
135 if err != nil {
136 t.Errorf("Expected nil error, but got: %v", err)
137 }
138 checkLogOutput(t, buf1.String(), "time="+textTimeRE+` level=INFO msg="test message"`)
139 }
140
View as plain text