This is an automated email from the ASF dual-hosted git repository. hulk pushed a commit to branch unstable in repository https://gitbox.apache.org/repos/asf/kvrocks-controller.git
The following commit(s) were added to refs/heads/unstable by this push: new 817a884 Allow to send logs to the file with the rotation (#270) 817a884 is described below commit 817a884ca679f1a8f0e6c73fbfc5f474ecf25480 Author: Raphael <greatsh...@hotmail.com> AuthorDate: Sun Mar 2 22:55:15 2025 +0800 Allow to send logs to the file with the rotation (#270) --- cmd/server/main.go | 11 ++++++++ config/config-consul.yaml | 11 +++++++- config/config-raft.yaml | 9 ++++++ config/config-zk.yaml | 11 +++++++- config/config.go | 10 +++++++ config/config.yaml | 11 +++++++- go.mod | 3 ++ go.sum | 4 +++ logger/logger.go | 70 +++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 137 insertions(+), 3 deletions(-) diff --git a/cmd/server/main.go b/cmd/server/main.go index 614f184..080aae4 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -65,6 +65,8 @@ func handleSignals(sig os.Signal) (exitNow bool) { } func main() { + defer logger.Sync() + ctx, cancelFn := context.WithCancel(context.Background()) // os signal handler shutdownCh := make(chan struct{}) @@ -92,6 +94,15 @@ func main() { logger.Get().With(zap.Error(err)).Error("Failed to validate the config file") return } + + if cfg.Log != nil && cfg.Log.Filename != "" { + logger.Get().Info("Logs will be saved to " + cfg.Log.Filename) + if err := logger.InitLoggerRotate(cfg.Log.Level, cfg.Log.Filename, cfg.Log.MaxBackups, cfg.Log.MaxAge, cfg.Log.MaxSize, cfg.Log.Compress); err != nil { + logger.Get().With(zap.Error(err)).Error("Failed to init the log rotate") + return + } + } + srv, err := server.NewServer(cfg) if err != nil { logger.Get().With(zap.Error(err)).Error("Failed to create the server") diff --git a/config/config-consul.yaml b/config/config-consul.yaml index 3315721..b457cc5 100644 --- a/config/config-consul.yaml +++ b/config/config-consul.yaml @@ -39,4 +39,13 @@ consul: controller: failover: ping_interval_seconds: 3 - min_alive_size: 5 \ No newline at end of file + min_alive_size: 5 + +# Uncomment this part to save logs to filename instead of stdout +#log: +# level: info +# filename: /data/logs/kvctl.log +# max_backups: 10 +# max_age: 7 +# max_size: 100 +# compress: false \ No newline at end of file diff --git a/config/config-raft.yaml b/config/config-raft.yaml index 0ba300f..3f1bb34 100644 --- a/config/config-raft.yaml +++ b/config/config-raft.yaml @@ -42,3 +42,12 @@ controller: failover: ping_interval_seconds: 3 min_alive_size: 5 + +# Uncomment this part to save logs to filename instead of stdout +#log: +# level: info +# filename: /data/logs/kvctl.log +# max_backups: 10 +# max_age: 7 +# max_size: 100 +# compress: false \ No newline at end of file diff --git a/config/config-zk.yaml b/config/config-zk.yaml index ee54655..1870b23 100644 --- a/config/config-zk.yaml +++ b/config/config-zk.yaml @@ -36,4 +36,13 @@ zookeeper: controller: failover: ping_interval_seconds: 3 - min_alive_size: 5 \ No newline at end of file + min_alive_size: 5 + +# Uncomment this part to save logs to filename instead of stdout +#log: +# level: info +# filename: /data/logs/kvctl.log +# max_backups: 10 +# max_age: 7 +# max_size: 100 +# compress: false \ No newline at end of file diff --git a/config/config.go b/config/config.go index 6c80312..69558fb 100644 --- a/config/config.go +++ b/config/config.go @@ -49,6 +49,15 @@ type ControllerConfig struct { FailOver *FailOverConfig `yaml:"failover"` } +type LogConfig struct { + Level string `yaml:"level"` + Filename string `yaml:"filename"` + MaxBackups int `yaml:"max_backups"` + MaxAge int `yaml:"max_age"` + MaxSize int `yaml:"max_size"` + Compress bool `yaml:"compress"` +} + const defaultPort = 9379 type Config struct { @@ -60,6 +69,7 @@ type Config struct { Consul *consul.Config `yaml:"consul"` Admin AdminConfig `yaml:"admin"` Controller *ControllerConfig `yaml:"controller"` + Log *LogConfig `yaml:"log"` } func DefaultFailOverConfig() *FailOverConfig { diff --git a/config/config.yaml b/config/config.yaml index 3877ecf..c3cfb0b 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -41,4 +41,13 @@ etcd: controller: failover: ping_interval_seconds: 3 - min_alive_size: 5 \ No newline at end of file + min_alive_size: 5 + +# Uncomment this part to save logs to filename instead of stdout +#log: +# level: info +# filename: /data/logs/kvctl.log +# max_backups: 10 +# max_age: 7 +# max_size: 100 +# compress: false \ No newline at end of file diff --git a/go.mod b/go.mod index fb5d32c..b45115f 100644 --- a/go.mod +++ b/go.mod @@ -22,9 +22,12 @@ require ( go.etcd.io/etcd/raft/v3 v3.5.18 go.etcd.io/etcd/server/v3 v3.5.18 go.uber.org/zap v1.27.0 + gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0 ) +require github.com/BurntSushi/toml v1.4.0 // indirect + require ( github.com/armon/go-metrics v0.4.1 // indirect github.com/beorn7/perks v1.0.1 // indirect diff --git a/go.sum b/go.sum index add17ec..6023ada 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,6 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -423,6 +425,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/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/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0 h1:POO/ycCATvegFmVuPpQzZFJ+pGZeX22Ufu6fibxDVjU= diff --git a/logger/logger.go b/logger/logger.go index 9bd844f..437dfa6 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -20,8 +20,11 @@ package logger import ( + "fmt" + "go.uber.org/zap" "go.uber.org/zap/zapcore" + "gopkg.in/natefinch/lumberjack.v2" ) var zapLogger *zap.Logger @@ -36,3 +39,70 @@ func init() { zapConfig.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder zapLogger, _ = zapConfig.Build() } + +func getEncoder() zapcore.Encoder { + encoderConfig := zap.NewProductionEncoderConfig() + encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder + encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder + encoderConfig.TimeKey = "time" + + return zapcore.NewJSONEncoder(encoderConfig) +} + +func getWriteSyncer(filename string, maxBackups, maxAge, maxSize int, compress bool) (zapcore.WriteSyncer, error) { + lumberJackLogger := &lumberjack.Logger{ + Filename: filename, + MaxBackups: maxBackups, + MaxSize: maxSize, + MaxAge: maxAge, + Compress: compress, + } + + if _, err := lumberJackLogger.Write([]byte("test logfile writable\r\n")); err != nil { + return nil, fmt.Errorf("test writing to log file %s failed", filename) + } + + return zapcore.AddSync(lumberJackLogger), nil +} + +func InitLoggerRotate(level, filename string, maxBackups, maxAge, maxSize int, compress bool) error { + // if file path is empty, use default zapLogger, print in console + if len(filename) == 0 { + return nil + } + if level != "info" && level != "warn" && level != "error" { + return fmt.Errorf("log level must be one of info,warn,error") + } + if maxBackups > 100 || maxBackups < 10 { + return fmt.Errorf("log max_backups must be between 10 and 100") + } + if maxAge > 30 || maxAge < 1 { + return fmt.Errorf("log max_age must be between 1 and 30") + } + if maxSize > 500 || maxSize < 100 { + return fmt.Errorf("log max_size must be between 100 and 500") + } + + var l = new(zapcore.Level) + if err := l.UnmarshalText([]byte(level)); err != nil { + return err + } + encoder := getEncoder() + writeSync, err := getWriteSyncer(filename, maxBackups, maxAge, maxSize, compress) + if err != nil { + return err + } + core := zapcore.NewCore(encoder, writeSync, l) + rotateLogger := zap.New(core, zap.AddCaller()) + zapLogger = rotateLogger + + return nil +} + +func Sync() { + if zapLogger != nil { + if err := zapLogger.Sync(); err != nil { + return + } + } +}