1
16
17 package json
18
19 import (
20 "bytes"
21 "fmt"
22 "strings"
23 "testing"
24 "time"
25
26 "github.com/stretchr/testify/assert"
27 "go.uber.org/zap"
28 "go.uber.org/zap/zapcore"
29
30 "k8s.io/apimachinery/pkg/types"
31 logsapi "k8s.io/component-base/logs/api/v1"
32 )
33
34
35 func TestZapLoggerInfo(t *testing.T) {
36 timeNow = func() time.Time {
37 return time.Date(1970, time.January, 1, 0, 0, 0, 123, time.UTC)
38 }
39 var testDataInfo = []struct {
40 msg string
41 format string
42 keysValues []interface{}
43 names []string
44 }{
45 {
46 msg: "test",
47 format: "{\"ts\":%f,\"caller\":\"json/json_test.go:%d\",\"msg\":\"test\",\"v\":0,\"ns\":\"default\",\"podnum\":2}\n",
48 keysValues: []interface{}{"ns", "default", "podnum", 2},
49 },
50 {
51 msg: "test for strongly typed Zap field",
52 format: "{\"ts\":%f,\"caller\":\"json/json_test.go:%d\",\"msg\":\"strongly-typed Zap Field passed to logr\",\"zap field\":{\"Key\":\"attempt\",\"Type\":11,\"Integer\":3,\"String\":\"\",\"Interface\":null}}\n{\"ts\":%f,\"caller\":\"json/json_test.go:%d\",\"msg\":\"test for strongly typed Zap field\",\"v\":0,\"ns\":\"default\",\"podnum\":2}\n",
53 keysValues: []interface{}{"ns", "default", "podnum", 2, zap.Int("attempt", 3), "attempt", "Running", 10},
54 },
55 {
56 msg: "test for non-string key argument",
57 format: "{\"ts\":%f,\"caller\":\"json/json_test.go:%d\",\"msg\":\"non-string key argument passed to logging, ignoring all later arguments\",\"invalid key\":200}\n{\"ts\":%f,\"caller\":\"json/json_test.go:%d\",\"msg\":\"test for non-string key argument\",\"v\":0,\"ns\":\"default\",\"podnum\":2}\n",
58 keysValues: []interface{}{"ns", "default", "podnum", 2, 200, "replica", "Running", 10},
59 },
60 {
61 msg: "test for duration value argument",
62 format: "{\"ts\":%f,\"caller\":\"json/json_test.go:%d\",\"msg\":\"test for duration value argument\",\"v\":0,\"duration\":\"5s\"}\n",
63 keysValues: []interface{}{"duration", time.Duration(5 * time.Second)},
64 },
65 {
66 msg: "test for WithName",
67 names: []string{"hello", "world"},
68 format: "{\"ts\":%f,\"logger\":\"hello.world\",\"caller\":\"json/json_test.go:%d\",\"msg\":\"test for WithName\",\"v\":0}\n",
69 },
70 {
71 msg: "test for duplicate keys",
72 format: "{\"ts\":%f,\"caller\":\"json/json_test.go:%d\",\"msg\":\"test for duplicate keys\",\"v\":0,\"akey\":\"avalue\",\"akey\":\"anothervalue\"}\n",
73 keysValues: []interface{}{"akey", "avalue", "akey", "anothervalue"},
74 },
75 {
76 msg: "test for NamespacedName argument",
77 format: "{\"ts\":%f,\"caller\":\"json/json_test.go:%d\",\"msg\":\"test for NamespacedName argument\",\"v\":0,\"obj\":{\"name\":\"kube-proxy\",\"namespace\":\"kube-system\"}}\n",
78 keysValues: []interface{}{"obj", types.NamespacedName{Name: "kube-proxy", Namespace: "kube-system"}},
79 },
80 {
81 msg: "test for NamespacedName argument with no namespace",
82 format: "{\"ts\":%f,\"caller\":\"json/json_test.go:%d\",\"msg\":\"test for NamespacedName argument with no namespace\",\"v\":0,\"obj\":{\"name\":\"kube-proxy\"}}\n",
83 keysValues: []interface{}{"obj", types.NamespacedName{Name: "kube-proxy"}},
84 },
85 }
86
87 for _, data := range testDataInfo {
88 var buffer bytes.Buffer
89 writer := zapcore.AddSync(&buffer)
90 sampleInfoLogger, _ := NewJSONLogger(0, writer, nil, nil)
91 for _, name := range data.names {
92
93 sampleInfoLogger = sampleInfoLogger.WithName(name)
94 }
95
96 sampleInfoLogger.Info(data.msg, data.keysValues...)
97 logStr := buffer.String()
98
99 logStrLines := strings.Split(logStr, "\n")
100 dataFormatLines := strings.Split(data.format, "\n")
101 if !assert.Equal(t, len(logStrLines), len(dataFormatLines)) {
102 t.Errorf("Info has wrong format: no. of lines in log is incorrect \n expect:%d\n got:%d", len(dataFormatLines), len(logStrLines))
103 }
104
105 for i := range logStrLines {
106 if len(logStrLines[i]) == 0 && len(dataFormatLines[i]) == 0 {
107 continue
108 }
109 var ts float64
110 var lineNo int
111 n, err := fmt.Sscanf(logStrLines[i], dataFormatLines[i], &ts, &lineNo)
112 if n != 2 || err != nil {
113 t.Errorf("log format error: %d elements, error %s:\n%s", n, err, logStrLines[i])
114 }
115 expect := fmt.Sprintf(dataFormatLines[i], ts, lineNo)
116 if !assert.Equal(t, expect, logStrLines[i]) {
117 t.Errorf("Info has wrong format \n expect:%s\n got:%s", expect, logStrLines[i])
118 }
119 }
120 }
121 }
122
123
124 func TestZapLoggerEnabled(t *testing.T) {
125 verbosityLevel := 10
126 sampleInfoLogger, _ := NewJSONLogger(logsapi.VerbosityLevel(verbosityLevel), nil, nil, nil)
127 for v := 0; v <= verbosityLevel; v++ {
128 enabled := sampleInfoLogger.V(v).Enabled()
129 expectEnabled := v <= verbosityLevel
130 if !expectEnabled && enabled {
131 t.Errorf("V(%d).Info should be disabled", v)
132 }
133 if expectEnabled && !enabled {
134 t.Errorf("V(%d).Info should be enabled", v)
135 }
136 }
137 }
138
139
140 func TestZapLoggerV(t *testing.T) {
141 timeNow = func() time.Time {
142 return time.Date(1970, time.January, 1, 0, 0, 0, 123, time.UTC)
143 }
144
145 verbosityLevel := 10
146 for v := 0; v <= verbosityLevel; v++ {
147 var buffer bytes.Buffer
148 writer := zapcore.AddSync(&buffer)
149 sampleInfoLogger, _ := NewJSONLogger(logsapi.VerbosityLevel(verbosityLevel), writer, nil, nil)
150 sampleInfoLogger.V(v).Info("test", "ns", "default", "podnum", 2, "time", time.Microsecond)
151 logStr := buffer.String()
152
153 shouldHaveLogged := v <= verbosityLevel
154 if logged := logStr != ""; logged != shouldHaveLogged {
155 if logged {
156 t.Fatalf("Expected no output at v=%d, got: %s", v, logStr)
157 }
158 t.Fatalf("Expected output at v=%d, got none.", v)
159 }
160 if !shouldHaveLogged {
161 continue
162 }
163
164 var actualV, lineNo int
165 expectFormat := "{\"ts\":0.000123,\"caller\":\"json/json_test.go:%d\",\"msg\":\"test\",\"v\":%d,\"ns\":\"default\",\"podnum\":2,\"time\":\"1µs\"}\n"
166 n, err := fmt.Sscanf(logStr, expectFormat, &lineNo, &actualV)
167 if n != 2 || err != nil {
168 t.Errorf("log format error: %d elements, error %s:\n%s", n, err, logStr)
169 }
170 if actualV != v {
171 t.Errorf("V(%d).Info...) returned v=%d. expected v=%d", v, actualV, v)
172 }
173 expect := fmt.Sprintf(expectFormat, lineNo, v)
174 if !assert.Equal(t, expect, logStr) {
175 t.Errorf("V(%d).Info has wrong format \n expect:%s\n got:%s", v, expect, logStr)
176 }
177 buffer.Reset()
178 }
179 }
180
181
182 func TestZapLoggerError(t *testing.T) {
183 var buffer bytes.Buffer
184 writer := zapcore.AddSync(&buffer)
185 timeNow = func() time.Time {
186 return time.Date(1970, time.January, 1, 0, 0, 0, 123, time.UTC)
187 }
188 sampleInfoLogger, _ := NewJSONLogger(0, writer, nil, nil)
189 sampleInfoLogger.Error(fmt.Errorf("invalid namespace:%s", "default"), "wrong namespace", "ns", "default", "podnum", 2, "time", time.Microsecond)
190 logStr := buffer.String()
191 var ts float64
192 var lineNo int
193 expectFormat := `{"ts":%f,"caller":"json/json_test.go:%d","msg":"wrong namespace","ns":"default","podnum":2,"time":"1µs","err":"invalid namespace:default"}`
194 n, err := fmt.Sscanf(logStr, expectFormat, &ts, &lineNo)
195 if n != 2 || err != nil {
196 t.Errorf("log format error: %d elements, error %s:\n%s", n, err, logStr)
197 }
198 expect := fmt.Sprintf(expectFormat, ts, lineNo)
199 if !assert.JSONEq(t, expect, logStr) {
200 t.Errorf("Info has wrong format \n expect:%s\n got:%s", expect, logStr)
201 }
202 }
203
204 func TestZapLoggerStreams(t *testing.T) {
205 var infoBuffer, errorBuffer bytes.Buffer
206 log, _ := NewJSONLogger(0, zapcore.AddSync(&infoBuffer), zapcore.AddSync(&errorBuffer), nil)
207
208 log.Error(fmt.Errorf("some error"), "failed")
209 log.Info("hello world")
210
211 logStr := errorBuffer.String()
212 var ts float64
213 var lineNo int
214 expectFormat := `{"ts":%f,"caller":"json/json_test.go:%d","msg":"failed","err":"some error"}`
215 n, err := fmt.Sscanf(logStr, expectFormat, &ts, &lineNo)
216 if n != 2 || err != nil {
217 t.Errorf("error log format error: %d elements, error %s:\n%s", n, err, logStr)
218 }
219 expect := fmt.Sprintf(expectFormat, ts, lineNo)
220 if !assert.JSONEq(t, expect, logStr) {
221 t.Errorf("error log has wrong format \n expect:%s\n got:%s", expect, logStr)
222 }
223
224 logStr = infoBuffer.String()
225 expectFormat = `{"ts":%f,"caller":"json/json_test.go:%d","msg":"hello world","v":0}`
226 n, err = fmt.Sscanf(logStr, expectFormat, &ts, &lineNo)
227 if n != 2 || err != nil {
228 t.Errorf("info log format error: %d elements, error %s:\n%s", n, err, logStr)
229 }
230 expect = fmt.Sprintf(expectFormat, ts, lineNo)
231 if !assert.JSONEq(t, expect, logStr) {
232 t.Errorf("info has wrong format \n expect:%s\n got:%s", expect, logStr)
233 }
234 }
235
236 type testBuff struct {
237 writeCount int
238 }
239
240
241 func (b *testBuff) Sync() error {
242 return nil
243 }
244
245
246 func (b *testBuff) Write(p []byte) (int, error) {
247 b.writeCount++
248 return len(p), nil
249 }
250
View as plain text