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  }