From 26a802b85c0d9c13e3f61c9c241f4f838024ae6b Mon Sep 17 00:00:00 2001 From: engigu Date: Sat, 7 Feb 2026 21:54:54 +0800 Subject: [PATCH] feat: logger change to zap --- agent/logger.go | 72 ++++++++++++++------ go.mod | 6 +- go.sum | 10 +-- internal/logger/logger.go | 136 +++++++++++++++++++++++--------------- 4 files changed, 144 insertions(+), 80 deletions(-) diff --git a/agent/logger.go b/agent/logger.go index 8b51a95..9f315f7 100644 --- a/agent/logger.go +++ b/agent/logger.go @@ -2,20 +2,18 @@ package main import ( "fmt" - "io" "os" "path/filepath" "strings" - "github.com/sirupsen/logrus" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" "gopkg.in/natefinch/lumberjack.v2" ) // 日志实例 -var log = logrus.New() - -// CustomFormatter 自定义日志格式 -type CustomFormatter struct{} +var loggerInstance *zap.Logger +var log *zap.SugaredLogger // ANSI 颜色代码 const ( @@ -26,26 +24,52 @@ const ( colorGray = "\033[37m" ) -func (f *CustomFormatter) Format(entry *logrus.Entry) ([]byte, error) { - timestamp := entry.Time.Format("2006-01-02 15:04:05") - level := strings.ToUpper(entry.Level.String()) +// customCore 实现 zapcore.Core 以提供与 logrus 一模一样的格式 +type customCore struct { + level zapcore.LevelEnabler + writer zapcore.WriteSyncer +} + +func (c *customCore) Enabled(l zapcore.Level) bool { + return c.level.Enabled(l) +} + +func (c *customCore) With(fields []zapcore.Field) zapcore.Core { + return c +} + +func (c *customCore) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry { + if c.Enabled(ent.Level) { + return ce.AddCore(ent, c) + } + return ce +} + +func (c *customCore) Write(ent zapcore.Entry, fields []zapcore.Field) error { + timestamp := ent.Time.Format("2006-01-02 15:04:05") + level := strings.ToUpper(ent.Level.String()) var levelColor string - switch entry.Level { - case logrus.DebugLevel, logrus.TraceLevel: + switch ent.Level { + case zapcore.DebugLevel: levelColor = colorGray - case logrus.InfoLevel: + case zapcore.InfoLevel: levelColor = colorBlue - case logrus.WarnLevel: + case zapcore.WarnLevel: levelColor = colorYellow - case logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel: + case zapcore.ErrorLevel, zapcore.DPanicLevel, zapcore.PanicLevel, zapcore.FatalLevel: levelColor = colorRed default: levelColor = colorBlue } - msg := fmt.Sprintf("[%s]%s[%s]%s %s\n", timestamp, levelColor, level, colorReset, entry.Message) - return []byte(msg), nil + msg := fmt.Sprintf("[%s]%s[%s]%s %s\n", timestamp, levelColor, level, colorReset, ent.Message) + _, err := c.writer.Write([]byte(msg)) + return err +} + +func (c *customCore) Sync() error { + return c.writer.Sync() } func initLogger(logFile string, fileOnly bool) { @@ -54,9 +78,6 @@ func initLogger(logFile string, fileOnly bool) { os.MkdirAll(logDir, 0755) } - log.SetFormatter(&CustomFormatter{}) - log.SetLevel(logrus.InfoLevel) - lumberjackLogger := &lumberjack.Logger{ Filename: logFile, MaxSize: 5, @@ -65,11 +86,20 @@ func initLogger(logFile string, fileOnly bool) { Compress: false, } + var output zapcore.WriteSyncer // fileOnly 模式下只输出到文件(daemon 模式或重启模式) if fileOnly { - log.SetOutput(lumberjackLogger) + output = zapcore.AddSync(lumberjackLogger) } else { // 前台运行时同时输出到终端和文件 - log.SetOutput(io.MultiWriter(os.Stdout, lumberjackLogger)) + output = zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(lumberjackLogger)) } + + core := &customCore{ + level: zap.NewAtomicLevelAt(zap.InfoLevel), + writer: output, + } + + loggerInstance = zap.New(core) + log = loggerInstance.Sugar() } diff --git a/go.mod b/go.mod index aa574af..a593d4e 100644 --- a/go.mod +++ b/go.mod @@ -12,9 +12,10 @@ require ( github.com/gorilla/websocket v1.5.3 github.com/robfig/cron/v3 v3.0.1 github.com/shirou/gopsutil/v3 v3.24.5 - github.com/sirupsen/logrus v1.9.3 + go.uber.org/zap v1.27.1 golang.org/x/text v0.32.0 gopkg.in/ini.v1 v1.67.0 + gopkg.in/natefinch/lumberjack.v2 v2.2.1 gorm.io/driver/mysql v1.6.0 gorm.io/driver/postgres v1.6.0 gorm.io/gorm v1.31.1 @@ -43,7 +44,6 @@ require ( github.com/jinzhu/now v1.1.5 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect - github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mattn/go-isatty v0.0.19 // indirect @@ -59,13 +59,13 @@ require ( github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect + go.uber.org/multierr v1.10.0 // indirect golang.org/x/arch v0.3.0 // indirect golang.org/x/crypto v0.46.0 // indirect golang.org/x/net v0.47.0 // indirect golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.39.0 // indirect google.golang.org/protobuf v1.30.0 // indirect - gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect modernc.org/libc v1.22.5 // indirect modernc.org/mathutil v1.5.0 // indirect diff --git a/go.sum b/go.sum index c6a97af..49e765e 100644 --- a/go.sum +++ b/go.sum @@ -6,7 +6,6 @@ github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZX github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -103,8 +102,6 @@ github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFt github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= -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= @@ -127,6 +124,12 @@ github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4d github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= @@ -139,7 +142,6 @@ golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/internal/logger/logger.go b/internal/logger/logger.go index 7166760..dcdc72d 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -7,57 +7,84 @@ import ( "strings" "time" - "github.com/sirupsen/logrus" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" ) -var Log *logrus.Logger +var Log *zap.Logger +var Sugar *zap.SugaredLogger +var atomicLevel zap.AtomicLevel -// CustomFormatter 自定义日志格式 -type CustomFormatter struct{} - -// ANSI 颜色代码 (参考 logrus 默认配色) +// ANSI 颜色代码 const ( colorReset = "\033[0m" colorRed = "\033[31m" // error, fatal, panic colorYellow = "\033[33m" // warn - colorBlue = "\033[36m" // info (logrus 默认用 cyan) - colorGray = "\033[37m" // debug (logrus 默认用 white) + colorBlue = "\033[36m" // info + colorGray = "\033[37m" // debug ) -func (f *CustomFormatter) Format(entry *logrus.Entry) ([]byte, error) { - timestamp := entry.Time.Format("2006-01-02 15:04:05") - level := strings.ToUpper(entry.Level.String()) +// customCore 实现 zapcore.Core 以提供与 logrus 一模一样的格式 +type customCore struct { + level zapcore.LevelEnabler + writer zapcore.WriteSyncer +} + +func (c *customCore) Enabled(l zapcore.Level) bool { + return c.level.Enabled(l) +} + +func (c *customCore) With(fields []zapcore.Field) zapcore.Core { + // 目前忽略字段,以保持与旧 logrus 格式一模一样(旧格式只输出 entry.Message) + return c +} + +func (c *customCore) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry { + if c.Enabled(ent.Level) { + return ce.AddCore(ent, c) + } + return ce +} + +func (c *customCore) Write(ent zapcore.Entry, fields []zapcore.Field) error { + timestamp := ent.Time.Format("2006-01-02 15:04:05") + level := strings.ToUpper(ent.Level.String()) - // 根据日志级别设置颜色 (参考 logrus TextFormatter 默认配色) var levelColor string - switch entry.Level { - case logrus.DebugLevel, logrus.TraceLevel: + switch ent.Level { + case zapcore.DebugLevel: levelColor = colorGray - case logrus.InfoLevel: + case zapcore.InfoLevel: levelColor = colorBlue - case logrus.WarnLevel: + case zapcore.WarnLevel: levelColor = colorYellow - case logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel: + case zapcore.ErrorLevel, zapcore.DPanicLevel, zapcore.PanicLevel, zapcore.FatalLevel: levelColor = colorRed default: levelColor = colorBlue } - msg := fmt.Sprintf("[%s]%s[%s]%s %s\n", timestamp, levelColor, level, colorReset, entry.Message) - return []byte(msg), nil + msg := fmt.Sprintf("[%s]%s[%s]%s %s\n", timestamp, levelColor, level, colorReset, ent.Message) + _, err := c.writer.Write([]byte(msg)) + return err +} + +func (c *customCore) Sync() error { + return c.writer.Sync() +} + +func newLogger(output zapcore.WriteSyncer) *zap.Logger { + core := &customCore{ + level: atomicLevel, + writer: output, + } + return zap.New(core) } func init() { - Log = logrus.New() - - // 设置自定义日志格式 - Log.SetFormatter(&CustomFormatter{}) - - // 设置日志级别 - Log.SetLevel(logrus.InfoLevel) - - // 输出到标准输出 - Log.SetOutput(os.Stdout) + atomicLevel = zap.NewAtomicLevelAt(zap.InfoLevel) + Log = newLogger(zapcore.AddSync(os.Stdout)) + Sugar = Log.Sugar() } // SetupFileOutput 设置文件输出 @@ -72,7 +99,8 @@ func SetupFileOutput(logDir string) error { return err } - Log.SetOutput(file) + Log = newLogger(zapcore.AddSync(file)) + Sugar = Log.Sugar() return nil } @@ -80,52 +108,56 @@ func SetupFileOutput(logDir string) error { func SetLevel(level string) { switch level { case "debug": - Log.SetLevel(logrus.DebugLevel) + atomicLevel.SetLevel(zap.DebugLevel) case "info": - Log.SetLevel(logrus.InfoLevel) + atomicLevel.SetLevel(zap.InfoLevel) case "warn": - Log.SetLevel(logrus.WarnLevel) + atomicLevel.SetLevel(zap.WarnLevel) case "error": - Log.SetLevel(logrus.ErrorLevel) + atomicLevel.SetLevel(zap.ErrorLevel) default: - Log.SetLevel(logrus.InfoLevel) + atomicLevel.SetLevel(zap.InfoLevel) } } // 便捷方法 -func Debug(args ...interface{}) { Log.Debug(args...) } -func Info(args ...interface{}) { Log.Info(args...) } -func Warn(args ...interface{}) { Log.Warn(args...) } -func Error(args ...interface{}) { Log.Error(args...) } -func Fatal(args ...interface{}) { Log.Fatal(args...) } +func Debug(args ...interface{}) { Sugar.Debug(args...) } +func Info(args ...interface{}) { Sugar.Info(args...) } +func Warn(args ...interface{}) { Sugar.Warn(args...) } +func Error(args ...interface{}) { Sugar.Error(args...) } +func Fatal(args ...interface{}) { Sugar.Fatal(args...) } -func Debugf(format string, args ...interface{}) { Log.Debugf(format, args...) } -func Infof(format string, args ...interface{}) { Log.Infof(format, args...) } -func Warnf(format string, args ...interface{}) { Log.Warnf(format, args...) } -func Errorf(format string, args ...interface{}) { Log.Errorf(format, args...) } -func Fatalf(format string, args ...interface{}) { Log.Fatalf(format, args...) } +func Debugf(format string, args ...interface{}) { Sugar.Debugf(format, args...) } +func Infof(format string, args ...interface{}) { Sugar.Infof(format, args...) } +func Warnf(format string, args ...interface{}) { Sugar.Warnf(format, args...) } +func Errorf(format string, args ...interface{}) { Sugar.Errorf(format, args...) } +func Fatalf(format string, args ...interface{}) { Sugar.Fatalf(format, args...) } // WithField 带字段的日志 -func WithField(key string, value interface{}) *logrus.Entry { - return Log.WithField(key, value) +func WithField(key string, value interface{}) *zap.SugaredLogger { + return Sugar.With(key, value) } // WithFields 带多个字段的日志 -func WithFields(fields logrus.Fields) *logrus.Entry { - return Log.WithFields(fields) +func WithFields(fields map[string]interface{}) *zap.SugaredLogger { + f := make([]interface{}, 0, len(fields)*2) + for k, v := range fields { + f = append(f, k, v) + } + return Sugar.With(f...) } // SchedulerLogger 兼容 internal/executor 的日志接口 type SchedulerLogger struct{} func (s *SchedulerLogger) Infof(format string, args ...interface{}) { - Log.Infof(format, args...) + Sugar.Infof(format, args...) } func (s *SchedulerLogger) Warnf(format string, args ...interface{}) { - Log.Warnf(format, args...) + Sugar.Warnf(format, args...) } func (s *SchedulerLogger) Errorf(format string, args ...interface{}) { - Log.Errorf(format, args...) + Sugar.Errorf(format, args...) } // NewSchedulerLogger 创建一个兼容 executor.SchedulerLogger 的实例