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  }