From 97ba8e61ff2e76be24a71f911a90476d810fa19b Mon Sep 17 00:00:00 2001 From: Dnomd343 Date: Sun, 4 Feb 2024 23:43:20 +0800 Subject: [PATCH 01/10] refactor: expand source code directory --- {next/logger => logger}/encoder.go | 0 {next/logger => logger}/interface.go | 0 {next/logger => logger}/logger.go | 0 next/main.go => main.go | 2 +- 4 files changed, 1 insertion(+), 1 deletion(-) rename {next/logger => logger}/encoder.go (100%) rename {next/logger => logger}/interface.go (100%) rename {next/logger => logger}/logger.go (100%) rename next/main.go => main.go (97%) diff --git a/next/logger/encoder.go b/logger/encoder.go similarity index 100% rename from next/logger/encoder.go rename to logger/encoder.go diff --git a/next/logger/interface.go b/logger/interface.go similarity index 100% rename from next/logger/interface.go rename to logger/interface.go diff --git a/next/logger/logger.go b/logger/logger.go similarity index 100% rename from next/logger/logger.go rename to logger/logger.go diff --git a/next/main.go b/main.go similarity index 97% rename from next/main.go rename to main.go index 4083a99..a8c03a1 100644 --- a/next/main.go +++ b/main.go @@ -1,7 +1,7 @@ package main import ( - "XProxy/next/logger" + "XProxy/logger" "os" ) From d29208f465b3f91194dc7dd331216912dfa0a18b Mon Sep 17 00:00:00 2001 From: Dnomd343 Date: Mon, 5 Feb 2024 23:22:31 +0800 Subject: [PATCH 02/10] refactor: enhance logger module --- logger/encoder.go | 17 ++++--- logger/interface.go | 38 +++++++++------- logger/logger.go | 105 +++++++++++++++++++++++++------------------- main.go | 6 ++- 4 files changed, 94 insertions(+), 72 deletions(-) diff --git a/logger/encoder.go b/logger/encoder.go index b9eda34..e2086ed 100644 --- a/logger/encoder.go +++ b/logger/encoder.go @@ -25,9 +25,9 @@ func getGid() uint64 { // getCaller calculate relative source path of caller. func getCaller(ec zapcore.EntryCaller, verbose bool) string { - file, err := filepath.Rel(logHandle.path, ec.File) + file, err := filepath.Rel(project, ec.File) if err != nil { - return "undefined" + return "unknown" } if verbose { return file + ":" + strconv.Itoa(ec.Line) @@ -46,24 +46,23 @@ func timeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { func timeColoredEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString(fmt.Sprintf( "%s %s", - color.Cyan.Render(logHandle.prefix), // colored prefix + color.Cyan.Render(logger.prefix), // colored prefix color.Gray.Render(t.Format("2006-01-02 15:04:05.000")), )) } // callerEncoder formats caller in square brackets. func callerEncoder(ec zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) { - if !logHandle.verbose { + if !logger.verbose { enc.AppendString("[" + getCaller(ec, false) + "]") return } enc.AppendString(fmt.Sprintf("[%d] [%s]", getGid(), getCaller(ec, true))) } -// callerColoredEncoder formats caller in square brackets with -// magenta color. +// callerColoredEncoder formats caller in square brackets with magenta color. func callerColoredEncoder(ec zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) { - if !logHandle.verbose { + if !logger.verbose { enc.AppendString(color.Magenta.Render("[" + getCaller(ec, false) + "]")) return } @@ -79,8 +78,8 @@ func levelEncoder(level zapcore.Level, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString("[" + level.CapitalString() + "]") } -// levelColoredEncoder formats log level using square brackets -// and uses different colors. +// levelColoredEncoder formats log level using square brackets and uses +// different colors. func levelColoredEncoder(level zapcore.Level, enc zapcore.PrimitiveArrayEncoder) { levelStr := "[" + level.CapitalString() + "]" switch level { diff --git a/logger/interface.go b/logger/interface.go index f58b811..34d8fa5 100644 --- a/logger/interface.go +++ b/logger/interface.go @@ -14,46 +14,50 @@ const ( ) func Debugf(template string, args ...interface{}) { - logHandle.sugar.Debugf(template, args...) + logger.entry.Debugf(template, args...) } func Infof(template string, args ...interface{}) { - logHandle.sugar.Infof(template, args...) + logger.entry.Infof(template, args...) } func Warnf(template string, args ...interface{}) { - logHandle.sugar.Warnf(template, args...) + logger.entry.Warnf(template, args...) } func Errorf(template string, args ...interface{}) { - logHandle.sugar.Errorf(template, args...) + logger.entry.Errorf(template, args...) } func Panicf(template string, args ...interface{}) { - logHandle.sugar.Panicf(template, args...) + logger.entry.Panicf(template, args...) } // GetLevel return the current logger level. func GetLevel() zapcore.Level { - return logHandle.level.Level() + return logger.level.Level() } -// SetLevel configure logger output level. Note that debug level -// will output more information and reduce performance. +// SetLevel configure logger output level. Note that debug level will output +// more information and reduce performance. func SetLevel(level zapcore.Level) { - logHandle.level.SetLevel(level) + logger.level.SetLevel(level) if level == DebugLevel { - logHandle.verbose = true + logger.verbose = true } else { - logHandle.verbose = false + logger.verbose = false } } -// AddOutputs adds more plain output channel to the logger module. -func AddOutputs(outputs ...io.Writer) { - var writers []zapcore.WriteSyncer - for _, output := range outputs { - writers = append(writers, zapcore.AddSync(output)) +// AddWriters add more writers to target log channel. +func AddWriters(colored bool, writers ...io.Writer) { + var syncWriters []zapcore.WriteSyncer + for _, writer := range writers { + syncWriters = append(syncWriters, zapcore.AddSync(writer)) + } + if !colored { + logger.addPlainWrites(syncWriters...) + } else { + logger.addColoredWrites(syncWriters...) } - addWrites(writers...) } diff --git a/logger/logger.go b/logger/logger.go index 8e792a5..8d6a21a 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -8,21 +8,44 @@ import ( "runtime" ) -type logger struct { - logger *zap.Logger - level *zap.AtomicLevel - sugar *zap.SugaredLogger +var project string // project absolute path +var logger *logCore // singleton logger handle + +// logChannel handle multiple writers with unified format. +type logChannel struct { + encoder zapcore.Encoder writers []zapcore.WriteSyncer - stderr zapcore.Core // fixed stderr output - prefix string // custom output prefix - path string // project absolute path - verbose bool // show goroutine id and caller line } -var logHandle *logger // singleton logger handle +// logCore manage log level, channels and other interfaces. +type logCore struct { + prefix string // custom output prefix + verbose bool // show verbose information + + plain logChannel // log channel with plain text + colored logChannel // log channel with colored text + + level *zap.AtomicLevel // zap log level pointer + entry *zap.SugaredLogger // zap sugared logger entry +} + +func init() { + _, src, _, _ := runtime.Caller(0) // absolute path of current code + project = path.Join(path.Dir(src), "../") -// logConfig generates log config for XProxy. -func logConfig(colored bool) zapcore.EncoderConfig { + zapLevel := zap.NewAtomicLevelAt(InfoLevel) // using info level in default + logger = &logCore{ + verbose: false, + level: &zapLevel, + prefix: "[XProxy]", + plain: buildChannel(false), + colored: buildChannel(true), + } + logger.addColoredWrites(os.Stderr) // output into stderr in default +} + +// buildChannel generate logChannel with `colored` option. +func buildChannel(colored bool) logChannel { config := zapcore.EncoderConfig{ ConsoleSeparator: " ", MessageKey: "msg", @@ -33,46 +56,40 @@ func logConfig(colored bool) zapcore.EncoderConfig { EncodeLevel: levelEncoder, EncodeCaller: callerEncoder, } - if colored { + if colored { // using colored version config.EncodeTime = timeColoredEncoder config.EncodeLevel = levelColoredEncoder config.EncodeCaller = callerColoredEncoder } - return config + return logChannel{ + encoder: zapcore.NewConsoleEncoder(config), + writers: []zapcore.WriteSyncer{}, // without any writer + } } -func init() { - zapLevel := zap.NewAtomicLevelAt(InfoLevel) // using info level in default - zapCore := zapcore.NewCore( - zapcore.NewConsoleEncoder(logConfig(true)), // colorful output - zapcore.Lock(os.Stderr), - zapLevel, - ) - zapLogger := zap.New(zapCore, zap.AddCaller(), zap.AddCallerSkip(1)) - _, src, _, _ := runtime.Caller(0) // absolute path of current code - logHandle = &logger{ - logger: zapLogger, - level: &zapLevel, - stderr: zapCore, - sugar: zapLogger.Sugar(), - writers: []zapcore.WriteSyncer{}, - path: path.Join(path.Dir(src), "../"), - prefix: "[XProxy]", - verbose: false, +// update refreshes the binding of the log core to the writers. +func (handle *logCore) update() { + buildCore := func(channel *logChannel) zapcore.Core { // build zap core from logChannel + return zapcore.NewCore( + channel.encoder, + zap.CombineWriteSyncers(channel.writers...), + handle.level, + ) } + handle.entry = zap.New( + zapcore.NewTee(buildCore(&handle.plain), buildCore(&handle.colored)), + zap.AddCaller(), zap.AddCallerSkip(1), + ).Sugar() } -// addWrites adds more plain log writers. -func addWrites(writers ...zapcore.WriteSyncer) { - logHandle.writers = append(logHandle.writers, writers...) - plainCore := zapcore.NewCore( - zapcore.NewConsoleEncoder(logConfig(false)), // without colored - zap.CombineWriteSyncers(logHandle.writers...), - logHandle.level, - ) - logHandle.logger = zap.New( - zapcore.NewTee(logHandle.stderr, plainCore), - zap.AddCaller(), zap.AddCallerSkip(1), - ) - logHandle.sugar = logHandle.logger.Sugar() +// addPlainWrites adds plain text writers to the logCore. +func (handle *logCore) addPlainWrites(writers ...zapcore.WriteSyncer) { + handle.plain.writers = append(handle.plain.writers, writers...) + handle.update() +} + +// addColoredWrites adds colored text writers to the logCore. +func (handle *logCore) addColoredWrites(writers ...zapcore.WriteSyncer) { + handle.colored.writers = append(handle.colored.writers, writers...) + handle.update() } diff --git a/main.go b/main.go index a8c03a1..7efa3d8 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,8 @@ import ( ) func main() { + //logger.SetLevel(zap.DebugLevel) + //logger.Debugf("here is %s level", "debug") //logger.Infof("here is %s level", "info") //logger.Warnf("here is %s level", "warn") @@ -19,13 +21,13 @@ func main() { logger.Debugf("output msg 1 at debug") logger.Infof("output msg 1 at info") logger.Warnf("output msg 1 at warn") - logger.AddOutputs(fp1, fp2) + logger.AddWriters(false, fp1, fp2) logger.SetLevel(logger.InfoLevel) logger.Debugf("output msg 2 at debug") logger.Infof("output msg 2 at info") logger.Warnf("output msg 2 at warn") logger.SetLevel(logger.WarnLevel) - logger.AddOutputs(fp3) + logger.AddWriters(true, fp3) logger.Debugf("output msg 3 at debug") logger.Infof("output msg 3 at info") logger.Warnf("output msg 3 at warn") From 07c247a49171bfcd4da823c4dec91b6a4d143735 Mon Sep 17 00:00:00 2001 From: Dnomd343 Date: Tue, 6 Feb 2024 19:21:13 +0800 Subject: [PATCH 03/10] feat: get goroutine id using `goid` --- go.mod | 1 + go.sum | 2 ++ logger/encoder.go | 18 +++--------------- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 3643107..6990193 100644 --- a/go.mod +++ b/go.mod @@ -18,6 +18,7 @@ require ( github.com/gookit/color v1.5.4 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.17 // indirect + github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67 // indirect github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/sys v0.10.0 // indirect diff --git a/go.sum b/go.sum index 5bee901..d8d95c9 100644 --- a/go.sum +++ b/go.sum @@ -13,6 +13,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67 h1:jik8PHtAIsPlCRJjJzl4udgEf7hawInF9texMeO2jrU= +github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= diff --git a/logger/encoder.go b/logger/encoder.go index e2086ed..2aa0e43 100644 --- a/logger/encoder.go +++ b/logger/encoder.go @@ -1,28 +1,16 @@ package logger import ( - "bytes" "fmt" "github.com/gookit/color" + "github.com/petermattis/goid" "go.uber.org/zap/zapcore" "path/filepath" - "runtime" "strconv" "strings" "time" ) -// getGid get goroutine ID only for debugging. -// -> https://blog.sgmansfield.com/2015/12/goroutine-ids/ -func getGid() uint64 { - b := make([]byte, 64) - b = b[:runtime.Stack(b, false)] - b = bytes.TrimPrefix(b, []byte("goroutine ")) - b = b[:bytes.IndexByte(b, ' ')] - n, _ := strconv.ParseUint(string(b), 10, 64) - return n -} - // getCaller calculate relative source path of caller. func getCaller(ec zapcore.EntryCaller, verbose bool) string { file, err := filepath.Rel(project, ec.File) @@ -57,7 +45,7 @@ func callerEncoder(ec zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString("[" + getCaller(ec, false) + "]") return } - enc.AppendString(fmt.Sprintf("[%d] [%s]", getGid(), getCaller(ec, true))) + enc.AppendString(fmt.Sprintf("[%d] [%s]", goid.Get(), getCaller(ec, true))) } // callerColoredEncoder formats caller in square brackets with magenta color. @@ -68,7 +56,7 @@ func callerColoredEncoder(ec zapcore.EntryCaller, enc zapcore.PrimitiveArrayEnco } enc.AppendString(fmt.Sprintf( "%s %s", - color.Blue.Render(fmt.Sprintf("[%d]", getGid())), + color.Blue.Render(fmt.Sprintf("[%d]", goid.Get())), color.Magenta.Render("["+getCaller(ec, true)+"]"), )) } From 11acb494bee67f43392c06b0cca55956b84cb7ee Mon Sep 17 00:00:00 2001 From: Dnomd343 Date: Tue, 6 Feb 2024 19:29:21 +0800 Subject: [PATCH 04/10] test: encoder of logger module --- go.mod | 24 ++++--- go.sum | 62 ++++++++++++----- logger/encoder_test.go | 122 +++++++++++++++++++++++++++++++++ mocks/PrimitiveArrayEncoder.go | 114 ++++++++++++++++++++++++++++++ 4 files changed, 295 insertions(+), 27 deletions(-) create mode 100644 logger/encoder_test.go create mode 100644 mocks/PrimitiveArrayEncoder.go diff --git a/go.mod b/go.mod index 6990193..95080e6 100644 --- a/go.mod +++ b/go.mod @@ -4,22 +4,26 @@ go 1.21.0 require ( github.com/BurntSushi/toml v1.3.2 - github.com/andybalholm/brotli v1.0.5 + github.com/andybalholm/brotli v1.1.0 github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a - github.com/klauspost/compress v1.16.7 + github.com/gookit/color v1.5.4 + github.com/klauspost/compress v1.17.6 + github.com/magiconair/properties v1.8.7 + github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67 github.com/robfig/cron v1.2.0 github.com/sirupsen/logrus v1.9.3 - go.uber.org/zap v1.25.0 + github.com/stretchr/testify v1.8.4 + go.uber.org/zap v1.26.0 gopkg.in/yaml.v3 v3.0.1 ) require ( - github.com/fatih/color v1.15.0 // indirect - github.com/gookit/color v1.5.4 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect - github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67 // indirect - github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/objx v0.5.1 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/sys v0.10.0 // indirect + golang.org/x/sys v0.16.0 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) diff --git a/go.sum b/go.sum index d8d95c9..6119163 100644 --- a/go.sum +++ b/go.sum @@ -1,36 +1,64 @@ +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= +github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a h1:v6zMvHuY9yue4+QkG/HQ/W67wvtQmWJ4SDo9aK/GIno= github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a/go.mod h1:I79BieaU4fxrw4LMXby6q5OS9XnoR9UIKLOzDFjUmuw= github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= +github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67 h1:jik8PHtAIsPlCRJjJzl4udgEf7hawInF9texMeO2jrU= github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +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/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= +github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= -github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= -go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= +golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/logger/encoder_test.go b/logger/encoder_test.go new file mode 100644 index 0000000..9020805 --- /dev/null +++ b/logger/encoder_test.go @@ -0,0 +1,122 @@ +package logger + +import ( + "XProxy/mocks" + "fmt" + "github.com/magiconair/properties/assert" + "github.com/petermattis/goid" + "github.com/stretchr/testify/mock" + "go.uber.org/zap/zapcore" + "runtime" + "testing" + "time" +) + +var testTime = time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC) + +// zapCaller build fake caller with the absolute path of current code. +func zapCaller() zapcore.EntryCaller { + _, srcPath, _, _ := runtime.Caller(0) + return zapcore.EntryCaller{ + File: srcPath, + Line: 0, + } +} + +func Test_getCaller(t *testing.T) { + caller := zapCaller() + assert.Equal(t, getCaller(caller, false), "logger/encoder_test") + assert.Equal(t, getCaller(caller, true), "logger/encoder_test.go:0") + + caller.File = "Invalid Path" + assert.Equal(t, getCaller(caller, true), "unknown") + assert.Equal(t, getCaller(caller, false), "unknown") +} + +func Test_timeEncoder(t *testing.T) { + { + encoder := mocks.NewPrimitiveArrayEncoder(t) + encoder.On("AppendString", mock.Anything).Once() + timeEncoder(testTime, encoder) + encoder.AssertCalled(t, "AppendString", "2000-01-01 00:00:00.000") + } + { + encoder := mocks.NewPrimitiveArrayEncoder(t) + encoder.On("AppendString", mock.Anything).Once() + timeColoredEncoder(testTime, encoder) + exceptPrefix := "\x1b[36m" + logger.prefix + "\x1b[0m" + encoder.AssertCalled(t, "AppendString", exceptPrefix+" \x1b[90m2000-01-01 00:00:00.000\x1b[0m") + } +} + +func Test_callerEncoder(t *testing.T) { + logger.verbose = false + { + encoder := mocks.NewPrimitiveArrayEncoder(t) + encoder.On("AppendString", mock.Anything).Once() + callerEncoder(zapCaller(), encoder) + encoder.AssertCalled(t, "AppendString", "[logger/encoder_test]") + } + { + encoder := mocks.NewPrimitiveArrayEncoder(t) + encoder.On("AppendString", mock.Anything).Once() + callerColoredEncoder(zapCaller(), encoder) + encoder.AssertCalled(t, "AppendString", "\x1b[35m[logger/encoder_test]\x1b[0m") + } + + logger.verbose = true + { + encoder := mocks.NewPrimitiveArrayEncoder(t) + encoder.On("AppendString", mock.Anything).Once() + callerEncoder(zapCaller(), encoder) + expectPrefix := fmt.Sprintf("[%d]", goid.Get()) + encoder.AssertCalled(t, "AppendString", expectPrefix+" [logger/encoder_test.go:0]") + } + { + encoder := mocks.NewPrimitiveArrayEncoder(t) + encoder.On("AppendString", mock.Anything).Once() + callerColoredEncoder(zapCaller(), encoder) + expectPrefix := fmt.Sprintf("\x1b[34m[%d]\x1b[0m", goid.Get()) + encoder.AssertCalled(t, "AppendString", expectPrefix+" \x1b[35m[logger/encoder_test.go:0]\x1b[0m") + } +} + +func Test_levelEncoder(t *testing.T) { + encoder := mocks.NewPrimitiveArrayEncoder(t) + enroll := func(values []string, call *mock.Call) *mock.Call { + for _, value := range values { + if call == nil { + call = encoder.On("AppendString", value).Once() + } else { + call = encoder.On("AppendString", value).Once().NotBefore(call) + } + } + return call + } + + caller := enroll([]string{ + "[DEBUG]", + "[INFO]", + "[WARN]", + "[ERROR]", + "[PANIC]", + }, nil) + levelEncoder(zapcore.DebugLevel, encoder) + levelEncoder(zapcore.InfoLevel, encoder) + levelEncoder(zapcore.WarnLevel, encoder) + levelEncoder(zapcore.ErrorLevel, encoder) + levelEncoder(zapcore.PanicLevel, encoder) + + enroll([]string{ + "\x1b[39m[DEBUG]\x1b[0m", + "\x1b[32m[INFO]\x1b[0m", + "\x1b[33m[WARN]\x1b[0m", + "\x1b[31m[ERROR]\x1b[0m", + "\x1b[91m[PANIC]\x1b[0m", + }, caller) + levelColoredEncoder(zapcore.DebugLevel, encoder) + levelColoredEncoder(zapcore.InfoLevel, encoder) + levelColoredEncoder(zapcore.WarnLevel, encoder) + levelColoredEncoder(zapcore.ErrorLevel, encoder) + levelColoredEncoder(zapcore.PanicLevel, encoder) +} diff --git a/mocks/PrimitiveArrayEncoder.go b/mocks/PrimitiveArrayEncoder.go new file mode 100644 index 0000000..d78572e --- /dev/null +++ b/mocks/PrimitiveArrayEncoder.go @@ -0,0 +1,114 @@ +// Code generated by mockery v2.40.1. DO NOT EDIT. + +package mocks + +import mock "github.com/stretchr/testify/mock" + +// PrimitiveArrayEncoder is an autogenerated mock type for the PrimitiveArrayEncoder type +type PrimitiveArrayEncoder struct { + mock.Mock +} + +// AppendBool provides a mock function with given fields: _a0 +func (_m *PrimitiveArrayEncoder) AppendBool(_a0 bool) { + _m.Called(_a0) +} + +// AppendByteString provides a mock function with given fields: _a0 +func (_m *PrimitiveArrayEncoder) AppendByteString(_a0 []byte) { + _m.Called(_a0) +} + +// AppendComplex128 provides a mock function with given fields: _a0 +func (_m *PrimitiveArrayEncoder) AppendComplex128(_a0 complex128) { + _m.Called(_a0) +} + +// AppendComplex64 provides a mock function with given fields: _a0 +func (_m *PrimitiveArrayEncoder) AppendComplex64(_a0 complex64) { + _m.Called(_a0) +} + +// AppendFloat32 provides a mock function with given fields: _a0 +func (_m *PrimitiveArrayEncoder) AppendFloat32(_a0 float32) { + _m.Called(_a0) +} + +// AppendFloat64 provides a mock function with given fields: _a0 +func (_m *PrimitiveArrayEncoder) AppendFloat64(_a0 float64) { + _m.Called(_a0) +} + +// AppendInt provides a mock function with given fields: _a0 +func (_m *PrimitiveArrayEncoder) AppendInt(_a0 int) { + _m.Called(_a0) +} + +// AppendInt16 provides a mock function with given fields: _a0 +func (_m *PrimitiveArrayEncoder) AppendInt16(_a0 int16) { + _m.Called(_a0) +} + +// AppendInt32 provides a mock function with given fields: _a0 +func (_m *PrimitiveArrayEncoder) AppendInt32(_a0 int32) { + _m.Called(_a0) +} + +// AppendInt64 provides a mock function with given fields: _a0 +func (_m *PrimitiveArrayEncoder) AppendInt64(_a0 int64) { + _m.Called(_a0) +} + +// AppendInt8 provides a mock function with given fields: _a0 +func (_m *PrimitiveArrayEncoder) AppendInt8(_a0 int8) { + _m.Called(_a0) +} + +// AppendString provides a mock function with given fields: _a0 +func (_m *PrimitiveArrayEncoder) AppendString(_a0 string) { + _m.Called(_a0) +} + +// AppendUint provides a mock function with given fields: _a0 +func (_m *PrimitiveArrayEncoder) AppendUint(_a0 uint) { + _m.Called(_a0) +} + +// AppendUint16 provides a mock function with given fields: _a0 +func (_m *PrimitiveArrayEncoder) AppendUint16(_a0 uint16) { + _m.Called(_a0) +} + +// AppendUint32 provides a mock function with given fields: _a0 +func (_m *PrimitiveArrayEncoder) AppendUint32(_a0 uint32) { + _m.Called(_a0) +} + +// AppendUint64 provides a mock function with given fields: _a0 +func (_m *PrimitiveArrayEncoder) AppendUint64(_a0 uint64) { + _m.Called(_a0) +} + +// AppendUint8 provides a mock function with given fields: _a0 +func (_m *PrimitiveArrayEncoder) AppendUint8(_a0 uint8) { + _m.Called(_a0) +} + +// AppendUintptr provides a mock function with given fields: _a0 +func (_m *PrimitiveArrayEncoder) AppendUintptr(_a0 uintptr) { + _m.Called(_a0) +} + +// NewPrimitiveArrayEncoder creates a new instance of PrimitiveArrayEncoder. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewPrimitiveArrayEncoder(t interface { + mock.TestingT + Cleanup(func()) +}) *PrimitiveArrayEncoder { + mock := &PrimitiveArrayEncoder{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} From 09ea911a283a2e5042121ea8b8ea8e76f5b1468f Mon Sep 17 00:00:00 2001 From: Dnomd343 Date: Tue, 6 Feb 2024 21:48:51 +0800 Subject: [PATCH 05/10] test: perf encoder test suite --- logger/encoder_test.go | 127 +++++++++++++++++------------------------ 1 file changed, 52 insertions(+), 75 deletions(-) diff --git a/logger/encoder_test.go b/logger/encoder_test.go index 9020805..23cd8f1 100644 --- a/logger/encoder_test.go +++ b/logger/encoder_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/mock" "go.uber.org/zap/zapcore" "runtime" + "strings" "testing" "time" ) @@ -23,100 +24,76 @@ func zapCaller() zapcore.EntryCaller { } } +// encoderTest is a helper function to test buffer output of mock encoder. +func encoderTest(t *testing.T, exec func(*mocks.PrimitiveArrayEncoder), expect string) { + encoder := mocks.NewPrimitiveArrayEncoder(t) + encoder.On("AppendInt64", mock.Anything).Maybe() // only enroll used method + encoder.On("AppendString", mock.Anything).Maybe() + + exec(encoder) + var values []string + for _, call := range encoder.Calls { + values = append(values, fmt.Sprintf("%v", call.Arguments.Get(0))) + } + assert.Equal(t, strings.Join(values, " "), expect) +} + func Test_getCaller(t *testing.T) { caller := zapCaller() - assert.Equal(t, getCaller(caller, false), "logger/encoder_test") - assert.Equal(t, getCaller(caller, true), "logger/encoder_test.go:0") - caller.File = "Invalid Path" assert.Equal(t, getCaller(caller, true), "unknown") assert.Equal(t, getCaller(caller, false), "unknown") + + assert.Equal(t, getCaller(zapCaller(), false), "logger/encoder_test") + assert.Equal(t, getCaller(zapCaller(), true), "logger/encoder_test.go:0") } func Test_timeEncoder(t *testing.T) { - { - encoder := mocks.NewPrimitiveArrayEncoder(t) - encoder.On("AppendString", mock.Anything).Once() + encoderTest(t, func(encoder *mocks.PrimitiveArrayEncoder) { timeEncoder(testTime, encoder) - encoder.AssertCalled(t, "AppendString", "2000-01-01 00:00:00.000") - } - { - encoder := mocks.NewPrimitiveArrayEncoder(t) - encoder.On("AppendString", mock.Anything).Once() + }, "2000-01-01 00:00:00.000") + + encoderTest(t, func(encoder *mocks.PrimitiveArrayEncoder) { timeColoredEncoder(testTime, encoder) - exceptPrefix := "\x1b[36m" + logger.prefix + "\x1b[0m" - encoder.AssertCalled(t, "AppendString", exceptPrefix+" \x1b[90m2000-01-01 00:00:00.000\x1b[0m") - } + }, "\x1b[36m"+logger.prefix+"\x1b[0m \x1b[90m2000-01-01 00:00:00.000\x1b[0m") } func Test_callerEncoder(t *testing.T) { - logger.verbose = false - { - encoder := mocks.NewPrimitiveArrayEncoder(t) - encoder.On("AppendString", mock.Anything).Once() - callerEncoder(zapCaller(), encoder) - encoder.AssertCalled(t, "AppendString", "[logger/encoder_test]") - } - { - encoder := mocks.NewPrimitiveArrayEncoder(t) - encoder.On("AppendString", mock.Anything).Once() - callerColoredEncoder(zapCaller(), encoder) - encoder.AssertCalled(t, "AppendString", "\x1b[35m[logger/encoder_test]\x1b[0m") + verboseVal := logger.verbose + callerTest := func(entry func(zapcore.EntryCaller, zapcore.PrimitiveArrayEncoder), expect string) { + encoderTest(t, func(encoder *mocks.PrimitiveArrayEncoder) { + entry(zapCaller(), encoder) + }, expect) } + logger.verbose = false + callerTest(callerEncoder, "[logger/encoder_test]") + callerTest(callerColoredEncoder, "\x1b[35m[logger/encoder_test]\x1b[0m") + logger.verbose = true - { - encoder := mocks.NewPrimitiveArrayEncoder(t) - encoder.On("AppendString", mock.Anything).Once() - callerEncoder(zapCaller(), encoder) - expectPrefix := fmt.Sprintf("[%d]", goid.Get()) - encoder.AssertCalled(t, "AppendString", expectPrefix+" [logger/encoder_test.go:0]") - } - { - encoder := mocks.NewPrimitiveArrayEncoder(t) - encoder.On("AppendString", mock.Anything).Once() - callerColoredEncoder(zapCaller(), encoder) - expectPrefix := fmt.Sprintf("\x1b[34m[%d]\x1b[0m", goid.Get()) - encoder.AssertCalled(t, "AppendString", expectPrefix+" \x1b[35m[logger/encoder_test.go:0]\x1b[0m") - } + gid := fmt.Sprintf("[%d]", goid.Get()) + callerTest(callerEncoder, gid+" [logger/encoder_test.go:0]") + callerTest(callerColoredEncoder, "\x1b[34m"+gid+"\x1b[0m \x1b[35m[logger/encoder_test.go:0]\x1b[0m") + + logger.verbose = verboseVal } func Test_levelEncoder(t *testing.T) { - encoder := mocks.NewPrimitiveArrayEncoder(t) - enroll := func(values []string, call *mock.Call) *mock.Call { - for _, value := range values { - if call == nil { - call = encoder.On("AppendString", value).Once() - } else { - call = encoder.On("AppendString", value).Once().NotBefore(call) - } - } - return call + levelTest := func(entry func(zapcore.Level, zapcore.PrimitiveArrayEncoder), level zapcore.Level, expect string) { + encoderTest(t, func(encoder *mocks.PrimitiveArrayEncoder) { + entry(level, encoder) + }, expect) } - caller := enroll([]string{ - "[DEBUG]", - "[INFO]", - "[WARN]", - "[ERROR]", - "[PANIC]", - }, nil) - levelEncoder(zapcore.DebugLevel, encoder) - levelEncoder(zapcore.InfoLevel, encoder) - levelEncoder(zapcore.WarnLevel, encoder) - levelEncoder(zapcore.ErrorLevel, encoder) - levelEncoder(zapcore.PanicLevel, encoder) - - enroll([]string{ - "\x1b[39m[DEBUG]\x1b[0m", - "\x1b[32m[INFO]\x1b[0m", - "\x1b[33m[WARN]\x1b[0m", - "\x1b[31m[ERROR]\x1b[0m", - "\x1b[91m[PANIC]\x1b[0m", - }, caller) - levelColoredEncoder(zapcore.DebugLevel, encoder) - levelColoredEncoder(zapcore.InfoLevel, encoder) - levelColoredEncoder(zapcore.WarnLevel, encoder) - levelColoredEncoder(zapcore.ErrorLevel, encoder) - levelColoredEncoder(zapcore.PanicLevel, encoder) + levelTest(levelEncoder, zapcore.DebugLevel, "[DEBUG]") + levelTest(levelEncoder, zapcore.InfoLevel, "[INFO]") + levelTest(levelEncoder, zapcore.WarnLevel, "[WARN]") + levelTest(levelEncoder, zapcore.ErrorLevel, "[ERROR]") + levelTest(levelEncoder, zapcore.PanicLevel, "[PANIC]") + + levelTest(levelColoredEncoder, zapcore.DebugLevel, "\x1b[39m[DEBUG]\x1b[0m") + levelTest(levelColoredEncoder, zapcore.InfoLevel, "\x1b[32m[INFO]\x1b[0m") + levelTest(levelColoredEncoder, zapcore.WarnLevel, "\x1b[33m[WARN]\x1b[0m") + levelTest(levelColoredEncoder, zapcore.ErrorLevel, "\x1b[31m[ERROR]\x1b[0m") + levelTest(levelColoredEncoder, zapcore.PanicLevel, "\x1b[91m[PANIC]\x1b[0m") } From 5ca6d6a5212b43069a43f868b0b9073f1d12790b Mon Sep 17 00:00:00 2001 From: Dnomd343 Date: Tue, 6 Feb 2024 21:51:35 +0800 Subject: [PATCH 06/10] perf: update logger encoder --- logger/encoder.go | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/logger/encoder.go b/logger/encoder.go index 2aa0e43..a190ca5 100644 --- a/logger/encoder.go +++ b/logger/encoder.go @@ -1,7 +1,6 @@ package logger import ( - "fmt" "github.com/gookit/color" "github.com/petermattis/goid" "go.uber.org/zap/zapcore" @@ -11,6 +10,11 @@ import ( "time" ) +// / getGid return goroutine id with string. +func getGid() string { + return strconv.FormatInt(goid.Get(), 10) +} + // getCaller calculate relative source path of caller. func getCaller(ec zapcore.EntryCaller, verbose bool) string { file, err := filepath.Rel(project, ec.File) @@ -32,33 +36,28 @@ func timeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { // timeColoredEncoder formats the time as a colored string // with custom prefix. func timeColoredEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { - enc.AppendString(fmt.Sprintf( - "%s %s", - color.Cyan.Render(logger.prefix), // colored prefix - color.Gray.Render(t.Format("2006-01-02 15:04:05.000")), - )) + enc.AppendString(color.Cyan.Render(logger.prefix)) + enc.AppendString(color.Gray.Render(t.Format("2006-01-02 15:04:05.000"))) } // callerEncoder formats caller in square brackets. func callerEncoder(ec zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) { - if !logger.verbose { + if logger.verbose { + enc.AppendString("[" + getGid() + "]") + enc.AppendString("[" + getCaller(ec, true) + "]") + } else { enc.AppendString("[" + getCaller(ec, false) + "]") - return } - enc.AppendString(fmt.Sprintf("[%d] [%s]", goid.Get(), getCaller(ec, true))) } // callerColoredEncoder formats caller in square brackets with magenta color. func callerColoredEncoder(ec zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) { - if !logger.verbose { + if logger.verbose { + enc.AppendString(color.Blue.Render("[" + getGid() + "]")) + enc.AppendString(color.Magenta.Render("[" + getCaller(ec, true) + "]")) + } else { enc.AppendString(color.Magenta.Render("[" + getCaller(ec, false) + "]")) - return } - enc.AppendString(fmt.Sprintf( - "%s %s", - color.Blue.Render(fmt.Sprintf("[%d]", goid.Get())), - color.Magenta.Render("["+getCaller(ec, true)+"]"), - )) } // levelEncoder formats log level using square brackets. From 99f61438a013c0d4ba6a128ce0db4540e6ff7f67 Mon Sep 17 00:00:00 2001 From: Dnomd343 Date: Wed, 7 Feb 2024 00:05:27 +0800 Subject: [PATCH 07/10] update: add logger handle name --- logger/logger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logger/logger.go b/logger/logger.go index 8d6a21a..0c7ebc1 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -79,7 +79,7 @@ func (handle *logCore) update() { handle.entry = zap.New( zapcore.NewTee(buildCore(&handle.plain), buildCore(&handle.colored)), zap.AddCaller(), zap.AddCallerSkip(1), - ).Sugar() + ).Named("xproxy").Sugar() } // addPlainWrites adds plain text writers to the logCore. From 36a8f8266edfe85988b2cff495572093164072f4 Mon Sep 17 00:00:00 2001 From: Dnomd343 Date: Wed, 7 Feb 2024 00:06:14 +0800 Subject: [PATCH 08/10] test: add logger module test suite --- logger/logger_test.go | 47 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 logger/logger_test.go diff --git a/logger/logger_test.go b/logger/logger_test.go new file mode 100644 index 0000000..08ef07e --- /dev/null +++ b/logger/logger_test.go @@ -0,0 +1,47 @@ +package logger + +import ( + "github.com/stretchr/testify/assert" + "go.uber.org/zap/zapcore" + "os" + "path" + "testing" +) + +func Test_init(t *testing.T) { + pwd := path.Dir(zapCaller().File) + assert.Equal(t, path.Join(pwd, "../"), project) + + assert.NotNil(t, logger) + assert.NotNil(t, logger.entry) + assert.NotNil(t, logger.level) + + assert.Equal(t, logger.verbose, false) + assert.Equal(t, logger.prefix, "[XProxy]") + assert.Equal(t, logger.level.Level(), InfoLevel) + + assert.Equal(t, len(logger.plain.writers), 0) + assert.Equal(t, len(logger.colored.writers), 1) + + for _, level := range []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, PanicLevel} { + logger.level.SetLevel(level) + assert.Equal(t, logger.entry.Level(), level) + } + logger.level.SetLevel(InfoLevel) // revert to INFO level +} + +func Test_addWrites(t *testing.T) { + core := new(logCore) + assert.Equal(t, len(core.plain.writers), 0) + assert.Equal(t, len(core.colored.writers), 0) + + core.entry = nil + core.addPlainWrites(os.Stdout, os.Stdout, os.Stdout) + assert.NotNil(t, core.entry) + assert.Equal(t, len(core.plain.writers), 3) + + core.entry = nil + core.addColoredWrites(os.Stderr, os.Stderr, os.Stderr) + assert.NotNil(t, core.entry) + assert.Equal(t, len(core.plain.writers), 3) +} From 506ad861fe0b3b298f9bb15f866b3e512c8e0413 Mon Sep 17 00:00:00 2001 From: Dnomd343 Date: Wed, 7 Feb 2024 00:10:09 +0800 Subject: [PATCH 09/10] perf: avoid exposure of zapcore --- logger/interface.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/logger/interface.go b/logger/interface.go index 34d8fa5..6350292 100644 --- a/logger/interface.go +++ b/logger/interface.go @@ -5,6 +5,8 @@ import ( "io" ) +type Level = zapcore.Level + const ( DebugLevel = zapcore.DebugLevel InfoLevel = zapcore.InfoLevel @@ -34,13 +36,13 @@ func Panicf(template string, args ...interface{}) { } // GetLevel return the current logger level. -func GetLevel() zapcore.Level { +func GetLevel() Level { return logger.level.Level() } // SetLevel configure logger output level. Note that debug level will output // more information and reduce performance. -func SetLevel(level zapcore.Level) { +func SetLevel(level Level) { logger.level.SetLevel(level) if level == DebugLevel { logger.verbose = true From 3aaccc4b31ee4489ab51bf6fe866f95856aca546 Mon Sep 17 00:00:00 2001 From: Dnomd343 Date: Wed, 7 Feb 2024 01:33:40 +0800 Subject: [PATCH 10/10] test: add logger interface test suite --- logger/interface_test.go | 101 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 logger/interface_test.go diff --git a/logger/interface_test.go b/logger/interface_test.go new file mode 100644 index 0000000..c9b954c --- /dev/null +++ b/logger/interface_test.go @@ -0,0 +1,101 @@ +package logger + +import ( + "bytes" + "github.com/stretchr/testify/assert" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "os" + "regexp" + "strings" + "testing" +) + +var Levels = []Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, PanicLevel} + +func Test_level(t *testing.T) { + assert.Equal(t, DebugLevel, zap.DebugLevel) + assert.Equal(t, InfoLevel, zap.InfoLevel) + assert.Equal(t, WarnLevel, zap.WarnLevel) + assert.Equal(t, ErrorLevel, zap.ErrorLevel) + assert.Equal(t, PanicLevel, zap.PanicLevel) + + for _, level := range Levels { + SetLevel(level) + assert.Equal(t, GetLevel(), level) + assert.Equal(t, logger.verbose, level == DebugLevel) // verbose only for DEBUG level + } + SetLevel(InfoLevel) // revert to INFO level +} + +func Test_logger(t *testing.T) { + var plainBuf, plainTeeBuf bytes.Buffer + var coloredBuf, coloredTeeBuf bytes.Buffer + logger.plain.writers = []zapcore.WriteSyncer{} // clear slice + logger.colored.writers = []zapcore.WriteSyncer{} + AddWriters(false, &plainBuf, &plainTeeBuf) + AddWriters(true, &coloredBuf, &coloredTeeBuf) + logger.update() // apply test writers + + printLogs := func(usingLevel Level) { + SetLevel(usingLevel) + Debugf("Here is %s level", "DEBUG") + Infof("Here is %s level", "INFO") + Warnf("Here is %s level", "WARN") + Errorf("Here is %s level", "ERROR") + assert.Panics(t, func() { + Panicf("Here is %s level", "PANIC") + }) + } + printLogs(DebugLevel) // output into buffer + printLogs(InfoLevel) + printLogs(WarnLevel) + printLogs(ErrorLevel) + printLogs(PanicLevel) + + assertLine := func(log string, level Level, colored bool, verbose bool) { + regex := `^([\d.:\- ]+) \[(\S+)] (\[\d+] )?\[(\S+)] Here is (\S+) level$` + if colored { + regex = `^\x1b\[36m\[XProxy]\x1b\[0m \x1b\[90m([\d.:\- ]+)\x1b\[0m ` + + `\x1b\[\d\dm\[(\S+)]\x1b\[0m (\x1b\[34m\[\d+]\x1b\[0m )?` + + `\x1b\[35m\[(\S+)]\x1b\[0m Here is (\S+) level$` + } + matches := regexp.MustCompile(regex).FindStringSubmatch(log) + timeRegex := regexp.MustCompile(`^\d{4}(-\d\d){2} \d{2}(:\d\d){2}\.\d{3}$`) + + assert.NotEmpty(t, matches) // valid log line + assert.Regexp(t, timeRegex, matches[1]) // valid time format + assert.Equal(t, level.CapitalString(), matches[2]) // valid level string + assert.Equal(t, level.CapitalString(), matches[5]) + if !verbose { + assert.Equal(t, "logger/interface_test", matches[4]) + } else { + assert.Regexp(t, regexp.MustCompile(`^logger/interface_test.go:\d+$`), matches[4]) + } + } + + assertLogs := func(buffer string, colored bool) { + var line string + logs := strings.Split(buffer, "\n") + for _, limit := range Levels { + for _, level := range Levels { + if level >= limit { + line, logs = logs[0], logs[1:] + assertLine(line, level, colored, limit == DebugLevel) + } + } + } + assert.Equal(t, len(logs), 1) + assert.Equal(t, logs[0], "") // last line is empty + } + + assertLogs(plainBuf.String(), false) + assertLogs(coloredBuf.String(), true) + assert.Equal(t, plainBuf.String(), plainTeeBuf.String()) + assert.Equal(t, coloredBuf.String(), coloredTeeBuf.String()) + + logger.plain.writers = []zapcore.WriteSyncer{} // revert logger configure + logger.colored.writers = []zapcore.WriteSyncer{os.Stdout} + logger.level.SetLevel(InfoLevel) + logger.update() +}