github.com/ethersphere/bee/v2@v2.2.0/pkg/log/logger_test.go (about) 1 // Copyright 2022 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package log 6 7 import ( 8 "bytes" 9 "errors" 10 "fmt" 11 "path/filepath" 12 "runtime" 13 "sync" 14 "testing" 15 ) 16 17 // hook is a helper type for recording 18 // if Hook.Fire method has been triggered. 19 type hook bool 20 21 // reset resets this hook as if it were never fired. 22 func (h *hook) reset() { 23 *h = false 24 } 25 26 // Fire implements Hook.Fire method. 27 func (h *hook) Fire(Level) error { 28 *h = true 29 return nil 30 } 31 32 // applyError is a higher order function that returns the given fn with an applied err. 33 func applyError(fn func(error, string, ...interface{}), err error) func(string, ...interface{}) { 34 return func(msg string, kvs ...interface{}) { 35 fn(err, msg, kvs...) 36 } 37 } 38 39 // newLogger is a convenient constructor for creating 40 // new instances of Logger without pinning the defaults. 41 func newLogger(modifications ...Option) (Builder, *bytes.Buffer) { 42 const name = "root" 43 44 opts := *defaults.options 45 for _, modify := range modifications { 46 modify(&opts) 47 } 48 49 b := new(bytes.Buffer) 50 l := &logger{ 51 formatter: newFormatter(opts.fmtOptions), 52 verbosity: opts.verbosity, 53 sink: b, 54 levelHooks: opts.levelHooks, 55 } 56 l.builder = &builder{ 57 l: l, 58 names: []string{name}, 59 namesStr: name, 60 } 61 return l, b 62 } 63 64 func TestLoggerOptionsLevelHooks(t *testing.T) { 65 l := loggers 66 t.Cleanup(func() { 67 loggers = l 68 }) 69 loggers = new(sync.Map) 70 71 t.Run("verbosity=none", func(t *testing.T) { 72 have := hook(false) 73 logger, _ := newLogger(WithLevelHooks(VerbosityNone, &have)) 74 75 tests := []struct { 76 fn func(string, ...interface{}) 77 want bool 78 }{{ 79 fn: logger.Build().Debug, 80 want: false, 81 }, { 82 fn: logger.Build().Info, 83 want: false, 84 }, { 85 fn: logger.Build().Warning, 86 want: false, 87 }, { 88 fn: applyError(logger.Build().Error, nil), 89 want: false, 90 }} 91 92 for _, tc := range tests { 93 tc.fn("") 94 if tc.want != bool(have) { 95 t.Errorf("hook: want fired %t; have %t", tc.want, have) 96 } 97 have.reset() 98 } 99 }) 100 t.Run("verbosity=debug", func(t *testing.T) { 101 have := hook(false) 102 logger, _ := newLogger(WithLevelHooks(VerbosityDebug, &have)) 103 104 tests := []struct { 105 fn func(string, ...interface{}) 106 want bool 107 }{{ 108 fn: logger.Build().Debug, 109 want: true, 110 }, { 111 fn: logger.Build().Info, 112 want: false, 113 }, { 114 fn: logger.Build().Warning, 115 want: false, 116 }, { 117 fn: applyError(logger.Build().Error, nil), 118 want: false, 119 }} 120 121 for _, tc := range tests { 122 tc.fn("") 123 if tc.want != bool(have) { 124 t.Errorf("hook: want fired %t; have %t", tc.want, have) 125 } 126 have.reset() 127 } 128 }) 129 t.Run("verbosity=info", func(t *testing.T) { 130 have := hook(false) 131 logger, _ := newLogger(WithLevelHooks(VerbosityInfo, &have)) 132 133 tests := []struct { 134 fn func(string, ...interface{}) 135 want bool 136 }{{ 137 fn: logger.Build().Debug, 138 want: false, 139 }, { 140 fn: logger.Build().Info, 141 want: true, 142 }, { 143 fn: logger.Build().Warning, 144 want: false, 145 }, { 146 fn: applyError(logger.Build().Error, nil), 147 want: false, 148 }} 149 150 for _, tc := range tests { 151 tc.fn("") 152 if tc.want != bool(have) { 153 t.Errorf("hook: want fired %t; have %t", tc.want, have) 154 } 155 have.reset() 156 } 157 }) 158 t.Run("verbosity=warning", func(t *testing.T) { 159 have := hook(false) 160 logger, _ := newLogger(WithLevelHooks(VerbosityWarning, &have)) 161 162 tests := []struct { 163 fn func(string, ...interface{}) 164 want bool 165 }{{ 166 fn: logger.Build().Debug, 167 want: false, 168 }, { 169 fn: logger.Build().Info, 170 want: false, 171 }, { 172 fn: logger.Build().Warning, 173 want: true, 174 }, { 175 fn: applyError(logger.Build().Error, nil), 176 want: false, 177 }} 178 179 for _, tc := range tests { 180 tc.fn("") 181 if tc.want != bool(have) { 182 t.Errorf("hook: want fired %t; have %t", tc.want, have) 183 } 184 have.reset() 185 } 186 }) 187 t.Run("verbosity=error", func(t *testing.T) { 188 have := hook(false) 189 logger, _ := newLogger(WithLevelHooks(VerbosityError, &have)) 190 191 tests := []struct { 192 fn func(string, ...interface{}) 193 want bool 194 }{{ 195 fn: logger.Build().Debug, 196 want: false, 197 }, { 198 fn: logger.Build().Info, 199 want: false, 200 }, { 201 fn: logger.Build().Warning, 202 want: false, 203 }, { 204 fn: applyError(logger.Build().Error, nil), 205 want: true, 206 }} 207 208 for _, tc := range tests { 209 tc.fn("") 210 if tc.want != bool(have) { 211 t.Errorf("hook: want fired %t; have %t", tc.want, have) 212 } 213 have.reset() 214 } 215 }) 216 t.Run("verbosity=all", func(t *testing.T) { 217 have := hook(false) 218 logger, _ := newLogger(WithLevelHooks(VerbosityAll, &have)) 219 220 tests := []struct { 221 fn func(string, ...interface{}) 222 want bool 223 }{{ 224 fn: logger.Build().Debug, 225 want: true, 226 }, { 227 fn: logger.Build().Info, 228 want: true, 229 }, { 230 fn: logger.Build().Warning, 231 want: true, 232 }, { 233 fn: applyError(logger.Build().Error, nil), 234 want: true, 235 }} 236 237 for _, tc := range tests { 238 tc.fn("") 239 if tc.want != bool(have) { 240 t.Errorf("hook: want fired %t; have %t", tc.want, have) 241 } 242 have.reset() 243 } 244 }) 245 } 246 247 func TestLoggerOptionsTimestampFormat(t *testing.T) { 248 l := loggers 249 t.Cleanup(func() { 250 loggers = l 251 }) 252 loggers = new(sync.Map) 253 254 logger, bb := newLogger( 255 WithTimestamp(), 256 WithTimestampLayout("TIMESTAMP"), 257 WithCallerDepth(1), 258 ) 259 logger.Build().Info("msg") 260 have := string(bytes.TrimRight(bb.Bytes(), "\n")) 261 want := `"time"="TIMESTAMP" "level"="info" "logger"="root" "msg"="msg"` 262 if have != want { 263 t.Errorf("\nwant %q\nhave %q", want, have) 264 } 265 } 266 267 func TestLogger(t *testing.T) { 268 l := loggers 269 t.Cleanup(func() { 270 loggers = l 271 }) 272 loggers = new(sync.Map) 273 274 logger, bb := newLogger() 275 276 testCases := []struct { 277 name string 278 logFn func(string, ...interface{}) 279 args []interface{} 280 want string 281 }{{ 282 name: "just msg", 283 logFn: logger.Build().Debug, 284 args: makeKV(), 285 want: `"level"="debug" "logger"="root" "msg"="msg"`, 286 }, { 287 name: "primitives", 288 logFn: logger.Build().Debug, 289 args: makeKV("int", 1, "str", "ABC", "bool", true), 290 want: `"level"="debug" "logger"="root" "msg"="msg" "int"=1 "str"="ABC" "bool"=true`, 291 }, { 292 name: "just msg", 293 logFn: logger.Build().Info, 294 args: makeKV(), 295 want: `"level"="info" "logger"="root" "msg"="msg"`, 296 }, { 297 name: "primitives", 298 logFn: logger.Build().Info, 299 args: makeKV("int", 1, "str", "ABC", "bool", true), 300 want: `"level"="info" "logger"="root" "msg"="msg" "int"=1 "str"="ABC" "bool"=true`, 301 }, { 302 name: "just msg", 303 logFn: logger.Build().Warning, 304 args: makeKV(), 305 want: `"level"="warning" "logger"="root" "msg"="msg"`, 306 }, { 307 name: "primitives", 308 logFn: logger.Build().Warning, 309 args: makeKV("int", 1, "str", "ABC", "bool", true), 310 want: `"level"="warning" "logger"="root" "msg"="msg" "int"=1 "str"="ABC" "bool"=true`, 311 }, { 312 name: "just msg", 313 logFn: applyError(logger.Build().Error, errors.New("err")), 314 args: makeKV(), 315 want: `"level"="error" "logger"="root" "msg"="msg" "error"="err"`, 316 }, { 317 name: "primitives", 318 logFn: applyError(logger.Build().Error, errors.New("err")), 319 args: makeKV("int", 1, "str", "ABC", "bool", true), 320 want: `"level"="error" "logger"="root" "msg"="msg" "error"="err" "int"=1 "str"="ABC" "bool"=true`, 321 }} 322 323 for _, tc := range testCases { 324 t.Run(tc.name, func(t *testing.T) { 325 bb.Reset() 326 tc.logFn("msg", tc.args...) 327 have := string(bytes.TrimRight(bb.Bytes(), "\n")) 328 if have != tc.want { 329 t.Errorf("\nwant %q\nhave %q", tc.want, have) 330 } 331 }) 332 } 333 } 334 335 func TestLoggerWithCaller(t *testing.T) { 336 l := loggers 337 t.Cleanup(func() { 338 loggers = l 339 }) 340 loggers = new(sync.Map) 341 342 t.Run("caller=CategoryAll", func(t *testing.T) { 343 logger, bb := newLogger(WithCaller(CategoryAll)) 344 345 logger.Build().Debug("msg") 346 _, file, line, _ := runtime.Caller(0) 347 want := fmt.Sprintf(`"level"="debug" "logger"="root" "caller"={"file":%q,"line":%d} "msg"="msg"`, filepath.Base(file), line-1) 348 have := string(bytes.TrimRight(bb.Bytes(), "\n")) 349 if have != want { 350 t.Errorf("\nwant %q\nhave %q", want, have) 351 } 352 353 bb.Reset() 354 355 logger.Build().Info("msg") 356 _, file, line, _ = runtime.Caller(0) 357 want = fmt.Sprintf(`"level"="info" "logger"="root" "caller"={"file":%q,"line":%d} "msg"="msg"`, filepath.Base(file), line-1) 358 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 359 if have != want { 360 t.Errorf("\nwant %q\nhave %q", want, have) 361 } 362 363 bb.Reset() 364 365 logger.Build().Warning("msg") 366 _, file, line, _ = runtime.Caller(0) 367 want = fmt.Sprintf(`"level"="warning" "logger"="root" "caller"={"file":%q,"line":%d} "msg"="msg"`, filepath.Base(file), line-1) 368 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 369 if have != want { 370 t.Errorf("\nwant %q\nhave %q", want, have) 371 } 372 373 bb.Reset() 374 375 logger.Build().Error(errors.New("err"), "msg") 376 _, file, line, _ = runtime.Caller(0) 377 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 378 want = fmt.Sprintf(`"level"="error" "logger"="root" "caller"={"file":%q,"line":%d} "msg"="msg" "error"="err"`, filepath.Base(file), line-1) 379 if have != want { 380 t.Errorf("\nwant %q\nhave %q", want, have) 381 } 382 }) 383 t.Run("caller=CategoryAll, logCallerFunc=true", func(t *testing.T) { 384 const thisFunc = "github.com/ethersphere/bee/v2/pkg/log.TestLoggerWithCaller.func3" 385 386 logger, bb := newLogger(WithCaller(CategoryAll), WithCallerFunc()) 387 388 logger.Build().Debug("msg") 389 _, file, line, _ := runtime.Caller(0) 390 want := fmt.Sprintf(`"level"="debug" "logger"="root" "caller"={"file":%q,"line":%d,"function":%q} "msg"="msg"`, filepath.Base(file), line-1, thisFunc) 391 have := string(bytes.TrimRight(bb.Bytes(), "\n")) 392 if have != want { 393 t.Errorf("\nwant %q\nhave %q", want, have) 394 } 395 396 bb.Reset() 397 398 logger.Build().Info("msg") 399 _, file, line, _ = runtime.Caller(0) 400 want = fmt.Sprintf(`"level"="info" "logger"="root" "caller"={"file":%q,"line":%d,"function":%q} "msg"="msg"`, filepath.Base(file), line-1, thisFunc) 401 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 402 if have != want { 403 t.Errorf("\nwant %q\nhave %q", want, have) 404 } 405 406 bb.Reset() 407 408 logger.Build().Warning("msg") 409 _, file, line, _ = runtime.Caller(0) 410 want = fmt.Sprintf(`"level"="warning" "logger"="root" "caller"={"file":%q,"line":%d,"function":%q} "msg"="msg"`, filepath.Base(file), line-1, thisFunc) 411 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 412 if have != want { 413 t.Errorf("\nwant %q\nhave %q", want, have) 414 } 415 416 bb.Reset() 417 418 logger.Build().Error(errors.New("err"), "msg") 419 _, file, line, _ = runtime.Caller(0) 420 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 421 want = fmt.Sprintf(`"level"="error" "logger"="root" "caller"={"file":%q,"line":%d,"function":%q} "msg"="msg" "error"="err"`, filepath.Base(file), line-1, thisFunc) 422 if have != want { 423 t.Errorf("\nwant %q\nhave %q", want, have) 424 } 425 }) 426 t.Run("caller=CategoryDebug", func(t *testing.T) { 427 logger, bb := newLogger(WithCaller(CategoryDebug)) 428 429 logger.Build().Debug("msg") 430 _, file, line, _ := runtime.Caller(0) 431 want := fmt.Sprintf(`"level"="debug" "logger"="root" "caller"={"file":%q,"line":%d} "msg"="msg"`, filepath.Base(file), line-1) 432 have := string(bytes.TrimRight(bb.Bytes(), "\n")) 433 if have != want { 434 t.Errorf("\nwant %q\nhave %q", want, have) 435 } 436 437 bb.Reset() 438 439 logger.Build().Info("msg") 440 want = `"level"="info" "logger"="root" "msg"="msg"` 441 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 442 if have != want { 443 t.Errorf("\nwant %q\nhave %q", want, have) 444 } 445 446 bb.Reset() 447 448 logger.Build().Warning("msg") 449 want = `"level"="warning" "logger"="root" "msg"="msg"` 450 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 451 if have != want { 452 t.Errorf("\nwant %q\nhave %q", want, have) 453 } 454 455 bb.Reset() 456 457 logger.Build().Error(errors.New("err"), "msg") 458 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 459 want = `"level"="error" "logger"="root" "msg"="msg" "error"="err"` 460 if have != want { 461 t.Errorf("\nwant %q\nhave %q", want, have) 462 } 463 }) 464 t.Run("caller=CategoryInfo", func(t *testing.T) { 465 logger, bb := newLogger(WithCaller(CategoryInfo)) 466 467 logger.Build().Debug("msg") 468 want := `"level"="debug" "logger"="root" "msg"="msg"` 469 have := string(bytes.TrimRight(bb.Bytes(), "\n")) 470 if have != want { 471 t.Errorf("\nwant %q\nhave %q", want, have) 472 } 473 474 bb.Reset() 475 476 logger.Build().Info("msg") 477 _, file, line, _ := runtime.Caller(0) 478 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 479 want = fmt.Sprintf(`"level"="info" "logger"="root" "caller"={"file":%q,"line":%d} "msg"="msg"`, filepath.Base(file), line-1) 480 if have != want { 481 t.Errorf("\nwant %q\nhave %q", want, have) 482 } 483 484 bb.Reset() 485 486 logger.Build().Warning("msg") 487 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 488 want = `"level"="warning" "logger"="root" "msg"="msg"` 489 if have != want { 490 t.Errorf("\nwant %q\nhave %q", want, have) 491 } 492 493 bb.Reset() 494 495 logger.Build().Error(errors.New("err"), "msg") 496 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 497 want = `"level"="error" "logger"="root" "msg"="msg" "error"="err"` 498 if have != want { 499 t.Errorf("\nwant %q\nhave %q", want, have) 500 } 501 }) 502 t.Run("caller=CategoryWarning", func(t *testing.T) { 503 logger, bb := newLogger(WithCaller(CategoryWarning)) 504 505 logger.Build().Debug("msg") 506 want := `"level"="debug" "logger"="root" "msg"="msg"` 507 have := string(bytes.TrimRight(bb.Bytes(), "\n")) 508 if have != want { 509 t.Errorf("\nwant %q\nhave %q", want, have) 510 } 511 512 bb.Reset() 513 514 logger.Build().Info("msg") 515 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 516 want = `"level"="info" "logger"="root" "msg"="msg"` 517 if have != want { 518 t.Errorf("\nwant %q\nhave %q", want, have) 519 } 520 521 bb.Reset() 522 523 logger.Build().Warning("msg") 524 _, file, line, _ := runtime.Caller(0) 525 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 526 want = fmt.Sprintf(`"level"="warning" "logger"="root" "caller"={"file":%q,"line":%d} "msg"="msg"`, filepath.Base(file), line-1) 527 if have != want { 528 t.Errorf("\nwant %q\nhave %q", want, have) 529 } 530 531 bb.Reset() 532 533 logger.Build().Error(errors.New("err"), "msg") 534 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 535 want = `"level"="error" "logger"="root" "msg"="msg" "error"="err"` 536 if have != want { 537 t.Errorf("\nwant %q\nhave %q", want, have) 538 } 539 }) 540 t.Run("caller=CategoryError", func(t *testing.T) { 541 logger, bb := newLogger(WithCaller(CategoryError)) 542 543 logger.Build().Debug("msg") 544 want := `"level"="debug" "logger"="root" "msg"="msg"` 545 have := string(bytes.TrimRight(bb.Bytes(), "\n")) 546 if have != want { 547 t.Errorf("\nwant %q\nhave %q", want, have) 548 } 549 550 bb.Reset() 551 552 logger.Build().Info("msg") 553 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 554 want = `"level"="info" "logger"="root" "msg"="msg"` 555 if have != want { 556 t.Errorf("\nwant %q\nhave %q", want, have) 557 } 558 559 bb.Reset() 560 561 logger.Build().Warning("msg") 562 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 563 want = `"level"="warning" "logger"="root" "msg"="msg"` 564 if have != want { 565 t.Errorf("\nwant %q\nhave %q", want, have) 566 } 567 568 bb.Reset() 569 570 logger.Build().Error(errors.New("err"), "msg") 571 _, file, line, _ := runtime.Caller(0) 572 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 573 want = fmt.Sprintf(`"level"="error" "logger"="root" "caller"={"file":%q,"line":%d} "msg"="msg" "error"="err"`, filepath.Base(file), line-1) 574 if have != want { 575 t.Errorf("\nwant %q\nhave %q", want, have) 576 } 577 }) 578 t.Run("caller=CategoryNone", func(t *testing.T) { 579 logger, bb := newLogger(WithCaller(CategoryNone)) 580 581 logger.Build().Debug("msg") 582 want := `"level"="debug" "logger"="root" "msg"="msg"` 583 have := string(bytes.TrimRight(bb.Bytes(), "\n")) 584 if have != want { 585 t.Errorf("\nwant %q\nhave %q", want, have) 586 } 587 588 bb.Reset() 589 590 logger.Build().Info("msg") 591 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 592 want = `"level"="info" "logger"="root" "msg"="msg"` 593 if have != want { 594 t.Errorf("\nwant %q\nhave %q", want, have) 595 } 596 597 bb.Reset() 598 599 logger.Build().Warning("msg") 600 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 601 want = `"level"="warning" "logger"="root" "msg"="msg"` 602 if have != want { 603 t.Errorf("\nwant %q\nhave %q", want, have) 604 } 605 606 bb.Reset() 607 608 logger.Build().Error(errors.New("err"), "msg") 609 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 610 want = `"level"="error" "logger"="root" "msg"="msg" "error"="err"` 611 if have != want { 612 t.Errorf("\nwant %q\nhave %q", want, have) 613 } 614 }) 615 } 616 617 func TestLoggerWithName(t *testing.T) { 618 l := loggers 619 t.Cleanup(func() { 620 loggers = l 621 }) 622 loggers = new(sync.Map) 623 624 logger, bb := newLogger() 625 logger.Build() 626 627 testCases := []struct { 628 name string 629 logFn func(string, ...interface{}) 630 args []interface{} 631 want string 632 }{{ 633 name: "one", 634 logFn: logger.WithName("pfx1").Build().Debug, 635 args: makeKV("k", "v"), 636 want: `"level"="debug" "logger"="root/pfx1" "msg"="msg" "k"="v"`, 637 }, { 638 name: "two", 639 logFn: logger.WithName("pfx1").WithName("pfx2").Build().Debug, 640 args: makeKV("k", "v"), 641 want: `"level"="debug" "logger"="root/pfx1/pfx2" "msg"="msg" "k"="v"`, 642 }, { 643 name: "one", 644 logFn: logger.WithName("pfx1").Build().Info, 645 args: makeKV("k", "v"), 646 want: `"level"="info" "logger"="root/pfx1" "msg"="msg" "k"="v"`, 647 }, { 648 name: "two", 649 logFn: logger.WithName("pfx1").WithName("pfx2").Build().Info, 650 args: makeKV("k", "v"), 651 want: `"level"="info" "logger"="root/pfx1/pfx2" "msg"="msg" "k"="v"`, 652 }, { 653 name: "one", 654 logFn: logger.WithName("pfx1").Build().Warning, 655 args: makeKV("k", "v"), 656 want: `"level"="warning" "logger"="root/pfx1" "msg"="msg" "k"="v"`, 657 }, { 658 name: "two", 659 logFn: logger.WithName("pfx1").WithName("pfx2").Build().Warning, 660 args: makeKV("k", "v"), 661 want: `"level"="warning" "logger"="root/pfx1/pfx2" "msg"="msg" "k"="v"`, 662 }, { 663 name: "one", 664 logFn: applyError(logger.WithName("pfx1").Build().Error, errors.New("err")), 665 args: makeKV("k", "v"), 666 want: `"level"="error" "logger"="root/pfx1" "msg"="msg" "error"="err" "k"="v"`, 667 }, { 668 name: "two", 669 logFn: applyError(logger.WithName("pfx1").WithName("pfx2").Build().Error, errors.New("err")), 670 args: makeKV("k", "v"), 671 want: `"level"="error" "logger"="root/pfx1/pfx2" "msg"="msg" "error"="err" "k"="v"`, 672 }} 673 674 for _, tc := range testCases { 675 t.Run(tc.name, func(t *testing.T) { 676 bb.Reset() 677 tc.logFn("msg", tc.args...) 678 have := string(bytes.TrimRight(bb.Bytes(), "\n")) 679 if have != tc.want { 680 t.Errorf("\nwant %q\nhave %q", tc.want, have) 681 } 682 }) 683 } 684 } 685 686 func TestLoggerWithValues(t *testing.T) { 687 l := loggers 688 t.Cleanup(func() { 689 loggers = l 690 }) 691 loggers = new(sync.Map) 692 693 logger, bb := newLogger() 694 695 testCases := []struct { 696 name string 697 logFn func(string, ...interface{}) 698 args []interface{} 699 want string 700 }{{ 701 name: "zero", 702 logFn: logger.Build().Debug, 703 args: makeKV("k", "v"), 704 want: `"level"="debug" "logger"="root" "msg"="msg" "k"="v"`, 705 }, { 706 name: "one", 707 logFn: logger.WithValues("one", 1).Build().Debug, 708 args: makeKV("k", "v"), 709 want: `"level"="debug" "logger"="root" "msg"="msg" "one"=1 "k"="v"`, 710 }, { 711 name: "two", 712 logFn: logger.WithValues("one", 1, "two", 2).Build().Debug, 713 args: makeKV("k", "v"), 714 want: `"level"="debug" "logger"="root" "msg"="msg" "one"=1 "two"=2 "k"="v"`, 715 }, { 716 name: "dangling", 717 logFn: logger.WithValues("dangling").Build().Debug, 718 args: makeKV("k", "v"), 719 want: `"level"="debug" "logger"="root" "msg"="msg" "dangling"="<no-value>" "k"="v"`, 720 }, { 721 name: "zero", 722 logFn: logger.Build().Info, 723 args: makeKV("k", "v"), 724 want: `"level"="info" "logger"="root" "msg"="msg" "k"="v"`, 725 }, { 726 name: "one", 727 logFn: logger.WithValues("one", 1).Build().Info, 728 args: makeKV("k", "v"), 729 want: `"level"="info" "logger"="root" "msg"="msg" "one"=1 "k"="v"`, 730 }, { 731 name: "two", 732 logFn: logger.WithValues("one", 1, "two", 2).Build().Info, 733 args: makeKV("k", "v"), 734 want: `"level"="info" "logger"="root" "msg"="msg" "one"=1 "two"=2 "k"="v"`, 735 }, { 736 name: "dangling", 737 logFn: logger.WithValues("dangling").Build().Info, 738 args: makeKV("k", "v"), 739 want: `"level"="info" "logger"="root" "msg"="msg" "dangling"="<no-value>" "k"="v"`, 740 }, { 741 name: "zero", 742 logFn: logger.Build().Warning, 743 args: makeKV("k", "v"), 744 want: `"level"="warning" "logger"="root" "msg"="msg" "k"="v"`, 745 }, { 746 name: "one", 747 logFn: logger.WithValues("one", 1).Build().Warning, 748 args: makeKV("k", "v"), 749 want: `"level"="warning" "logger"="root" "msg"="msg" "one"=1 "k"="v"`, 750 }, { 751 name: "two", 752 logFn: logger.WithValues("one", 1, "two", 2).Build().Warning, 753 args: makeKV("k", "v"), 754 want: `"level"="warning" "logger"="root" "msg"="msg" "one"=1 "two"=2 "k"="v"`, 755 }, { 756 name: "dangling", 757 logFn: logger.WithValues("dangling").Build().Warning, 758 args: makeKV("k", "v"), 759 want: `"level"="warning" "logger"="root" "msg"="msg" "dangling"="<no-value>" "k"="v"`, 760 }, { 761 name: "zero", 762 logFn: applyError(logger.Build().Error, errors.New("err")), 763 args: makeKV("k", "v"), 764 want: `"level"="error" "logger"="root" "msg"="msg" "error"="err" "k"="v"`, 765 }, { 766 name: "one", 767 logFn: applyError(logger.WithValues("one", 1).Build().Error, errors.New("err")), 768 args: makeKV("k", "v"), 769 want: `"level"="error" "logger"="root" "msg"="msg" "error"="err" "one"=1 "k"="v"`, 770 }, { 771 name: "two", 772 logFn: applyError(logger.WithValues("one", 1, "two", 2).Build().Error, errors.New("err")), 773 args: makeKV("k", "v"), 774 want: `"level"="error" "logger"="root" "msg"="msg" "error"="err" "one"=1 "two"=2 "k"="v"`, 775 }, { 776 name: "dangling", 777 logFn: applyError(logger.WithValues("dangling").Build().Error, errors.New("err")), 778 args: makeKV("k", "v"), 779 want: `"level"="error" "logger"="root" "msg"="msg" "error"="err" "dangling"="<no-value>" "k"="v"`, 780 }} 781 782 for _, tc := range testCases { 783 t.Run(tc.name, func(t *testing.T) { 784 bb.Reset() 785 tc.logFn("msg", tc.args...) 786 have := string(bytes.TrimRight(bb.Bytes(), "\n")) 787 if have != tc.want { 788 t.Errorf("\nwant %q\nhave %q", tc.want, have) 789 } 790 }) 791 } 792 } 793 794 func TestLoggerWithCallDepth(t *testing.T) { 795 l := loggers 796 t.Cleanup(func() { 797 loggers = l 798 }) 799 loggers = new(sync.Map) 800 801 t.Run("verbosity=debug, callerDepth=1", func(t *testing.T) { 802 logger, bb := newLogger(WithCallerDepth(1), WithCaller(CategoryAll)) 803 logger.Build().Debug("msg") 804 _, file, line, _ := runtime.Caller(1) 805 have := string(bytes.TrimRight(bb.Bytes(), "\n")) 806 want := fmt.Sprintf(`"level"="debug" "logger"="root" "caller"={"file":%q,"line":%d} "msg"="msg"`, filepath.Base(file), line) 807 if have != want { 808 t.Errorf("\nwant %q\nhave %q", want, have) 809 } 810 }) 811 t.Run("verbosity=info, callerDepth=1", func(t *testing.T) { 812 logger, bb := newLogger(WithCallerDepth(1), WithCaller(CategoryAll)) 813 logger.Build().Info("msg") 814 _, file, line, _ := runtime.Caller(1) 815 have := string(bytes.TrimRight(bb.Bytes(), "\n")) 816 want := fmt.Sprintf(`"level"="info" "logger"="root" "caller"={"file":%q,"line":%d} "msg"="msg"`, filepath.Base(file), line) 817 if have != want { 818 t.Errorf("\nwant %q\nhave %q", want, have) 819 } 820 }) 821 t.Run("verbosity=warning, callerDepth=1", func(t *testing.T) { 822 logger, bb := newLogger(WithCallerDepth(1), WithCaller(CategoryAll)) 823 logger.Build().Warning("msg") 824 _, file, line, _ := runtime.Caller(1) 825 have := string(bytes.TrimRight(bb.Bytes(), "\n")) 826 want := fmt.Sprintf(`"level"="warning" "logger"="root" "caller"={"file":%q,"line":%d} "msg"="msg"`, filepath.Base(file), line) 827 if have != want { 828 t.Errorf("\nwant %q\nhave %q", want, have) 829 } 830 }) 831 t.Run("verbosity=error, callerDepth=1", func(t *testing.T) { 832 logger, bb := newLogger(WithCallerDepth(1), WithCaller(CategoryAll)) 833 logger.Build().Error(errors.New("err"), "msg") 834 _, file, line, _ := runtime.Caller(1) 835 have := string(bytes.TrimRight(bb.Bytes(), "\n")) 836 want := fmt.Sprintf(`"level"="error" "logger"="root" "caller"={"file":%q,"line":%d} "msg"="msg" "error"="err"`, filepath.Base(file), line) 837 if have != want { 838 t.Errorf("\nwant %q\nhave %q", want, have) 839 } 840 }) 841 } 842 843 func TestLoggerVerbosity(t *testing.T) { 844 l := loggers 845 t.Cleanup(func() { 846 loggers = l 847 }) 848 loggers = new(sync.Map) 849 850 t.Run("verbosity=all", func(t *testing.T) { 851 logger, bb := newLogger(WithVerbosity(VerbosityAll)) 852 logger.Build() 853 854 logger.V(1).Build().Debug("msg") 855 want := `"level"="debug" "logger"="root" "v"=1 "msg"="msg"` 856 have := string(bytes.TrimRight(bb.Bytes(), "\n")) 857 if have != want { 858 t.Errorf("\nwant %q\nhave %q", want, have) 859 } 860 861 bb.Reset() 862 863 logger.Build().Debug("msg") 864 want = `"level"="debug" "logger"="root" "msg"="msg"` 865 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 866 if have != want { 867 t.Errorf("\nwant %q\nhave %q", want, have) 868 } 869 870 bb.Reset() 871 872 logger.Build().Info("msg") 873 want = `"level"="info" "logger"="root" "msg"="msg"` 874 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 875 if have != want { 876 t.Errorf("\nwant %q\nhave %q", want, have) 877 } 878 879 bb.Reset() 880 881 logger.Build().Warning("msg") 882 want = `"level"="warning" "logger"="root" "msg"="msg"` 883 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 884 if have != want { 885 t.Errorf("\nwant %q\nhave %q", want, have) 886 } 887 888 bb.Reset() 889 890 logger.Build().Error(nil, "msg") 891 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 892 want = `"level"="error" "logger"="root" "msg"="msg"` 893 if have != want { 894 t.Errorf("\nwant %q\nhave %q", want, have) 895 } 896 }) 897 t.Run("verbosity=debug", func(t *testing.T) { 898 logger, bb := newLogger(WithVerbosity(VerbosityDebug)) 899 logger.Build() 900 901 logger.V(1).Build().Debug("msg") 902 want := "" 903 have := string(bytes.TrimRight(bb.Bytes(), "\n")) 904 if have != want { 905 t.Errorf("\nwant %q\nhave %q", want, have) 906 } 907 908 bb.Reset() 909 910 logger.Build().Debug("msg") 911 want = `"level"="debug" "logger"="root" "msg"="msg"` 912 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 913 if have != want { 914 t.Errorf("\nwant %q\nhave %q", want, have) 915 } 916 917 bb.Reset() 918 919 logger.Build().Info("msg") 920 want = `"level"="info" "logger"="root" "msg"="msg"` 921 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 922 if have != want { 923 t.Errorf("\nwant %q\nhave %q", want, have) 924 } 925 926 bb.Reset() 927 928 logger.Build().Warning("msg") 929 want = `"level"="warning" "logger"="root" "msg"="msg"` 930 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 931 if have != want { 932 t.Errorf("\nwant %q\nhave %q", want, have) 933 } 934 935 bb.Reset() 936 937 logger.Build().Error(nil, "msg") 938 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 939 want = `"level"="error" "logger"="root" "msg"="msg"` 940 if have != want { 941 t.Errorf("\nwant %q\nhave %q", want, have) 942 } 943 }) 944 t.Run("verbosity=info", func(t *testing.T) { 945 logger, bb := newLogger(WithVerbosity(VerbosityInfo)) 946 logger.Build() 947 948 logger.V(1).Build().Debug("msg") 949 logger.Build().Debug("msg") 950 want := "" 951 have := string(bytes.TrimRight(bb.Bytes(), "\n")) 952 if have != want { 953 t.Errorf("\nwant %q\nhave %q", want, have) 954 } 955 956 bb.Reset() 957 958 logger.Build().Info("msg") 959 want = `"level"="info" "logger"="root" "msg"="msg"` 960 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 961 if have != want { 962 t.Errorf("\nwant %q\nhave %q", want, have) 963 } 964 965 bb.Reset() 966 967 logger.Build().Warning("msg") 968 want = `"level"="warning" "logger"="root" "msg"="msg"` 969 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 970 if have != want { 971 t.Errorf("\nwant %q\nhave %q", want, have) 972 } 973 974 bb.Reset() 975 976 logger.Build().Error(nil, "msg") 977 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 978 want = `"level"="error" "logger"="root" "msg"="msg"` 979 if have != want { 980 t.Errorf("\nwant %q\nhave %q", want, have) 981 } 982 }) 983 t.Run("verbosity=warning", func(t *testing.T) { 984 logger, bb := newLogger(WithVerbosity(VerbosityWarning)) 985 logger.Build() 986 987 logger.V(1).Build().Debug("msg") 988 logger.Build().Debug("msg") 989 logger.Build().Info("msg") 990 want := "" 991 have := string(bytes.TrimRight(bb.Bytes(), "\n")) 992 if have != want { 993 t.Errorf("\nwant %q\nhave %q", want, have) 994 } 995 996 bb.Reset() 997 998 logger.Build().Warning("msg") 999 want = `"level"="warning" "logger"="root" "msg"="msg"` 1000 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 1001 if have != want { 1002 t.Errorf("\nwant %q\nhave %q", want, have) 1003 } 1004 1005 bb.Reset() 1006 1007 logger.Build().Error(nil, "msg") 1008 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 1009 want = `"level"="error" "logger"="root" "msg"="msg"` 1010 if have != want { 1011 t.Errorf("\nwant %q\nhave %q", want, have) 1012 } 1013 }) 1014 t.Run("verbosity=error", func(t *testing.T) { 1015 logger, bb := newLogger(WithVerbosity(VerbosityError)) 1016 logger.Build() 1017 1018 logger.V(1).Build().Debug("msg") 1019 logger.Build().Debug("msg") 1020 logger.Build().Info("msg") 1021 logger.Build().Warning("msg") 1022 want := "" 1023 have := string(bytes.TrimRight(bb.Bytes(), "\n")) 1024 if have != want { 1025 t.Errorf("\nwant %q\nhave %q", want, have) 1026 } 1027 1028 bb.Reset() 1029 1030 logger.Build().Error(nil, "msg") 1031 have = string(bytes.TrimRight(bb.Bytes(), "\n")) 1032 want = `"level"="error" "logger"="root" "msg"="msg"` 1033 if have != want { 1034 t.Errorf("\nwant %q\nhave %q", want, have) 1035 } 1036 }) 1037 t.Run("verbosity=none", func(t *testing.T) { 1038 logger, bb := newLogger(WithVerbosity(VerbosityNone)) 1039 logger.Build() 1040 1041 logger.V(1).Build().Debug("msg") 1042 logger.Build().Debug("msg") 1043 logger.Build().Info("msg") 1044 logger.Build().Warning("msg") 1045 logger.Build().Error(nil, "msg") 1046 want := "" 1047 have := string(bytes.TrimRight(bb.Bytes(), "\n")) 1048 if have != want { 1049 t.Errorf("\nwant %q\nhave %q", want, have) 1050 } 1051 }) 1052 }