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")