diff --git a/go.mod b/go.mod index f6b58d2..ce7fecb 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/shipengqi/log go 1.17 require ( + github.com/shipengqi/errors v0.1.4 github.com/stretchr/testify v1.7.0 go.uber.org/zap v1.21.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 diff --git a/go.sum b/go.sum index ffdf7e6..348e601 100644 --- a/go.sum +++ b/go.sum @@ -10,10 +10,13 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/shipengqi/errors v0.1.4 h1:X7OU/yRKM9AAuSDH9xGspLFj0oI62IdT+9IpgcqvKTY= +github.com/shipengqi/errors v0.1.4/go.mod h1:xymtEu8M3i0SIEhhMyShYHZfn/e7tBQwjuLHyQ/UGCw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= diff --git a/log.go b/log.go index 4da9a7b..23906f9 100644 --- a/log.go +++ b/log.go @@ -2,9 +2,7 @@ package log import ( - "errors" "log" - "strings" "go.uber.org/zap" ) @@ -23,72 +21,12 @@ func L() *Logger { // EncodedFilename returns the filename for logging when DisableFile is false. func EncodedFilename() string { - return _globalL.encodedFilename + return _globalL.EncodedFilename() } - -// ErrSlice represents an error slice. -type ErrSlice struct { - errs []error -} - -// NewErrSlice returns a new ErrSlice. -func NewErrSlice() ErrSlice { - return ErrSlice{ - errs: make([]error, 0), - } -} - -// Error implements error interface. -func (es ErrSlice) Error() string { - var b strings.Builder - if len(es.errs) == 0 { - return "" - } - - b.WriteString(es.errs[0].Error()) - - for i := 1; i < len(es.errs); i++ { - b.WriteString(" : ") - b.WriteString(es.errs[i].Error()) - } - - return b.String() -} - -// Len returns the length of slice. -func (es *ErrSlice) Len() int { - return len(es.errs) -} - -// Append appends an error to the slice, nil error will be ignored. -func (es *ErrSlice) Append(err ...error) { - for i := range err { - if err[i] == nil { - continue - } - if v, ok := err[i].(ErrSlice); ok { - if v.Len() == 0 { - continue - } - } - if v, ok := err[i].(*ErrSlice); ok { - if v.Len() == 0 { - continue - } - } - es.errs = append(es.errs, err[i]) - } -} - -// AppendStr appends an error string to the slice, empty string will be ignored. -func (es *ErrSlice) AppendStr(err ...string) { - for i := range err { - if len(err[i]) == 0 { - continue - } - es.errs = append(es.errs, errors.New(err[i])) - } +// SugaredL returns global sugared logger. +func SugaredL() *zap.SugaredLogger { + return _globalL.Sugared() } // StdLogger returns logger of standard library which writes to supplied zap @@ -250,6 +188,21 @@ func Fatal(msg string, keysAndValues ...interface{}) { _globalL.Fatal(msg, keysAndValues...) } +// Print logs a message at level Print. +func Print(args ...interface{}) { + _globalL.Print(args...) +} + +// Println logs a message at level Print. +func Println(args ...interface{}) { + _globalL.Println(args...) +} + +// Printf logs a message at level Print. +func Printf(format string, args ...interface{}) { + _globalL.Printf(format, args...) +} + // AtLevelt logs a message at Level. func AtLevelt(level Level, msg string, fields ...Field) { _globalL.AtLevelt(level, msg, fields...) diff --git a/log_test.go b/log_test.go index 33831fb..90e7339 100644 --- a/log_test.go +++ b/log_test.go @@ -3,7 +3,6 @@ package log import ( "bufio" "bytes" - "errors" "io/ioutil" "os" "path/filepath" @@ -30,10 +29,15 @@ func TestGlobalLogger(t *testing.T) { str := "Hello, world!" opts := NewOptions() Configure(opts) + + sugared := SugaredL() + assert.NotNil(t, sugared) + Debugf("Hello, %s", name+"1") Infof("Hello, %s", name+"2") Warnf("Hello, %s", name+"3") Errorf("Hello, %s", name+"4") + Printf("Hello, %s", name+"5") opts.ConsoleLevel = DebugLevel.String() Configure(opts) @@ -41,6 +45,7 @@ func TestGlobalLogger(t *testing.T) { Info(str) Warn(str) Error(str) + Print(str) opts.DisableConsoleColor = true Configure(opts) @@ -49,19 +54,23 @@ func TestGlobalLogger(t *testing.T) { L().Info(str) L().Warn(str) L().Error(str) + L().Println(str) expected := []string{ "\x1b[34mINFO\x1b[0m Hello, world2", "\x1b[33mWARN\x1b[0m Hello, world3", "\x1b[31mERROR\x1b[0m Hello, world4", + "\x1b[34mINFO\x1b[0m Hello, world5", "\x1b[35mDEBUG\x1b[0m Hello, world!", "\x1b[34mINFO\x1b[0m Hello, world!", "\x1b[33mWARN\x1b[0m Hello, world!", "\x1b[31mERROR\x1b[0m Hello, world!", + "\x1b[34mINFO\x1b[0m Hello, world!", "DEBUG Hello, world!", "INFO Hello, world!", "WARN Hello, world!", "ERROR Hello, world!", + "INFO Hello, world!", } _ = w.Close() stdout, _ := ioutil.ReadAll(r) @@ -95,10 +104,10 @@ func TestGlobalLogger(t *testing.T) { L().Warnf("Hello, %s", name+"3") L().Errorf("Hello, %s", name+"4") expected := []string{ - "DEBUG log/log_test.go:93 Hello, world1", - "INFO log/log_test.go:94 Hello, world2", - "WARN log/log_test.go:95 Hello, world3", - "ERROR log/log_test.go:96 Hello, world4", + "DEBUG log/log_test.go:102 Hello, world1", + "INFO log/log_test.go:103 Hello, world2", + "WARN log/log_test.go:104 Hello, world3", + "ERROR log/log_test.go:105 Hello, world4", } _ = w.Close() stdout, _ := ioutil.ReadAll(r) @@ -131,10 +140,10 @@ func TestGlobalLogger(t *testing.T) { L().Warnf("Hello, %s", name+"3") L().Errorf("Hello, %s", name+"4") expected := []string{ - "debug log/log_test.go:129 Hello, world1", - "info log/log_test.go:130 Hello, world2", - "warn log/log_test.go:131 Hello, world3", - "error log/log_test.go:132 Hello, world4", + "debug log/log_test.go:138 Hello, world1", + "info log/log_test.go:139 Hello, world2", + "warn log/log_test.go:140 Hello, world3", + "error log/log_test.go:141 Hello, world4", } _ = w.Close() stdout, _ := ioutil.ReadAll(r) @@ -172,10 +181,10 @@ func TestGlobalLogger(t *testing.T) { L().Warnf("Hello, %s", name+"3") L().Errorf("Hello, %s", name+"4") expected := []string{ - "debug log/log_test.go:170 Hello, world1", - "info log/log_test.go:171 Hello, world2", - "warn log/log_test.go:172 Hello, world3", - "error log/log_test.go:173 Hello, world4", + "debug log/log_test.go:179 Hello, world1", + "info log/log_test.go:180 Hello, world2", + "warn log/log_test.go:181 Hello, world3", + "error log/log_test.go:182 Hello, world4", } _ = w.Close() stdout, _ := ioutil.ReadAll(r) @@ -306,43 +315,6 @@ func TestLoggerClose(t *testing.T) { }) } -func TestErrSlice(t *testing.T) { - t.Run("Generic ErrSlice", func(t *testing.T) { - es := NewErrSlice() - assert.Equal(t, "", es.Error()) - es.Append(errors.New("error1")) - assert.Equal(t, "error1", es.Error()) - - es.AppendStr("error2") - assert.Equal(t, "error1 : error2", es.Error()) - - es.Append(errors.New("error3")) - assert.Equal(t, "error1 : error2 : error3", es.Error()) - }) - - t.Run("Append ErrSlice", func(t *testing.T) { - es := NewErrSlice() - es.Append(NewErrSlice()) - assert.Equal(t, 0, es.Len()) - - es.Append(errors.New("error1")) - assert.Equal(t, 1, es.Len()) - - es2 := NewErrSlice() - es2.Append(errors.New("error1")) - es.Append(es2) - assert.Equal(t, 2, es.Len()) - }) - - t.Run("Append *ErrSlice", func(t *testing.T) { - es := NewErrSlice() - es2 := NewErrSlice() - // es2.Append(errors.New("error1")) - es.Append(&es2) - assert.Equal(t, 0, es.Len()) - }) -} - func TestStdInfoLogger(t *testing.T) { opts := NewOptions() opts.DisableFile = true @@ -385,7 +357,6 @@ func TestStdInfoLogger(t *testing.T) { }) } - // fileWithLineNum return the file name and line number of the current file func fileWithLineNum() string { for i := 4; i < 15; i++ { diff --git a/logger.go b/logger.go index 96ac23c..504bc0e 100644 --- a/logger.go +++ b/logger.go @@ -1,10 +1,12 @@ package log import ( + "fmt" "io" "os" "strings" + "github.com/shipengqi/errors" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) @@ -26,8 +28,8 @@ type Logger struct { // New creates a new Logger. func New(opts *Options, encoders ...Encoder) *Logger { - if errs := opts.Validate(); errs.Len() > 0 { - panic(errs) + if errs := opts.Validate(); len(errs) > 0 { + panic(errors.NewAggregate(errs)) } l := &Logger{} @@ -201,6 +203,21 @@ func (l *Logger) Fatal(msg string, keysAndValues ...interface{}) { l.sugared.Fatalw(msg, keysAndValues...) } +// Print logs a message at level Print. +func (l *Logger) Print(args ...interface{}) { + l.log.Info(fmt.Sprint(args...)) +} + +// Println logs a message at level Print. +func (l *Logger) Println(args ...interface{}) { + l.log.Info(fmt.Sprint(args...)) +} + +// Printf logs a message at level Print. +func (l *Logger) Printf(format string, args ...interface{}) { + l.log.Info(fmt.Sprintf(format, args...)) +} + func (l *Logger) AtLevelt(level Level, msg string, fields ...Field) { switch level { case DebugLevel: @@ -261,6 +278,13 @@ func (l *Logger) AtLevelf(level Level, msg string, args ...interface{}) { } } +// Sugared returns sugared logger. +func (l *Logger) Sugared() *zap.SugaredLogger { + return l.sugared +} + +// WithValues creates a child logger and adds some Field of +// context to this logger. func (l *Logger) WithValues(fields ...Field) *Logger { newl := l.log.With(fields...) return &Logger{ @@ -269,10 +293,13 @@ func (l *Logger) WithValues(fields ...Field) *Logger { } } +// Flush calls the underlying Core's Sync method, flushing any buffered +// log entries. Applications should take care to call Sync before exiting. func (l *Logger) Flush() error { return l.log.Sync() } +// Close implements io.Closer, and closes the current logfile of default logger. func (l *Logger) Close() error { // https://github.com/uber-go/zap/issues/772 _ = l.Flush() diff --git a/options.go b/options.go index 82a4517..7785650 100644 --- a/options.go +++ b/options.go @@ -60,28 +60,29 @@ func NewOptions() *Options { } // Validate validates the options fields. -func (o *Options) Validate() ErrSlice { - var errs ErrSlice +func (o *Options) Validate() []error { + var errs []error var level Level if o.ConsoleLevel != "" { if err := level.UnmarshalText([]byte(o.ConsoleLevel)); err != nil { - errs.Append(err) + errs = append(errs, err) } } if o.FileLevel != "" { if err := level.UnmarshalText([]byte(o.FileLevel)); err != nil { - errs.Append(err) + errs = append(errs, err) } } if o.DisableConsole && o.DisableFile { - errs.Append(errors.New("no enabled logger")) + errs = append(errs, errors.New("no enabled logger, one or more of " + + "(DisableConsole, DisableFile) must be set to false")) } if !o.DisableFile && o.Output == "" { - errs.Append(errors.New("no log output")) + errs = append(errs, errors.New("no log output, 'Output' must be set")) } return errs } diff --git a/options_test.go b/options_test.go index 47335d1..47093e2 100644 --- a/options_test.go +++ b/options_test.go @@ -3,6 +3,7 @@ package log import ( "testing" + "github.com/shipengqi/errors" "github.com/stretchr/testify/assert" ) @@ -12,7 +13,7 @@ func Test_Default_Options_Validate(t *testing.T) { errs := opts.Validate() expected := 0 - assert.Equal(t, expected, errs.Len()) + assert.Equal(t, expected, len(errs)) }) t.Run("output error", func(t *testing.T) { @@ -20,8 +21,8 @@ func Test_Default_Options_Validate(t *testing.T) { opts.DisableFile = false errs := opts.Validate() - assert.Equal(t, 1, errs.Len()) - assert.Equal(t, "no log output", errs.Error()) + assert.Equal(t, 1, len(errs)) + assert.Equal(t, "no log output, 'Output' must be set", errors.NewAggregate(errs).Error()) }) t.Run("no enabled logger error", func(t *testing.T) { @@ -29,8 +30,8 @@ func Test_Default_Options_Validate(t *testing.T) { opts.DisableConsole = true errs := opts.Validate() - assert.Equal(t, 1, errs.Len()) - assert.Equal(t, "no enabled logger", errs.Error()) + assert.Equal(t, 1, len(errs)) + assert.Equal(t, "no enabled logger, one or more of (DisableConsole, DisableFile) must be set to false", errors.NewAggregate(errs).Error()) }) t.Run("unrecognized level error", func(t *testing.T) { @@ -39,7 +40,7 @@ func Test_Default_Options_Validate(t *testing.T) { opts.FileLevel = "errorlevel" errs := opts.Validate() - assert.Equal(t, 2, errs.Len()) - assert.Equal(t, "unrecognized level: \"errorlevel\" : unrecognized level: \"errorlevel\"", errs.Error()) + assert.Equal(t, 2, len(errs)) + assert.Equal(t, "unrecognized level: \"errorlevel\"", errors.NewAggregate(errs).Error()) }) }