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
+               }
+       }
+}

Reply via email to