1 package klogr
2
3 import (
4 "bytes"
5 "encoding/json"
6 "errors"
7 "strings"
8 "testing"
9
10 "k8s.io/klog/v2"
11 "k8s.io/klog/v2/internal/test/require"
12 "k8s.io/klog/v2/test"
13
14 "github.com/go-logr/logr"
15 )
16
17 const (
18 formatDefault = "Default"
19 formatNew = "New"
20 )
21
22 func testOutput(t *testing.T, format string) {
23 createLogger := func() logr.Logger {
24 switch format {
25 case formatNew:
26 return New()
27 case formatDefault:
28 return NewWithOptions()
29 default:
30 return NewWithOptions(WithFormat(Format(format)))
31 }
32 }
33 tests := map[string]struct {
34 klogr logr.Logger
35 text string
36 keysAndValues []interface{}
37 err error
38 expectedOutput string
39 expectedKlogOutput string
40 }{
41 "should log with values passed to keysAndValues": {
42 klogr: createLogger().V(0),
43 text: "test",
44 keysAndValues: []interface{}{"akey", "avalue"},
45 expectedOutput: `"msg"="test" "akey"="avalue"
46 `,
47 expectedKlogOutput: `"test" akey="avalue"
48 `,
49 },
50 "should log with name and values passed to keysAndValues": {
51 klogr: createLogger().V(0).WithName("me"),
52 text: "test",
53 keysAndValues: []interface{}{"akey", "avalue"},
54
55 expectedOutput: `"msg"="test" "akey"="avalue" "logger"="me"
56 `,
57
58 expectedKlogOutput: `"test" logger="me" akey="avalue"
59 `,
60 },
61 "should log with multiple names and values passed to keysAndValues": {
62 klogr: createLogger().V(0).WithName("hello").WithName("world"),
63 text: "test",
64 keysAndValues: []interface{}{"akey", "avalue"},
65
66 expectedOutput: `"msg"="test" "akey"="avalue" "logger"="hello.world"
67 `,
68
69 expectedKlogOutput: `"test" logger="hello.world" akey="avalue"
70 `,
71 },
72 "may print duplicate keys with the same value": {
73 klogr: createLogger().V(0),
74 text: "test",
75 keysAndValues: []interface{}{"akey", "avalue", "akey", "avalue"},
76 expectedOutput: `"msg"="test" "akey"="avalue"
77 `,
78 expectedKlogOutput: `"test" akey="avalue" akey="avalue"
79 `,
80 },
81 "may print duplicate keys when the values are passed to Info": {
82 klogr: createLogger().V(0),
83 text: "test",
84 keysAndValues: []interface{}{"akey", "avalue", "akey", "avalue2"},
85 expectedOutput: `"msg"="test" "akey"="avalue2"
86 `,
87 expectedKlogOutput: `"test" akey="avalue" akey="avalue2"
88 `,
89 },
90 "should only print the duplicate key that is passed to Info if one was passed to the logger": {
91 klogr: createLogger().WithValues("akey", "avalue"),
92 text: "test",
93 keysAndValues: []interface{}{"akey", "avalue"},
94 expectedOutput: `"msg"="test" "akey"="avalue"
95 `,
96 expectedKlogOutput: `"test" akey="avalue"
97 `,
98 },
99 "should sort within logger and parameter key/value pairs in the default format and dump the logger pairs first": {
100 klogr: createLogger().WithValues("akey9", "avalue9", "akey8", "avalue8", "akey1", "avalue1"),
101 text: "test",
102 keysAndValues: []interface{}{"akey5", "avalue5", "akey4", "avalue4"},
103 expectedOutput: `"msg"="test" "akey1"="avalue1" "akey4"="avalue4" "akey5"="avalue5" "akey8"="avalue8" "akey9"="avalue9"
104 `,
105 expectedKlogOutput: `"test" akey9="avalue9" akey8="avalue8" akey1="avalue1" akey5="avalue5" akey4="avalue4"
106 `,
107 },
108 "should only print the key passed to Info when one is already set on the logger": {
109 klogr: createLogger().WithValues("akey", "avalue"),
110 text: "test",
111 keysAndValues: []interface{}{"akey", "avalue2"},
112 expectedOutput: `"msg"="test" "akey"="avalue2"
113 `,
114 expectedKlogOutput: `"test" akey="avalue2"
115 `,
116 },
117 "should correctly handle odd-numbers of KVs": {
118 klogr: createLogger(),
119 text: "test",
120 keysAndValues: []interface{}{"akey", "avalue", "akey2"},
121 expectedOutput: `"msg"="test" "akey"="avalue" "akey2"="(MISSING)"
122 `,
123 expectedKlogOutput: `"test" akey="avalue" akey2="(MISSING)"
124 `,
125 },
126 "should correctly handle odd-numbers of KVs in WithValue": {
127 klogr: createLogger().WithValues("keyWithoutValue"),
128 text: "test",
129 keysAndValues: []interface{}{"akey", "avalue", "akey2"},
130
131 expectedOutput: `"msg"="test" "akey"="avalue" "akey2"="(MISSING)" "keyWithoutValue"="(MISSING)"
132 `,
133 expectedKlogOutput: `"test" keyWithoutValue="(MISSING)" akey="avalue" akey2="(MISSING)"
134 `,
135 },
136 "should correctly html characters": {
137 klogr: createLogger(),
138 text: "test",
139 keysAndValues: []interface{}{"akey", "<&>"},
140 expectedOutput: `"msg"="test" "akey"="<&>"
141 `,
142 expectedKlogOutput: `"test" akey="<&>"
143 `,
144 },
145 "should correctly handle odd-numbers of KVs in both log values and Info args": {
146 klogr: createLogger().WithValues("basekey1", "basevar1", "basekey2"),
147 text: "test",
148 keysAndValues: []interface{}{"akey", "avalue", "akey2"},
149
150 expectedOutput: `"msg"="test" "akey"="avalue" "akey2"="(MISSING)" "basekey1"="basevar1" "basekey2"="(MISSING)"
151 `,
152 expectedKlogOutput: `"test" basekey1="basevar1" basekey2="(MISSING)" akey="avalue" akey2="(MISSING)"
153 `,
154 },
155 "should correctly print regular error types": {
156 klogr: createLogger().V(0),
157 text: "test",
158 keysAndValues: []interface{}{"err", errors.New("whoops")},
159 expectedOutput: `"msg"="test" "err"="whoops"
160 `,
161 expectedKlogOutput: `"test" err="whoops"
162 `,
163 },
164 "should use MarshalJSON in the default format if an error type implements it": {
165 klogr: createLogger().V(0),
166 text: "test",
167 keysAndValues: []interface{}{"err", &customErrorJSON{"whoops"}},
168 expectedOutput: `"msg"="test" "err"="WHOOPS"
169 `,
170 expectedKlogOutput: `"test" err="whoops"
171 `,
172 },
173 "should correctly print regular error types when using logr.Error": {
174 klogr: createLogger().V(0),
175 text: "test",
176 err: errors.New("whoops"),
177 expectedOutput: `"msg"="test" "error"="whoops"
178 `,
179 expectedKlogOutput: `"test" err="whoops"
180 `,
181 },
182 }
183 for n, test := range tests {
184 t.Run(n, func(t *testing.T) {
185
186
187 tmpWriteBuffer := bytes.NewBuffer(nil)
188 klog.SetOutput(tmpWriteBuffer)
189
190 if test.err != nil {
191 test.klogr.Error(test.err, test.text, test.keysAndValues...)
192 } else {
193 test.klogr.Info(test.text, test.keysAndValues...)
194 }
195
196
197 klog.Flush()
198
199 actual := tmpWriteBuffer.String()
200 expectedOutput := test.expectedOutput
201 if format == string(FormatKlog) || format == formatDefault {
202 expectedOutput = test.expectedKlogOutput
203 }
204 if actual != expectedOutput {
205 t.Errorf("Expected:\n%s\nActual:\n%s\n", expectedOutput, actual)
206 }
207 })
208 }
209 }
210
211 func TestOutput(t *testing.T) {
212 fs := test.InitKlog(t)
213 require.NoError(t, fs.Set("skip_headers", "true"))
214
215 formats := []string{
216 formatNew,
217 formatDefault,
218 string(FormatSerialize),
219 string(FormatKlog),
220 }
221 for _, format := range formats {
222 t.Run(format, func(t *testing.T) {
223 testOutput(t, format)
224 })
225 }
226 }
227
228 type customErrorJSON struct {
229 s string
230 }
231
232 func (e *customErrorJSON) Error() string {
233 return e.s
234 }
235
236 func (e *customErrorJSON) MarshalJSON() ([]byte, error) {
237 return json.Marshal(strings.ToUpper(e.s))
238 }
239
View as plain text