github.com/phuslu/log@v1.0.100/logger_test.go (about) 1 package log 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "errors" 7 "fmt" 8 "io" 9 "net" 10 "os" 11 "strings" 12 "testing" 13 "time" 14 ) 15 16 func TestLoggerDefault(t *testing.T) { 17 notTest = false 18 19 DefaultLogger.Caller = 1 20 Trace().Str("foo", "bar").Msg("hello from Trace") 21 Debug().Str("foo", "bar").Msg("hello from Debug") 22 Info().Str("foo", "bar").Msg("hello from Info") 23 24 DefaultLogger.Caller = -1 25 Warn().Str("foo", "bar").Msg("hello from Warn") 26 Error().Str("foo", "bar").Msg("hello from Error") 27 Fatal().Str("foo", "bar").Msg("hello from Fatal") 28 Panic().Str("foo", "bar").Msg("hello from Panic") 29 30 DefaultLogger.Caller = 0 31 Printf("hello from %s", "Printf") 32 } 33 34 func TestLoggerInfo(t *testing.T) { 35 ipv4Addr, ipv4Net, err := net.ParseCIDR("192.0.2.1/24") 36 if err != nil { 37 t.Fatalf("net.ParseCIDR error: %+v", err) 38 } 39 40 logger := Logger{ 41 Level: ParseLevel("debug"), 42 } 43 logger.Info(). 44 Caller(-1). 45 Bool("bool", true). 46 Bools("bools", []bool{false}). 47 Bools("bools", []bool{true, false}). 48 Dur("1_sec", time.Second+2*time.Millisecond+30*time.Microsecond+400*time.Nanosecond). 49 Dur("1_sec", -time.Second+2*time.Millisecond+30*time.Microsecond+400*time.Nanosecond). 50 Durs("hour_minute_second", []time.Duration{time.Hour, time.Minute, time.Second, -time.Second}). 51 Err(errors.New("test error")). 52 Err(nil). 53 AnErr("an_error", fmt.Errorf("an %w", errors.New("test error"))). 54 AnErr("an_error", nil). 55 Int64("goid", Goid()). 56 Float32("float32", 1.111). 57 Floats32("float32", []float32{1.111}). 58 Floats32("float32", []float32{1.111, 2.222}). 59 Float64("float64", 1.111). 60 Floats64("float64", []float64{1.111, 2.222}). 61 Uint64("uint64", 1234567890). 62 Uint32("uint32", 123). 63 Uint16("uint16", 123). 64 Uint8("uint8", 123). 65 Int64("int64", 1234567890). 66 Int32("int32", 123). 67 Int16("int16", 123). 68 Int8("int8", 123). 69 Int("int", 123). 70 Uints64("uints64", []uint64{1234567890, 1234567890}). 71 Uints32("uints32", []uint32{123, 123}). 72 Uints16("uints16", []uint16{123, 123}). 73 Uints8("uints8", []uint8{123, 123}). 74 Uints("uints", []uint{123, 123}). 75 Ints64("ints64", []int64{1234567890, 1234567890}). 76 Ints32("ints32", []int32{123, 123}). 77 Ints16("ints16", []int16{123, 123}). 78 Ints8("ints8", []int8{123, 123}). 79 Ints("ints", []int{123, 123}). 80 Func(func(e *Entry) { e.Str("func", "func_output") }). 81 RawJSON("raw_json", []byte("{\"a\":1,\"b\":2}")). 82 RawJSONStr("raw_json", "{\"c\":1,\"d\":2}"). 83 Hex("hex", []byte("\"<>?'")). 84 Bytes("bytes1", []byte("bytes1")). 85 Bytes("bytes2", []byte("\"<>?'")). 86 BytesOrNil("bytes3", []byte("\"<>?'")). 87 Bytes("nil_bytes_1", nil). 88 BytesOrNil("nil_bytes_2", nil). 89 Str("foobar", "\"\\\t\r\n\f\b\x00<>?'"). 90 Strs("strings", []string{"a", "b", "\"<>?'"}). 91 Stringer("stringer", nil). 92 Stringer("stringer", ipv4Addr). 93 GoStringer("gostringer", nil). 94 GoStringer("gostringer", binary.BigEndian). 95 Time("now_1", timeNow().In(time.FixedZone("UTC-7", -7*60*60))). 96 Times("now_2", []time.Time{timeNow(), timeNow()}). 97 TimeFormat("now_3", time.RFC3339, timeNow()). 98 TimeFormat("now_3_1", TimeFormatUnix, timeNow()). 99 TimeFormat("now_3_2", TimeFormatUnixMs, timeNow()). 100 TimeFormat("now_3_3", TimeFormatUnixWithMs, timeNow()). 101 TimesFormat("now_4", time.RFC3339, []time.Time{timeNow(), timeNow()}). 102 TimeDiff("time_diff_1", timeNow().Add(time.Second), timeNow()). 103 TimeDiff("time_diff_2", time.Time{}, timeNow()). 104 Type("ip_type", ipv4Addr). 105 Stringer("ip_str", ipv4Addr). 106 GoStringer("big_edian", binary.BigEndian). 107 IPAddr("ip6", net.ParseIP("2001:4860:4860::8888")). 108 IPAddr("ip4", ipv4Addr). 109 IPPrefix("ip_prefix", *ipv4Net). 110 MACAddr("mac", net.HardwareAddr{0x00, 0x00, 0x5e, 0x00, 0x53, 0x01}). 111 Xid("xid", NewXID()). 112 Errs("errors", []error{errors.New("error1"), nil, errors.New("error3")}). 113 Interface("console_writer", ConsoleWriter{ColorOutput: true}). 114 Interface("time.Time", timeNow()). 115 KeysAndValues("foo", "bar", "number", 42). 116 Msgf("this is a \"%s\"", "test") 117 } 118 119 func TestLoggerNil(t *testing.T) { 120 e := Info() 121 e.Caller(1).Str("foo", "bar").Int("num", 42).Msgf("this is a nil entry test") 122 123 ipv4Addr, ipv4Net, err := net.ParseCIDR("192.0.2.1/24") 124 if err != nil { 125 t.Fatalf("net.ParseCIDR error: %+v", err) 126 } 127 128 logger := Logger{ 129 Level: ParseLevel("info"), 130 } 131 logger.Debug(). 132 Caller(1). 133 Bool("bool", true). 134 Bools("bools", []bool{true, false}). 135 Dur("1_hour", time.Hour). 136 Durs("hour_minute_second", []time.Duration{time.Hour, time.Minute, time.Second}). 137 Err(errors.New("test error")). 138 Err(nil). 139 AnErr("an_error", fmt.Errorf("an %w", errors.New("test error"))). 140 AnErr("an_error", nil). 141 Float32("float32", 1.111). 142 Floats32("float32", []float32{1.111}). 143 Float64("float64", 1.111). 144 Floats64("float64", []float64{1.111}). 145 Floats64("float64", []float64{1.111}). 146 Uint64("uint64", 1234567890). 147 Uint32("uint32", 123). 148 Uint16("uint16", 123). 149 Uint8("uint8", 123). 150 Int64("int64", 1234567890). 151 Int32("int32", 123). 152 Int16("int16", 123). 153 Int8("int8", 123). 154 Int("int", 123). 155 Uints64("uints64", []uint64{1234567890, 1234567890}). 156 Uints32("uints32", []uint32{123, 123}). 157 Uints16("uints16", []uint16{123, 123}). 158 Uints8("uints8", []uint8{123, 123}). 159 Uints("uints", []uint{123, 123}). 160 Ints64("ints64", []int64{1234567890, 1234567890}). 161 Ints32("ints32", []int32{123, 123}). 162 Ints16("ints16", []int16{123, 123}). 163 Ints8("ints8", []int8{123, 123}). 164 Ints("ints", []int{123, 123}). 165 Func(func(e *Entry) { e.Str("func", "func_output") }). 166 RawJSON("raw_json", []byte("{\"a\":1,\"b\":2}")). 167 RawJSONStr("raw_json", "{\"c\":1,\"d\":2}"). 168 Hex("hex", []byte("\"<>?'")). 169 Bytes("bytes1", []byte("bytes1")). 170 Bytes("bytes2", []byte("\"<>?'")). 171 BytesOrNil("bytes3", []byte("\"<>?'")). 172 BytesOrNil("bytes4", nil). 173 Byte("zero", 0). 174 Str("foobar", "\"\\\t\r\n\f\b\x00<>?'"). 175 Strs("strings", []string{"a", "b", "\"<>?'"}). 176 Stringer("stringer", nil). 177 Stringer("stringer", ipv4Addr). 178 GoStringer("gostringer", nil). 179 GoStringer("gostringer", binary.BigEndian). 180 Time("now_1", timeNow()). 181 Times("now_2", []time.Time{timeNow(), timeNow()}). 182 TimeFormat("now_3", time.RFC3339, timeNow()). 183 TimesFormat("now_4", time.RFC3339, []time.Time{timeNow(), timeNow()}). 184 TimeDiff("time_diff_1", timeNow().Add(time.Second), timeNow()). 185 TimeDiff("time_diff_2", time.Time{}, timeNow()). 186 IPAddr("ip6", net.ParseIP("2001:4860:4860::8888")). 187 Type("ip_type", ipv4Addr). 188 IPAddr("ip4", ipv4Addr). 189 IPPrefix("ip_prefix", *ipv4Net). 190 MACAddr("mac", net.HardwareAddr{0x00, 0x00, 0x5e, 0x00, 0x53, 0x01}). 191 Xid("xid", NewXID()). 192 Errs("errors", []error{errors.New("error1"), nil, errors.New("error3")}). 193 Interface("console_writer", ConsoleWriter{ColorOutput: true}). 194 Interface("time.Time", timeNow()). 195 KeysAndValues("foo", "bar", "number", 42). 196 Msgf("this is a \"%s\"", "test") 197 } 198 199 func TestLoggerInterface(t *testing.T) { 200 logger := Logger{ 201 Level: ParseLevel("debug"), 202 } 203 204 var normalStruct = struct { 205 Rate string 206 Low int 207 High float32 208 }{"15", 16, 123.2} 209 210 var cyclicStruct struct { 211 Value any 212 } 213 214 cyclicStruct.Value = &cyclicStruct 215 216 logger.Info(). 217 Caller(-1). 218 Interface("a", 1). 219 Interface("b", 1.2). 220 Interface("c", "str"). 221 Interface("a_normal_struct", normalStruct). 222 Interface("a_cyclic_struct", cyclicStruct). 223 Msgf("this is a cyclic struct test") 224 } 225 226 type testMarshalObject struct { 227 I int 228 N string 229 } 230 231 func (o *testMarshalObject) MarshalObject(e *Entry) { 232 e.Int("id", o.I).Str("name", o.N) 233 } 234 235 type nullMarshalObject struct { 236 I int 237 N string 238 } 239 240 func (o *nullMarshalObject) MarshalObject(e *Entry) { 241 e.Int("i", o.I).Str("n", o.N) 242 } 243 244 func TestLoggerObject(t *testing.T) { 245 logger := Logger{ 246 Level: ParseLevel("debug"), 247 } 248 249 logger.Info().Object("test_object", &testMarshalObject{1, "foo"}).Msg("this is a object test") 250 logger.Info().EmbedObject(&testMarshalObject{1, "foo"}).Msg("this is a object test") 251 logger.Info().Object("empty_object", nil).Msg("this is a empty_object test") 252 logger.Info().EmbedObject(nil).Msg("this is a empty_object test") 253 254 logger.Info().Object("null_object", &nullMarshalObject{3, "xxx"}).Msg("this is a empty_object test") 255 logger.Info().EmbedObject(&nullMarshalObject{3, "xxx"}).Msg("this is a empty_object test") 256 257 var nilObjct *nullMarshalObject 258 var nilIface ObjectMarshaler = nilObjct 259 logger.Info().Object("null_object_2", nilIface).Msg("this is a null_object_2 test") 260 logger.Info().EmbedObject(nilIface).Msg("this is a null_object_2 test") 261 } 262 263 func TestLoggerObjects(t *testing.T) { 264 logger := Logger{ 265 Level: ParseLevel("debug"), 266 } 267 268 logger.Info().Objects("bad_objects", "1234").Msg("this is a anys test") 269 270 objects1 := []*testMarshalObject{ 271 &testMarshalObject{1, "foo"}, 272 &testMarshalObject{2, "bar"}, 273 nil, 274 } 275 logger.Info().Objects("objects1", objects1).Msg("this is a objects1 test") 276 277 var nullObject ObjectMarshaler 278 objects2 := []any{ 279 nullObject, 280 &testMarshalObject{3, "ok"}, 281 nil, 282 "1234", 283 &testMarshalObject{4, "haha"}, 284 } 285 logger.Info().Objects("objects2", objects2).Msg("this is a objects2 test") 286 287 type TestObjects []*testMarshalObject 288 objects3 := TestObjects{ 289 nil, 290 &testMarshalObject{3, "ok"}, 291 nil, 292 &testMarshalObject{4, "haha"}, 293 nil, 294 } 295 logger.Info().Objects("objects3", objects3).Msg("this is a objects2 test") 296 } 297 298 func TestLoggerLog(t *testing.T) { 299 logger := Logger{ 300 Level: ParseLevel("debug"), 301 } 302 303 logger.Log().Msgf("this is a no level log") 304 305 logger.Caller = 1 306 logger.Log().Msgf("this is a no level log with caller") 307 } 308 309 func TestLoggerByte(t *testing.T) { 310 logger := Logger{ 311 Level: ParseLevel("debug"), 312 } 313 314 logger.Info().Byte("gender", 'm').Msg("") 315 logger.Info().Byte("quote", '"').Msg("") 316 logger.Info().Byte("reverse", '\\').Msg("") 317 logger.Info().Byte("cf", '\n').Msg("") 318 logger.Info().Byte("cr", '\r').Msg("") 319 logger.Info().Byte("tab", '\t').Msg("") 320 logger.Info().Byte("forward", '\f').Msg("") 321 logger.Info().Byte("back", '\b').Msg("") 322 logger.Info().Byte("less", '<').Msg("") 323 logger.Info().Byte("singlequote", '\'').Msg("") 324 logger.Info().Byte("zerobyte", '\x00').Msg("") 325 } 326 327 func TestLoggerSetLevel(t *testing.T) { 328 DefaultLogger.SetLevel(InfoLevel) 329 Warn().Msg("1. i am a warn log") 330 Info().Msg("2. i am a info log") 331 Debug().Msg("3. i am a debug log") 332 Trace().Msg("3. i am a trace log") 333 DefaultLogger.SetLevel(TraceLevel) 334 Info().Msg("4. i am a info log") 335 Debug().Msg("5. i am a debug log") 336 Trace().Msg("5. i am a trace log") 337 } 338 339 func TestLoggerStack(t *testing.T) { 340 Info().Stack().Msg("this is single stack log entry") 341 } 342 343 func TestLoggerEnabled(t *testing.T) { 344 DefaultLogger.SetLevel(InfoLevel) 345 Debug().Stack().Msgf("hello %s", "world") 346 if Debug().Enabled() { 347 t.Fatal("debug level should enabled") 348 } 349 } 350 351 func TestLoggerDiscard(t *testing.T) { 352 Info().Stack().Str("foo", "bar").Discard() 353 DefaultLogger.SetLevel(InfoLevel) 354 Debug().Stack().Str("foo", "bar").Discard() 355 } 356 357 func TestLoggerWithLevel(t *testing.T) { 358 DefaultLogger.WithLevel(InfoLevel).Msg("this is with level log entry") 359 DefaultLogger.Caller = 1 360 DefaultLogger.WithLevel(InfoLevel).Msg("this is with level caller log entry") 361 } 362 363 func TestLoggerCaller(t *testing.T) { 364 notTest = false 365 366 DefaultLogger.Caller = 1 367 DefaultLogger.SetLevel(TraceLevel) 368 Trace().Str("foo", "bar").Msg("hello from Trace") 369 Debug().Str("foo", "bar").Msg("hello from Debug") 370 Info().Str("foo", "bar").Msg("hello from Info") 371 Warn().Str("foo", "bar").Msg("hello from Warn") 372 Error().Str("foo", "bar").Msg("hello from Error") 373 Fatal().Str("foo", "bar").Msg("hello from Fatal") 374 Panic().Str("foo", "bar").Msg("hello from Panic") 375 Printf("hello from %s", "Printf") 376 377 logger := Logger{ 378 Level: ParseLevel("trace"), 379 Caller: 1, 380 } 381 logger.Trace().Str("foo", "bar").Msg("hello from Trace") 382 logger.Debug().Str("foo", "bar").Msg("hello from Debug") 383 logger.Info().Str("foo", "bar").Msg("hello from Info") 384 logger.Warn().Str("foo", "bar").Msg("hello from Warn") 385 logger.Error().Str("foo", "bar").Msg("hello from Error") 386 logger.Fatal().Str("foo", "bar").Msg("hello from Fatal") 387 logger.Panic().Str("foo", "bar").Msg("hello from Panic") 388 logger.Printf("hello from %s", "Printf") 389 } 390 391 func TestLoggerTimeField(t *testing.T) { 392 logger := Logger{} 393 394 logger.TimeField = "_time" 395 logger.Printf("this is no level and _time field log") 396 } 397 398 func TestLoggerTimeFormat(t *testing.T) { 399 logger := Logger{} 400 401 logger.TimeFormat = TimeFormatUnix 402 logger.Info().Int64("timestamp_ms", timeNow().UnixNano()/1000000).Msg("this is unix time log entry") 403 404 logger.TimeFormat = TimeFormatUnixMs 405 logger.Info().Int64("timestamp_ms", timeNow().UnixNano()/1000000).Msg("this is unix_ms time log entry") 406 407 logger.TimeFormat = TimeFormatUnixWithMs 408 logger.Info().Int64("timestamp_ms", timeNow().UnixNano()/1000000).Msg("this is unix_with_ms time log entry") 409 410 logger.TimeFormat = time.RFC3339Nano 411 logger.Info().Int64("timestamp_ms", timeNow().UnixNano()/1000000).Msg("this is rfc3339 time log entry") 412 } 413 414 func TestLoggerTimeLocation(t *testing.T) { 415 logger := Logger{} 416 417 for _, format := range []string{"", time.RFC822} { 418 logger.TimeFormat = format 419 420 logger.TimeLocation = nil 421 logger.Info().Msgf("this is TimeFormat=%#v TimeLocation=nil log entry", logger.TimeFormat) 422 423 logger.TimeLocation = time.Local 424 logger.Info().Msgf("this is TimeFormat=%#v TimeLocation=time.Local log entry", logger.TimeFormat) 425 426 logger.TimeLocation = time.UTC 427 logger.Info().Msgf("this is TimeFormat=%#v TimeLocation=time.UTC log entry", logger.TimeFormat) 428 429 logger.TimeLocation, _ = time.LoadLocation("Asia/Singapore") 430 logger.Info().Msgf("this is TimeFormat=%#v TimeLocation=Asia/Singapore log entry", logger.TimeFormat) 431 432 logger.TimeLocation, _ = time.LoadLocation("America/New_York") 433 logger.Info().Msgf("this is TimeFormat=%#v TimeLocation=America/New_York log entry", logger.TimeFormat) 434 } 435 } 436 437 func TestLoggerTimeOffset(t *testing.T) { 438 logger := Logger{} 439 440 timeOffset = -7 * 3600 441 timeZone = "-07:00" 442 443 logger.Info().Msg("this is -7:00 timezone time log entry") 444 } 445 446 func TestLoggerContext(t *testing.T) { 447 ctx := NewContext(nil).Bool("ctx_bool", true).Str("ctx_str", "ctx str").Value() 448 449 logger := Logger{Level: InfoLevel} 450 logger.Trace().Context(ctx).Int("no0", 0).Msg("this is zero context log entry") 451 logger.Debug().Context(ctx).Int("no0", 0).Msg("this is zero context log entry") 452 logger.Info().Context(ctx).Int("no1", 1).Msg("this is first context log entry") 453 logger.Info().Context(ctx).Int("no2", 2).Msg("this is second context log entry") 454 } 455 456 func TestLoggerContext2(t *testing.T) { 457 notTest = false 458 459 DefaultLogger.Context = NewContext(nil).Str("ctx", "some_ctx").Int("n", 42).Value() 460 461 Trace().Str("foo", "bar").Msg("hello from Trace") 462 Debug().Str("foo", "bar").Msg("hello from Debug") 463 Info().Str("foo", "bar").Msg("hello from Info") 464 Warn().Str("foo", "bar").Msg("hello from Warn") 465 Error().Str("foo", "bar").Msg("hello from Error") 466 Fatal().Str("foo", "bar").Msg("hello from Fatal") 467 Panic().Str("foo", "bar").Msg("hello from Panic") 468 Printf("hello from %s", "Printf") 469 } 470 471 func TestLoggerContextDict(t *testing.T) { 472 ctx := NewContext(nil).Bool("ctx_bool", true).Str("ctx_str", "ctx str").Value() 473 474 logger := Logger{Level: InfoLevel, Writer: &ConsoleWriter{ColorOutput: true}} 475 logger.Trace().Dict("akey", ctx).Int("no0", 0).Msg("this is zero dict log entry") 476 logger.Debug().Dict("akey", ctx).Int("no0", 0).Msg("this is zero dict log entry") 477 logger.Info().Dict("akey", ctx).Int("no1", 1).Msg("this is first dict log entry") 478 logger.Info(). 479 Dict("a", NewContext(nil). 480 Bool("b", true). 481 Dict("c", NewContext(nil). 482 Bool("d", true). 483 Str("e", "a str"). 484 Value()). 485 Value()). 486 Msg("") 487 488 ctx = NewContext(nil).Value() 489 logger.Trace().Dict("akey", ctx).Int("no0", 0).Msg("this is zero dict log entry") 490 logger.Debug().Dict("akey", ctx).Int("no0", 0).Msg("this is zero dict log entry") 491 logger.Info().Dict("akey", ctx).Int("no1", 1).Msg("this is first dict log entry") 492 } 493 494 func TestLoggerFields(t *testing.T) { 495 ipv4Addr, ipv4Net, err := net.ParseCIDR("192.0.2.1/24") 496 if err != nil { 497 t.Fatalf("net.ParseCIDR error: %+v", err) 498 } 499 500 logger := Logger{ 501 Level: InfoLevel, 502 Caller: 1, 503 Writer: &ConsoleWriter{ColorOutput: true, EndWithMessage: true}, 504 } 505 506 logger.Info().Fields(Fields{ 507 "bool": true, 508 "bools": []bool{false}, 509 "bools_2": []bool{true, false}, 510 "1_hour": time.Hour, 511 "hour_minute_second": []time.Duration{time.Hour, time.Minute, time.Second}, 512 "error": errors.New("test error"), 513 "an_error": fmt.Errorf("an %w", errors.New("test error")), 514 "an_nil_error": nil, 515 "dict": NewContext(nil).Str("foo", "bar").Int("no", 1).Value(), 516 "float32": float32(1.111), 517 "float32_2": []float32{1.111}, 518 "float32_3": []float32{1.111, 2.222}, 519 "float64": float64(1.111), 520 "float64_2": []float64{1.111, 2.222}, 521 "int64": int64(1234567890), 522 "int32": int32(123), 523 "int16": int16(123), 524 "int8": int8(123), 525 "int": int(123), 526 "uint64": uint64(1234567890), 527 "uint32": uint32(123), 528 "uint16": uint16(123), 529 "uint8": uint8(123), 530 "uint": uint(123), 531 "raw_json": []byte("{\"a\":1,\"b\":2}"), 532 "hex": []byte("\"<>?'"), 533 "bytes1": []byte("bytes1"), 534 "bytes2": []byte("\"<>?'"), 535 "foobar": "\"\\\t\r\n\f\b\x00<>?'", 536 "strings": []string{"a", "b", "\"<>?'"}, 537 "stringer_1": nil, 538 "stringer_2": ipv4Addr, 539 "gostringer_1": nil, 540 "gostringer_2": binary.BigEndian, 541 "now_1": timeNow(), 542 "ip_str": ipv4Addr, 543 "big_edian": binary.BigEndian, 544 "ip6": net.ParseIP("2001:4860:4860::8888"), 545 "ip4": ipv4Addr, 546 "ip_prefix": *ipv4Net, 547 "mac": net.HardwareAddr{0x00, 0x00, 0x5e, 0x00, 0x53, 0x01}, 548 "errors": []error{errors.New("error1"), nil, errors.New("error3")}, 549 "console_writer": ConsoleWriter{ColorOutput: true}, 550 "time.Time": timeNow(), 551 "buffer": bytes.NewBuffer([]byte("a_bytes_buffer")), 552 }).Msg("this is a fields test") 553 } 554 555 func TestWriterFunc(t *testing.T) { 556 logger := Logger{ 557 Writer: WriterFunc(func(e *Entry) (int, error) { 558 if e.Level >= ErrorLevel { 559 return os.Stderr.Write(e.Value()) 560 } else { 561 return os.Stdout.Write(e.Value()) 562 } 563 }), 564 } 565 566 logger.Info().Msg("a stdout entry") 567 logger.Error().Msg("a stderr entry") 568 } 569 570 type errno uint 571 572 func (e errno) Error() string { 573 return fmt.Sprintf("errno: %d", e) 574 } 575 576 func (e errno) Format(s fmt.State, verb rune) { 577 switch verb { 578 case 'v': 579 if e == 0 { 580 fmt.Fprintf(s, "%d", e) 581 return 582 } 583 fmt.Fprintf(s, "stack layer: %v\t", e-1) 584 case 's': 585 fmt.Fprintf(s, "errno: %d", e) 586 } 587 } 588 589 func TestLoggerErrorStack(t *testing.T) { 590 logger := Logger{Level: TraceLevel, Writer: &ConsoleWriter{ColorOutput: true}} 591 logger.Info().Err(errno(0)).Msg("log errno(0) here") 592 logger.Info().Err(errno(1)).Msg("log errno(1) here") 593 logger.Info().Err(errno(2)).Msg("log errno(2) here") 594 logger.Info().Err(errno(3)).Msg("log errno(3) here") 595 } 596 597 func TestFixMissingErrEntry(t *testing.T) { 598 var b bytes.Buffer 599 logger := Logger{Level: TraceLevel, Writer: &IOWriter{Writer: &b}} 600 logger.Err(errors.New("test error")).Msg("log error here") 601 if !strings.Contains(b.String(), `"error":"test error"`) { 602 t.Fatal("logger.Err need an error entry if err != nil") 603 } 604 b.Reset() 605 logger.Err(nil).Msg("log info here") 606 if !strings.Contains(b.String(), `"level":"info"`) { 607 t.Fatal("logger.Err need info level if err == nil") 608 } 609 } 610 611 func BenchmarkLogger(b *testing.B) { 612 logger := Logger{ 613 TimeFormat: TimeFormatUnix, 614 Level: DebugLevel, 615 Writer: IOWriter{io.Discard}, 616 } 617 618 b.ReportAllocs() 619 b.ResetTimer() 620 for i := 0; i < b.N; i++ { 621 logger.Info().Str("foo", "bar").Msgf("hello %s", "world") 622 } 623 }