1package zerolog 2 3import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "net" 8 "reflect" 9 "runtime" 10 "strconv" 11 "strings" 12 "testing" 13 "time" 14) 15 16func TestLog(t *testing.T) { 17 t.Run("empty", func(t *testing.T) { 18 out := &bytes.Buffer{} 19 log := New(out) 20 log.Log().Msg("") 21 if got, want := decodeIfBinaryToString(out.Bytes()), "{}\n"; got != want { 22 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 23 } 24 }) 25 26 t.Run("one-field", func(t *testing.T) { 27 out := &bytes.Buffer{} 28 log := New(out) 29 log.Log().Str("foo", "bar").Msg("") 30 if got, want := decodeIfBinaryToString(out.Bytes()), `{"foo":"bar"}`+"\n"; got != want { 31 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 32 } 33 }) 34 35 t.Run("two-field", func(t *testing.T) { 36 out := &bytes.Buffer{} 37 log := New(out) 38 log.Log(). 39 Str("foo", "bar"). 40 Int("n", 123). 41 Msg("") 42 if got, want := decodeIfBinaryToString(out.Bytes()), `{"foo":"bar","n":123}`+"\n"; got != want { 43 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 44 } 45 }) 46} 47 48func TestInfo(t *testing.T) { 49 t.Run("empty", func(t *testing.T) { 50 out := &bytes.Buffer{} 51 log := New(out) 52 log.Info().Msg("") 53 if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"info"}`+"\n"; got != want { 54 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 55 } 56 }) 57 58 t.Run("one-field", func(t *testing.T) { 59 out := &bytes.Buffer{} 60 log := New(out) 61 log.Info().Str("foo", "bar").Msg("") 62 if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"info","foo":"bar"}`+"\n"; got != want { 63 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 64 } 65 }) 66 67 t.Run("two-field", func(t *testing.T) { 68 out := &bytes.Buffer{} 69 log := New(out) 70 log.Info(). 71 Str("foo", "bar"). 72 Int("n", 123). 73 Msg("") 74 if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"info","foo":"bar","n":123}`+"\n"; got != want { 75 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 76 } 77 }) 78} 79 80func TestWith(t *testing.T) { 81 out := &bytes.Buffer{} 82 ctx := New(out).With(). 83 Str("string", "foo"). 84 Bytes("bytes", []byte("bar")). 85 Hex("hex", []byte{0x12, 0xef}). 86 RawJSON("json", []byte(`{"some":"json"}`)). 87 AnErr("some_err", nil). 88 Err(errors.New("some error")). 89 Bool("bool", true). 90 Int("int", 1). 91 Int8("int8", 2). 92 Int16("int16", 3). 93 Int32("int32", 4). 94 Int64("int64", 5). 95 Uint("uint", 6). 96 Uint8("uint8", 7). 97 Uint16("uint16", 8). 98 Uint32("uint32", 9). 99 Uint64("uint64", 10). 100 Float32("float32", 11.101). 101 Float64("float64", 12.30303). 102 Time("time", time.Time{}) 103 _, file, line, _ := runtime.Caller(0) 104 caller := fmt.Sprintf("%s:%d", file, line+3) 105 log := ctx.Caller().Logger() 106 log.Log().Msg("") 107 if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":"foo","bytes":"bar","hex":"12ef","json":{"some":"json"},"error":"some error","bool":true,"int":1,"int8":2,"int16":3,"int32":4,"int64":5,"uint":6,"uint8":7,"uint16":8,"uint32":9,"uint64":10,"float32":11.101,"float64":12.30303,"time":"0001-01-01T00:00:00Z","caller":"`+caller+`"}`+"\n"; got != want { 108 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 109 } 110 111 // Validate CallerWithSkipFrameCount. 112 out.Reset() 113 _, file, line, _ = runtime.Caller(0) 114 caller = fmt.Sprintf("%s:%d", file, line+5) 115 log = ctx.CallerWithSkipFrameCount(3).Logger() 116 func() { 117 log.Log().Msg("") 118 }() 119 // The above line is a little contrived, but the line above should be the line due 120 // to the extra frame skip. 121 if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":"foo","bytes":"bar","hex":"12ef","json":{"some":"json"},"error":"some error","bool":true,"int":1,"int8":2,"int16":3,"int32":4,"int64":5,"uint":6,"uint8":7,"uint16":8,"uint32":9,"uint64":10,"float32":11.101,"float64":12.30303,"time":"0001-01-01T00:00:00Z","caller":"`+caller+`"}`+"\n"; got != want { 122 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 123 } 124} 125 126func TestFieldsMap(t *testing.T) { 127 out := &bytes.Buffer{} 128 log := New(out) 129 log.Log().Fields(map[string]interface{}{ 130 "nil": nil, 131 "string": "foo", 132 "bytes": []byte("bar"), 133 "error": errors.New("some error"), 134 "bool": true, 135 "int": int(1), 136 "int8": int8(2), 137 "int16": int16(3), 138 "int32": int32(4), 139 "int64": int64(5), 140 "uint": uint(6), 141 "uint8": uint8(7), 142 "uint16": uint16(8), 143 "uint32": uint32(9), 144 "uint64": uint64(10), 145 "float32": float32(11), 146 "float64": float64(12), 147 "ipv6": net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34}, 148 "dur": 1 * time.Second, 149 "time": time.Time{}, 150 "obj": obj{"a", "b", 1}, 151 }).Msg("") 152 if got, want := decodeIfBinaryToString(out.Bytes()), `{"bool":true,"bytes":"bar","dur":1000,"error":"some error","float32":11,"float64":12,"int":1,"int16":3,"int32":4,"int64":5,"int8":2,"ipv6":"2001:db8:85a3::8a2e:370:7334","nil":null,"obj":{"Pub":"a","Tag":"b","priv":1},"string":"foo","time":"0001-01-01T00:00:00Z","uint":6,"uint16":8,"uint32":9,"uint64":10,"uint8":7}`+"\n"; got != want { 153 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 154 } 155} 156 157func TestFieldsMapPnt(t *testing.T) { 158 out := &bytes.Buffer{} 159 log := New(out) 160 log.Log().Fields(map[string]interface{}{ 161 "string": new(string), 162 "bool": new(bool), 163 "int": new(int), 164 "int8": new(int8), 165 "int16": new(int16), 166 "int32": new(int32), 167 "int64": new(int64), 168 "uint": new(uint), 169 "uint8": new(uint8), 170 "uint16": new(uint16), 171 "uint32": new(uint32), 172 "uint64": new(uint64), 173 "float32": new(float32), 174 "float64": new(float64), 175 "dur": new(time.Duration), 176 "time": new(time.Time), 177 }).Msg("") 178 if got, want := decodeIfBinaryToString(out.Bytes()), `{"bool":false,"dur":0,"float32":0,"float64":0,"int":0,"int16":0,"int32":0,"int64":0,"int8":0,"string":"","time":"0001-01-01T00:00:00Z","uint":0,"uint16":0,"uint32":0,"uint64":0,"uint8":0}`+"\n"; got != want { 179 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 180 } 181} 182 183func TestFieldsMapNilPnt(t *testing.T) { 184 var ( 185 stringPnt *string 186 boolPnt *bool 187 intPnt *int 188 int8Pnt *int8 189 int16Pnt *int16 190 int32Pnt *int32 191 int64Pnt *int64 192 uintPnt *uint 193 uint8Pnt *uint8 194 uint16Pnt *uint16 195 uint32Pnt *uint32 196 uint64Pnt *uint64 197 float32Pnt *float32 198 float64Pnt *float64 199 durPnt *time.Duration 200 timePnt *time.Time 201 ) 202 out := &bytes.Buffer{} 203 log := New(out) 204 fields := map[string]interface{}{ 205 "string": stringPnt, 206 "bool": boolPnt, 207 "int": intPnt, 208 "int8": int8Pnt, 209 "int16": int16Pnt, 210 "int32": int32Pnt, 211 "int64": int64Pnt, 212 "uint": uintPnt, 213 "uint8": uint8Pnt, 214 "uint16": uint16Pnt, 215 "uint32": uint32Pnt, 216 "uint64": uint64Pnt, 217 "float32": float32Pnt, 218 "float64": float64Pnt, 219 "dur": durPnt, 220 "time": timePnt, 221 } 222 223 log.Log().Fields(fields).Msg("") 224 if got, want := decodeIfBinaryToString(out.Bytes()), `{"bool":null,"dur":null,"float32":null,"float64":null,"int":null,"int16":null,"int32":null,"int64":null,"int8":null,"string":null,"time":null,"uint":null,"uint16":null,"uint32":null,"uint64":null,"uint8":null}`+"\n"; got != want { 225 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 226 } 227} 228 229func TestFields(t *testing.T) { 230 out := &bytes.Buffer{} 231 log := New(out) 232 now := time.Now() 233 _, file, line, _ := runtime.Caller(0) 234 caller := fmt.Sprintf("%s:%d", file, line+3) 235 log.Log(). 236 Caller(). 237 Str("string", "foo"). 238 Bytes("bytes", []byte("bar")). 239 Hex("hex", []byte{0x12, 0xef}). 240 RawJSON("json", []byte(`{"some":"json"}`)). 241 AnErr("some_err", nil). 242 Err(errors.New("some error")). 243 Bool("bool", true). 244 Int("int", 1). 245 Int8("int8", 2). 246 Int16("int16", 3). 247 Int32("int32", 4). 248 Int64("int64", 5). 249 Uint("uint", 6). 250 Uint8("uint8", 7). 251 Uint16("uint16", 8). 252 Uint32("uint32", 9). 253 Uint64("uint64", 10). 254 IPAddr("IPv4", net.IP{192, 168, 0, 100}). 255 IPAddr("IPv6", net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34}). 256 MACAddr("Mac", net.HardwareAddr{0x00, 0x14, 0x22, 0x01, 0x23, 0x45}). 257 IPPrefix("Prefix", net.IPNet{IP: net.IP{192, 168, 0, 100}, Mask: net.CIDRMask(24, 32)}). 258 Float32("float32", 11.1234). 259 Float64("float64", 12.321321321). 260 Dur("dur", 1*time.Second). 261 Time("time", time.Time{}). 262 TimeDiff("diff", now, now.Add(-10*time.Second)). 263 Msg("") 264 if got, want := decodeIfBinaryToString(out.Bytes()), `{"caller":"`+caller+`","string":"foo","bytes":"bar","hex":"12ef","json":{"some":"json"},"error":"some error","bool":true,"int":1,"int8":2,"int16":3,"int32":4,"int64":5,"uint":6,"uint8":7,"uint16":8,"uint32":9,"uint64":10,"IPv4":"192.168.0.100","IPv6":"2001:db8:85a3::8a2e:370:7334","Mac":"00:14:22:01:23:45","Prefix":"192.168.0.100/24","float32":11.1234,"float64":12.321321321,"dur":1000,"time":"0001-01-01T00:00:00Z","diff":10000}`+"\n"; got != want { 265 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 266 } 267} 268 269func TestFieldsArrayEmpty(t *testing.T) { 270 out := &bytes.Buffer{} 271 log := New(out) 272 log.Log(). 273 Strs("string", []string{}). 274 Errs("err", []error{}). 275 Bools("bool", []bool{}). 276 Ints("int", []int{}). 277 Ints8("int8", []int8{}). 278 Ints16("int16", []int16{}). 279 Ints32("int32", []int32{}). 280 Ints64("int64", []int64{}). 281 Uints("uint", []uint{}). 282 Uints8("uint8", []uint8{}). 283 Uints16("uint16", []uint16{}). 284 Uints32("uint32", []uint32{}). 285 Uints64("uint64", []uint64{}). 286 Floats32("float32", []float32{}). 287 Floats64("float64", []float64{}). 288 Durs("dur", []time.Duration{}). 289 Times("time", []time.Time{}). 290 Msg("") 291 if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":[],"err":[],"bool":[],"int":[],"int8":[],"int16":[],"int32":[],"int64":[],"uint":[],"uint8":[],"uint16":[],"uint32":[],"uint64":[],"float32":[],"float64":[],"dur":[],"time":[]}`+"\n"; got != want { 292 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 293 } 294} 295 296func TestFieldsArraySingleElement(t *testing.T) { 297 out := &bytes.Buffer{} 298 log := New(out) 299 log.Log(). 300 Strs("string", []string{"foo"}). 301 Errs("err", []error{errors.New("some error")}). 302 Bools("bool", []bool{true}). 303 Ints("int", []int{1}). 304 Ints8("int8", []int8{2}). 305 Ints16("int16", []int16{3}). 306 Ints32("int32", []int32{4}). 307 Ints64("int64", []int64{5}). 308 Uints("uint", []uint{6}). 309 Uints8("uint8", []uint8{7}). 310 Uints16("uint16", []uint16{8}). 311 Uints32("uint32", []uint32{9}). 312 Uints64("uint64", []uint64{10}). 313 Floats32("float32", []float32{11}). 314 Floats64("float64", []float64{12}). 315 Durs("dur", []time.Duration{1 * time.Second}). 316 Times("time", []time.Time{time.Time{}}). 317 Msg("") 318 if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":["foo"],"err":["some error"],"bool":[true],"int":[1],"int8":[2],"int16":[3],"int32":[4],"int64":[5],"uint":[6],"uint8":[7],"uint16":[8],"uint32":[9],"uint64":[10],"float32":[11],"float64":[12],"dur":[1000],"time":["0001-01-01T00:00:00Z"]}`+"\n"; got != want { 319 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 320 } 321} 322 323func TestFieldsArrayMultipleElement(t *testing.T) { 324 out := &bytes.Buffer{} 325 log := New(out) 326 log.Log(). 327 Strs("string", []string{"foo", "bar"}). 328 Errs("err", []error{errors.New("some error"), nil}). 329 Bools("bool", []bool{true, false}). 330 Ints("int", []int{1, 0}). 331 Ints8("int8", []int8{2, 0}). 332 Ints16("int16", []int16{3, 0}). 333 Ints32("int32", []int32{4, 0}). 334 Ints64("int64", []int64{5, 0}). 335 Uints("uint", []uint{6, 0}). 336 Uints8("uint8", []uint8{7, 0}). 337 Uints16("uint16", []uint16{8, 0}). 338 Uints32("uint32", []uint32{9, 0}). 339 Uints64("uint64", []uint64{10, 0}). 340 Floats32("float32", []float32{11, 0}). 341 Floats64("float64", []float64{12, 0}). 342 Durs("dur", []time.Duration{1 * time.Second, 0}). 343 Times("time", []time.Time{time.Time{}, time.Time{}}). 344 Msg("") 345 if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":["foo","bar"],"err":["some error",null],"bool":[true,false],"int":[1,0],"int8":[2,0],"int16":[3,0],"int32":[4,0],"int64":[5,0],"uint":[6,0],"uint8":[7,0],"uint16":[8,0],"uint32":[9,0],"uint64":[10,0],"float32":[11,0],"float64":[12,0],"dur":[1000,0],"time":["0001-01-01T00:00:00Z","0001-01-01T00:00:00Z"]}`+"\n"; got != want { 346 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 347 } 348} 349 350func TestFieldsDisabled(t *testing.T) { 351 out := &bytes.Buffer{} 352 log := New(out).Level(InfoLevel) 353 now := time.Now() 354 log.Debug(). 355 Str("string", "foo"). 356 Bytes("bytes", []byte("bar")). 357 Hex("hex", []byte{0x12, 0xef}). 358 AnErr("some_err", nil). 359 Err(errors.New("some error")). 360 Bool("bool", true). 361 Int("int", 1). 362 Int8("int8", 2). 363 Int16("int16", 3). 364 Int32("int32", 4). 365 Int64("int64", 5). 366 Uint("uint", 6). 367 Uint8("uint8", 7). 368 Uint16("uint16", 8). 369 Uint32("uint32", 9). 370 Uint64("uint64", 10). 371 Float32("float32", 11). 372 Float64("float64", 12). 373 Dur("dur", 1*time.Second). 374 Time("time", time.Time{}). 375 TimeDiff("diff", now, now.Add(-10*time.Second)). 376 Msg("") 377 if got, want := decodeIfBinaryToString(out.Bytes()), ""; got != want { 378 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 379 } 380} 381 382func TestMsgf(t *testing.T) { 383 out := &bytes.Buffer{} 384 log := New(out) 385 log.Log().Msgf("one %s %.1f %d %v", "two", 3.4, 5, errors.New("six")) 386 if got, want := decodeIfBinaryToString(out.Bytes()), `{"message":"one two 3.4 5 six"}`+"\n"; got != want { 387 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 388 } 389} 390 391func TestWithAndFieldsCombined(t *testing.T) { 392 out := &bytes.Buffer{} 393 log := New(out).With().Str("f1", "val").Str("f2", "val").Logger() 394 log.Log().Str("f3", "val").Msg("") 395 if got, want := decodeIfBinaryToString(out.Bytes()), `{"f1":"val","f2":"val","f3":"val"}`+"\n"; got != want { 396 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 397 } 398} 399 400func TestLevel(t *testing.T) { 401 t.Run("Disabled", func(t *testing.T) { 402 out := &bytes.Buffer{} 403 log := New(out).Level(Disabled) 404 log.Info().Msg("test") 405 if got, want := decodeIfBinaryToString(out.Bytes()), ""; got != want { 406 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 407 } 408 }) 409 410 t.Run("NoLevel/Disabled", func(t *testing.T) { 411 out := &bytes.Buffer{} 412 log := New(out).Level(Disabled) 413 log.Log().Msg("test") 414 if got, want := decodeIfBinaryToString(out.Bytes()), ""; got != want { 415 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 416 } 417 }) 418 419 t.Run("NoLevel/Info", func(t *testing.T) { 420 out := &bytes.Buffer{} 421 log := New(out).Level(InfoLevel) 422 log.Log().Msg("test") 423 if got, want := decodeIfBinaryToString(out.Bytes()), `{"message":"test"}`+"\n"; got != want { 424 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 425 } 426 }) 427 428 t.Run("NoLevel/Panic", func(t *testing.T) { 429 out := &bytes.Buffer{} 430 log := New(out).Level(PanicLevel) 431 log.Log().Msg("test") 432 if got, want := decodeIfBinaryToString(out.Bytes()), `{"message":"test"}`+"\n"; got != want { 433 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 434 } 435 }) 436 437 t.Run("NoLevel/WithLevel", func(t *testing.T) { 438 out := &bytes.Buffer{} 439 log := New(out).Level(InfoLevel) 440 log.WithLevel(NoLevel).Msg("test") 441 if got, want := decodeIfBinaryToString(out.Bytes()), `{"message":"test"}`+"\n"; got != want { 442 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 443 } 444 }) 445 446 t.Run("Info", func(t *testing.T) { 447 out := &bytes.Buffer{} 448 log := New(out).Level(InfoLevel) 449 log.Info().Msg("test") 450 if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"info","message":"test"}`+"\n"; got != want { 451 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 452 } 453 }) 454} 455 456func TestSampling(t *testing.T) { 457 out := &bytes.Buffer{} 458 log := New(out).Sample(&BasicSampler{N: 2}) 459 log.Log().Int("i", 1).Msg("") 460 log.Log().Int("i", 2).Msg("") 461 log.Log().Int("i", 3).Msg("") 462 log.Log().Int("i", 4).Msg("") 463 if got, want := decodeIfBinaryToString(out.Bytes()), "{\"i\":1}\n{\"i\":3}\n"; got != want { 464 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 465 } 466} 467 468func TestDiscard(t *testing.T) { 469 out := &bytes.Buffer{} 470 log := New(out) 471 log.Log().Discard().Str("a", "b").Msgf("one %s %.1f %d %v", "two", 3.4, 5, errors.New("six")) 472 if got, want := decodeIfBinaryToString(out.Bytes()), ""; got != want { 473 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 474 } 475 476 // Double call 477 log.Log().Discard().Discard().Str("a", "b").Msgf("one %s %.1f %d %v", "two", 3.4, 5, errors.New("six")) 478 if got, want := decodeIfBinaryToString(out.Bytes()), ""; got != want { 479 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 480 } 481} 482 483type levelWriter struct { 484 ops []struct { 485 l Level 486 p string 487 } 488} 489 490func (lw *levelWriter) Write(p []byte) (int, error) { 491 return len(p), nil 492} 493 494func (lw *levelWriter) WriteLevel(lvl Level, p []byte) (int, error) { 495 p = decodeIfBinaryToBytes(p) 496 lw.ops = append(lw.ops, struct { 497 l Level 498 p string 499 }{lvl, string(p)}) 500 return len(p), nil 501} 502 503func TestLevelWriter(t *testing.T) { 504 lw := &levelWriter{ 505 ops: []struct { 506 l Level 507 p string 508 }{}, 509 } 510 log := New(lw) 511 log.Debug().Msg("1") 512 log.Info().Msg("2") 513 log.Warn().Msg("3") 514 log.Error().Msg("4") 515 log.Log().Msg("nolevel-1") 516 log.WithLevel(DebugLevel).Msg("5") 517 log.WithLevel(InfoLevel).Msg("6") 518 log.WithLevel(WarnLevel).Msg("7") 519 log.WithLevel(ErrorLevel).Msg("8") 520 log.WithLevel(NoLevel).Msg("nolevel-2") 521 522 want := []struct { 523 l Level 524 p string 525 }{ 526 {DebugLevel, `{"level":"debug","message":"1"}` + "\n"}, 527 {InfoLevel, `{"level":"info","message":"2"}` + "\n"}, 528 {WarnLevel, `{"level":"warn","message":"3"}` + "\n"}, 529 {ErrorLevel, `{"level":"error","message":"4"}` + "\n"}, 530 {NoLevel, `{"message":"nolevel-1"}` + "\n"}, 531 {DebugLevel, `{"level":"debug","message":"5"}` + "\n"}, 532 {InfoLevel, `{"level":"info","message":"6"}` + "\n"}, 533 {WarnLevel, `{"level":"warn","message":"7"}` + "\n"}, 534 {ErrorLevel, `{"level":"error","message":"8"}` + "\n"}, 535 {NoLevel, `{"message":"nolevel-2"}` + "\n"}, 536 } 537 if got := lw.ops; !reflect.DeepEqual(got, want) { 538 t.Errorf("invalid ops:\ngot:\n%v\nwant:\n%v", got, want) 539 } 540} 541 542func TestContextTimestamp(t *testing.T) { 543 TimestampFunc = func() time.Time { 544 return time.Date(2001, time.February, 3, 4, 5, 6, 7, time.UTC) 545 } 546 defer func() { 547 TimestampFunc = time.Now 548 }() 549 out := &bytes.Buffer{} 550 log := New(out).With().Timestamp().Str("foo", "bar").Logger() 551 log.Log().Msg("hello world") 552 553 if got, want := decodeIfBinaryToString(out.Bytes()), `{"foo":"bar","time":"2001-02-03T04:05:06Z","message":"hello world"}`+"\n"; got != want { 554 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 555 } 556} 557 558func TestEventTimestamp(t *testing.T) { 559 TimestampFunc = func() time.Time { 560 return time.Date(2001, time.February, 3, 4, 5, 6, 7, time.UTC) 561 } 562 defer func() { 563 TimestampFunc = time.Now 564 }() 565 out := &bytes.Buffer{} 566 log := New(out).With().Str("foo", "bar").Logger() 567 log.Log().Timestamp().Msg("hello world") 568 569 if got, want := decodeIfBinaryToString(out.Bytes()), `{"foo":"bar","time":"2001-02-03T04:05:06Z","message":"hello world"}`+"\n"; got != want { 570 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 571 } 572} 573 574func TestOutputWithoutTimestamp(t *testing.T) { 575 ignoredOut := &bytes.Buffer{} 576 out := &bytes.Buffer{} 577 log := New(ignoredOut).Output(out).With().Str("foo", "bar").Logger() 578 log.Log().Msg("hello world") 579 580 if got, want := decodeIfBinaryToString(out.Bytes()), `{"foo":"bar","message":"hello world"}`+"\n"; got != want { 581 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 582 } 583} 584 585func TestOutputWithTimestamp(t *testing.T) { 586 TimestampFunc = func() time.Time { 587 return time.Date(2001, time.February, 3, 4, 5, 6, 7, time.UTC) 588 } 589 defer func() { 590 TimestampFunc = time.Now 591 }() 592 ignoredOut := &bytes.Buffer{} 593 out := &bytes.Buffer{} 594 log := New(ignoredOut).Output(out).With().Timestamp().Str("foo", "bar").Logger() 595 log.Log().Msg("hello world") 596 597 if got, want := decodeIfBinaryToString(out.Bytes()), `{"foo":"bar","time":"2001-02-03T04:05:06Z","message":"hello world"}`+"\n"; got != want { 598 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 599 } 600} 601 602type loggableError struct { 603 error 604} 605 606func (l loggableError) MarshalZerologObject(e *Event) { 607 e.Str("message", l.error.Error()+": loggableError") 608} 609 610func TestErrorMarshalFunc(t *testing.T) { 611 out := &bytes.Buffer{} 612 log := New(out) 613 614 // test default behaviour 615 log.Log().Err(errors.New("err")).Msg("msg") 616 if got, want := decodeIfBinaryToString(out.Bytes()), `{"error":"err","message":"msg"}`+"\n"; got != want { 617 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 618 } 619 out.Reset() 620 621 log.Log().Err(loggableError{errors.New("err")}).Msg("msg") 622 if got, want := decodeIfBinaryToString(out.Bytes()), `{"error":{"message":"err: loggableError"},"message":"msg"}`+"\n"; got != want { 623 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 624 } 625 out.Reset() 626 627 // test overriding the ErrorMarshalFunc 628 originalErrorMarshalFunc := ErrorMarshalFunc 629 defer func() { 630 ErrorMarshalFunc = originalErrorMarshalFunc 631 }() 632 633 ErrorMarshalFunc = func(err error) interface{} { 634 return err.Error() + ": marshaled string" 635 } 636 log.Log().Err(errors.New("err")).Msg("msg") 637 if got, want := decodeIfBinaryToString(out.Bytes()), `{"error":"err: marshaled string","message":"msg"}`+"\n"; got != want { 638 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 639 } 640 641 out.Reset() 642 ErrorMarshalFunc = func(err error) interface{} { 643 return errors.New(err.Error() + ": new error") 644 } 645 log.Log().Err(errors.New("err")).Msg("msg") 646 if got, want := decodeIfBinaryToString(out.Bytes()), `{"error":"err: new error","message":"msg"}`+"\n"; got != want { 647 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 648 } 649 650 out.Reset() 651 ErrorMarshalFunc = func(err error) interface{} { 652 return loggableError{err} 653 } 654 log.Log().Err(errors.New("err")).Msg("msg") 655 if got, want := decodeIfBinaryToString(out.Bytes()), `{"error":{"message":"err: loggableError"},"message":"msg"}`+"\n"; got != want { 656 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 657 } 658} 659 660func TestCallerMarshalFunc(t *testing.T) { 661 out := &bytes.Buffer{} 662 log := New(out) 663 664 // test default behaviour this is really brittle due to the line numbers 665 // actually mattering for validation 666 _, file, line, _ := runtime.Caller(0) 667 caller := fmt.Sprintf("%s:%d", file, line+2) 668 log.Log().Caller().Msg("msg") 669 if got, want := decodeIfBinaryToString(out.Bytes()), `{"caller":"`+caller+`","message":"msg"}`+"\n"; got != want { 670 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 671 } 672 out.Reset() 673 674 // test custom behavior. In this case we'll take just the last directory 675 origCallerMarshalFunc := CallerMarshalFunc 676 defer func() { CallerMarshalFunc = origCallerMarshalFunc }() 677 CallerMarshalFunc = func(file string, line int) string { 678 parts := strings.Split(file, "/") 679 if len(parts) > 1 { 680 return strings.Join(parts[len(parts)-2:], "/") + ":" + strconv.Itoa(line) 681 } else { 682 return file + ":" + strconv.Itoa(line) 683 } 684 } 685 _, file, line, _ = runtime.Caller(0) 686 caller = CallerMarshalFunc(file, line+2) 687 log.Log().Caller().Msg("msg") 688 if got, want := decodeIfBinaryToString(out.Bytes()), `{"caller":"`+caller+`","message":"msg"}`+"\n"; got != want { 689 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 690 } 691} 692 693func TestLevelFieldMarshalFunc(t *testing.T) { 694 origLevelFieldMarshalFunc := LevelFieldMarshalFunc 695 LevelFieldMarshalFunc = func(l Level) string { 696 return strings.ToUpper(l.String()) 697 } 698 defer func() { 699 LevelFieldMarshalFunc = origLevelFieldMarshalFunc 700 }() 701 out := &bytes.Buffer{} 702 log := New(out) 703 704 log.Debug().Msg("test") 705 if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"DEBUG","message":"test"}`+"\n"; got != want { 706 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 707 } 708 out.Reset() 709 710 log.Info().Msg("test") 711 if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"INFO","message":"test"}`+"\n"; got != want { 712 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 713 } 714 out.Reset() 715 716 log.Warn().Msg("test") 717 if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"WARN","message":"test"}`+"\n"; got != want { 718 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 719 } 720 out.Reset() 721 722 log.Error().Msg("test") 723 if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"ERROR","message":"test"}`+"\n"; got != want { 724 t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want) 725 } 726 out.Reset() 727} 728 729type errWriter struct { 730 error 731} 732 733func (w errWriter) Write(p []byte) (n int, err error) { 734 return 0, w.error 735} 736 737func TestErrorHandler(t *testing.T) { 738 var got error 739 want := errors.New("write error") 740 ErrorHandler = func(err error) { 741 got = err 742 } 743 log := New(errWriter{want}) 744 log.Log().Msg("test") 745 if got != want { 746 t.Errorf("ErrorHandler err = %#v, want %#v", got, want) 747 } 748} 749