github.com/Laisky/zap@v1.27.0/zapcore/json_encoder_test.go (about) 1 // Copyright (c) 2018 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package zapcore_test 22 23 import ( 24 "io" 25 "testing" 26 "time" 27 28 "github.com/stretchr/testify/assert" 29 30 "github.com/Laisky/zap" 31 "github.com/Laisky/zap/zapcore" 32 ) 33 34 // TestJSONEncodeEntry is an more "integrated" test that makes it easier to get 35 // coverage on the json encoder (e.g. putJSONEncoder, let alone EncodeEntry 36 // itself) than the tests in json_encoder_impl_test.go; it needs to be in the 37 // zapcore_test package, so that it can import the toplevel zap package for 38 // field constructor convenience. 39 func TestJSONEncodeEntry(t *testing.T) { 40 type bar struct { 41 Key string `json:"key"` 42 Val float64 `json:"val"` 43 } 44 45 type foo struct { 46 A string `json:"aee"` 47 B int `json:"bee"` 48 C float64 `json:"cee"` 49 D []bar `json:"dee"` 50 } 51 52 tests := []struct { 53 desc string 54 expected string 55 ent zapcore.Entry 56 fields []zapcore.Field 57 }{ 58 { 59 desc: "info entry with some fields", 60 expected: `{ 61 "L": "info", 62 "T": "2018-06-19T16:33:42.000Z", 63 "N": "bob", 64 "M": "lob law", 65 "so": "passes", 66 "answer": 42, 67 "a_float32": 2.71, 68 "common_pie": 3.14, 69 "complex_value": "3.14-2.71i", 70 "null_value": null, 71 "array_with_null_elements": [{}, null, null, 2], 72 "such": { 73 "aee": "lol", 74 "bee": 123, 75 "cee": 0.9999, 76 "dee": [ 77 {"key": "pi", "val": 3.141592653589793}, 78 {"key": "tau", "val": 6.283185307179586} 79 ] 80 } 81 }`, 82 ent: zapcore.Entry{ 83 Level: zapcore.InfoLevel, 84 Time: time.Date(2018, 6, 19, 16, 33, 42, 99, time.UTC), 85 LoggerName: "bob", 86 Message: "lob law", 87 }, 88 fields: []zapcore.Field{ 89 zap.String("so", "passes"), 90 zap.Int("answer", 42), 91 zap.Float64("common_pie", 3.14), 92 zap.Float32("a_float32", 2.71), 93 zap.Complex128("complex_value", 3.14-2.71i), 94 // Cover special-cased handling of nil in AddReflect() and 95 // AppendReflect(). Note that for the latter, we explicitly test 96 // correct results for both the nil static interface{} value 97 // (`nil`), as well as the non-nil interface value with a 98 // dynamic type and nil value (`(*struct{})(nil)`). 99 zap.Reflect("null_value", nil), 100 zap.Reflect("array_with_null_elements", []interface{}{&struct{}{}, nil, (*struct{})(nil), 2}), 101 zap.Reflect("such", foo{ 102 A: "lol", 103 B: 123, 104 C: 0.9999, 105 D: []bar{ 106 {"pi", 3.141592653589793}, 107 {"tau", 6.283185307179586}, 108 }, 109 }), 110 }, 111 }, 112 { 113 desc: "zero_time_omitted", 114 expected: `{ 115 "L": "info", 116 "N": "name", 117 "M": "message" 118 }`, 119 ent: zapcore.Entry{ 120 Level: zapcore.InfoLevel, 121 Time: time.Time{}, 122 LoggerName: "name", 123 Message: "message", 124 }, 125 }, 126 } 127 128 enc := zapcore.NewJSONEncoder(zapcore.EncoderConfig{ 129 MessageKey: "M", 130 LevelKey: "L", 131 TimeKey: "T", 132 NameKey: "N", 133 CallerKey: "C", 134 FunctionKey: "F", 135 StacktraceKey: "S", 136 EncodeLevel: zapcore.LowercaseLevelEncoder, 137 EncodeTime: zapcore.ISO8601TimeEncoder, 138 EncodeDuration: zapcore.SecondsDurationEncoder, 139 EncodeCaller: zapcore.ShortCallerEncoder, 140 }) 141 142 for _, tt := range tests { 143 t.Run(tt.desc, func(t *testing.T) { 144 buf, err := enc.EncodeEntry(tt.ent, tt.fields) 145 if assert.NoError(t, err, "Unexpected JSON encoding error.") { 146 assert.JSONEq(t, tt.expected, buf.String(), "Incorrect encoded JSON entry.") 147 } 148 buf.Free() 149 }) 150 } 151 } 152 153 func TestNoEncodeLevelSupplied(t *testing.T) { 154 enc := zapcore.NewJSONEncoder(zapcore.EncoderConfig{ 155 MessageKey: "M", 156 LevelKey: "L", 157 TimeKey: "T", 158 NameKey: "N", 159 CallerKey: "C", 160 FunctionKey: "F", 161 StacktraceKey: "S", 162 EncodeTime: zapcore.ISO8601TimeEncoder, 163 EncodeDuration: zapcore.SecondsDurationEncoder, 164 EncodeCaller: zapcore.ShortCallerEncoder, 165 }) 166 167 ent := zapcore.Entry{ 168 Level: zapcore.InfoLevel, 169 Time: time.Date(2018, 6, 19, 16, 33, 42, 99, time.UTC), 170 LoggerName: "bob", 171 Message: "lob law", 172 } 173 174 fields := []zapcore.Field{ 175 zap.Int("answer", 42), 176 } 177 178 _, err := enc.EncodeEntry(ent, fields) 179 assert.NoError(t, err, "Unexpected JSON encoding error.") 180 } 181 182 func TestJSONEmptyConfig(t *testing.T) { 183 tests := []struct { 184 name string 185 field zapcore.Field 186 expected string 187 }{ 188 { 189 name: "time", 190 field: zap.Time("foo", time.Unix(1591287718, 0)), // 2020-06-04 09:21:58 -0700 PDT 191 expected: `{"foo": 1591287718000000000}`, 192 }, 193 { 194 name: "duration", 195 field: zap.Duration("bar", time.Microsecond), 196 expected: `{"bar": 1000}`, 197 }, 198 } 199 200 for _, tt := range tests { 201 t.Run(tt.name, func(t *testing.T) { 202 enc := zapcore.NewJSONEncoder(zapcore.EncoderConfig{}) 203 204 buf, err := enc.EncodeEntry(zapcore.Entry{ 205 Level: zapcore.DebugLevel, 206 Time: time.Now(), 207 LoggerName: "mylogger", 208 Message: "things happened", 209 }, []zapcore.Field{tt.field}) 210 if assert.NoError(t, err, "Unexpected JSON encoding error.") { 211 assert.JSONEq(t, tt.expected, buf.String(), "Incorrect encoded JSON entry.") 212 } 213 214 buf.Free() 215 }) 216 } 217 } 218 219 // Encodes any object into empty json '{}' 220 type emptyReflectedEncoder struct { 221 writer io.Writer 222 } 223 224 func (enc *emptyReflectedEncoder) Encode(obj interface{}) error { 225 _, err := enc.writer.Write([]byte("{}")) 226 return err 227 } 228 229 func TestJSONCustomReflectedEncoder(t *testing.T) { 230 tests := []struct { 231 name string 232 field zapcore.Field 233 expected string 234 }{ 235 { 236 name: "encode custom map object", 237 field: zapcore.Field{ 238 Key: "data", 239 Type: zapcore.ReflectType, 240 Interface: map[string]interface{}{ 241 "foo": "hello", 242 "bar": 1111, 243 }, 244 }, 245 expected: `{"data":{}}`, 246 }, 247 { 248 name: "encode nil object", 249 field: zapcore.Field{ 250 Key: "data", 251 Type: zapcore.ReflectType, 252 }, 253 expected: `{"data":null}`, 254 }, 255 } 256 257 for _, tt := range tests { 258 tt := tt 259 t.Run(tt.name, func(t *testing.T) { 260 t.Parallel() 261 262 enc := zapcore.NewJSONEncoder(zapcore.EncoderConfig{ 263 NewReflectedEncoder: func(writer io.Writer) zapcore.ReflectedEncoder { 264 return &emptyReflectedEncoder{ 265 writer: writer, 266 } 267 }, 268 }) 269 270 buf, err := enc.EncodeEntry(zapcore.Entry{ 271 Level: zapcore.DebugLevel, 272 Time: time.Now(), 273 LoggerName: "logger", 274 Message: "things happened", 275 }, []zapcore.Field{tt.field}) 276 if assert.NoError(t, err, "Unexpected JSON encoding error.") { 277 assert.JSONEq(t, tt.expected, buf.String(), "Incorrect encoded JSON entry.") 278 } 279 buf.Free() 280 }) 281 } 282 }