diff --git a/next/logger/encoder.go b/next/logger/encoder.go index 4cacc8a..b9eda34 100644 --- a/next/logger/encoder.go +++ b/next/logger/encoder.go @@ -5,14 +5,16 @@ import ( "fmt" "github.com/gookit/color" "go.uber.org/zap/zapcore" + "path/filepath" "runtime" "strconv" + "strings" "time" ) -// getGID get goroutine ID only for debugging. +// getGid get goroutine ID only for debugging. // -> https://blog.sgmansfield.com/2015/12/goroutine-ids/ -func getGID() uint64 { +func getGid() uint64 { b := make([]byte, 64) b = b[:runtime.Stack(b, false)] b = bytes.TrimPrefix(b, []byte("goroutine ")) @@ -21,41 +23,54 @@ func getGID() uint64 { return n } +// getCaller calculate relative source path of caller. +func getCaller(ec zapcore.EntryCaller, verbose bool) string { + file, err := filepath.Rel(logHandle.path, ec.File) + if err != nil { + return "undefined" + } + if verbose { + return file + ":" + strconv.Itoa(ec.Line) + } + file, _ = strings.CutSuffix(file, ".go") // remove `.go` suffix + return file +} + // timeEncoder formats the time as a string. func timeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString(t.Format("2006-01-02 15:04:05.000")) } // timeColoredEncoder formats the time as a colored string -// with `[XProxy]` prefix. +// with custom prefix. func timeColoredEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { enc.AppendString(fmt.Sprintf( "%s %s", - color.Cyan.Render("[XProxy]"), + color.Cyan.Render(logHandle.prefix), // colored prefix color.Gray.Render(t.Format("2006-01-02 15:04:05.000")), )) } // callerEncoder formats caller in square brackets. -func callerEncoder(caller zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) { - if !handle.gid { - enc.AppendString("[" + caller.TrimmedPath() + "]") - } else { - enc.AppendString(fmt.Sprintf("[%s] [%d]", caller.TrimmedPath(), getGID())) +func callerEncoder(ec zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) { + if !logHandle.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. -func callerColoredEncoder(caller zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) { - if !handle.gid { - enc.AppendString(color.Magenta.Render("[" + caller.TrimmedPath() + "]")) +// callerColoredEncoder formats caller in square brackets with +// magenta color. +func callerColoredEncoder(ec zapcore.EntryCaller, enc zapcore.PrimitiveArrayEncoder) { + if !logHandle.verbose { + enc.AppendString(color.Magenta.Render("[" + getCaller(ec, false) + "]")) return } enc.AppendString(fmt.Sprintf( "%s %s", - color.Magenta.Render("["+caller.TrimmedPath()+"]"), - color.Blue.Render(fmt.Sprintf("[%d]", getGID())), + color.Blue.Render(fmt.Sprintf("[%d]", getGid())), + color.Magenta.Render("["+getCaller(ec, true)+"]"), )) } diff --git a/next/logger/interface.go b/next/logger/interface.go index 8e34adb..f58b811 100644 --- a/next/logger/interface.go +++ b/next/logger/interface.go @@ -13,35 +13,43 @@ const ( PanicLevel = zapcore.PanicLevel ) -func GetLevel() zapcore.Level { - return handle.level.Level() -} - -func SetLevel(level zapcore.Level) { - handle.level.SetLevel(level) -} - func Debugf(template string, args ...interface{}) { - handle.sugar.Debugf(template, args...) + logHandle.sugar.Debugf(template, args...) } func Infof(template string, args ...interface{}) { - handle.sugar.Infof(template, args...) + logHandle.sugar.Infof(template, args...) } func Warnf(template string, args ...interface{}) { - handle.sugar.Warnf(template, args...) + logHandle.sugar.Warnf(template, args...) } func Errorf(template string, args ...interface{}) { - handle.sugar.Errorf(template, args...) + logHandle.sugar.Errorf(template, args...) } func Panicf(template string, args ...interface{}) { - handle.sugar.Panicf(template, args...) + logHandle.sugar.Panicf(template, args...) +} + +// GetLevel return the current logger level. +func GetLevel() zapcore.Level { + return logHandle.level.Level() +} + +// 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) + if level == DebugLevel { + logHandle.verbose = true + } else { + logHandle.verbose = false + } } -// AddOutputs adds more plain log outputs. +// AddOutputs adds more plain output channel to the logger module. func AddOutputs(outputs ...io.Writer) { var writers []zapcore.WriteSyncer for _, output := range outputs { diff --git a/next/logger/logger.go b/next/logger/logger.go index f57fbff..8e792a5 100644 --- a/next/logger/logger.go +++ b/next/logger/logger.go @@ -4,18 +4,22 @@ import ( "go.uber.org/zap" "go.uber.org/zap/zapcore" "os" + "path" + "runtime" ) -type Logger struct { - gid bool +type logger struct { logger *zap.Logger level *zap.AtomicLevel sugar *zap.SugaredLogger writers []zapcore.WriteSyncer - stdCore zapcore.Core + 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 handle Logger +var logHandle *logger // singleton logger handle // logConfig generates log config for XProxy. func logConfig(colored bool) zapcore.EncoderConfig { @@ -38,33 +42,37 @@ func logConfig(colored bool) zapcore.EncoderConfig { } func init() { - level := zap.NewAtomicLevelAt(DebugLevel) - core := zapcore.NewCore( - zapcore.NewConsoleEncoder(logConfig(true)), - zapcore.Lock(os.Stderr), level, + zapLevel := zap.NewAtomicLevelAt(InfoLevel) // using info level in default + zapCore := zapcore.NewCore( + zapcore.NewConsoleEncoder(logConfig(true)), // colorful output + zapcore.Lock(os.Stderr), + zapLevel, ) - logger := zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1)) - handle = Logger{ - gid: true, - logger: logger, - level: &level, - sugar: logger.Sugar(), + 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{}, - stdCore: core, + path: path.Join(path.Dir(src), "../"), + prefix: "[XProxy]", + verbose: false, } } // addWrites adds more plain log writers. func addWrites(writers ...zapcore.WriteSyncer) { - handle.writers = append(handle.writers, writers...) + logHandle.writers = append(logHandle.writers, writers...) plainCore := zapcore.NewCore( - zapcore.NewConsoleEncoder(logConfig(false)), - zap.CombineWriteSyncers(handle.writers...), - handle.level, + zapcore.NewConsoleEncoder(logConfig(false)), // without colored + zap.CombineWriteSyncers(logHandle.writers...), + logHandle.level, ) - handle.logger = zap.New( - zapcore.NewTee(handle.stdCore, plainCore), + logHandle.logger = zap.New( + zapcore.NewTee(logHandle.stderr, plainCore), zap.AddCaller(), zap.AddCallerSkip(1), ) - handle.sugar = handle.logger.Sugar() + logHandle.sugar = logHandle.logger.Sugar() } diff --git a/next/main.go b/next/main.go index 4640261..4083a99 100644 --- a/next/main.go +++ b/next/main.go @@ -6,7 +6,6 @@ import ( ) func main() { - //logger.Debugf("here is %s level", "debug") //logger.Infof("here is %s level", "info") //logger.Warnf("here is %s level", "warn") @@ -30,4 +29,8 @@ func main() { logger.Debugf("output msg 3 at debug") logger.Infof("output msg 3 at info") logger.Warnf("output msg 3 at warn") + logger.SetLevel(logger.DebugLevel) + logger.Debugf("output msg 4 at debug") + logger.Infof("output msg 4 at info") + logger.Warnf("output msg 4 at warn") }