[#702] Reload config level on SIGHUP

Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
Denis Kirillov 2022-09-12 12:45:54 +03:00 committed by Alex Vanin
parent be4600988e
commit 63275f7876
3 changed files with 135 additions and 64 deletions

View file

@ -6,7 +6,10 @@ import (
"fmt" "fmt"
"net" "net"
"net/http" "net/http"
"os"
"os/signal"
"strconv" "strconv"
"syscall"
"time" "time"
"github.com/gorilla/mux" "github.com/gorilla/mux"
@ -42,6 +45,17 @@ type (
webDone chan struct{} webDone chan struct{}
wrkDone chan struct{} wrkDone chan struct{}
settings *appSettings
}
appSettings struct {
LogLevel zap.AtomicLevel
}
Logger struct {
logger *zap.Logger
lvl zap.AtomicLevel
} }
tlsConfig struct { tlsConfig struct {
@ -54,7 +68,9 @@ type (
} }
) )
func newApp(ctx context.Context, l *zap.Logger, v *viper.Viper) *App { func newApp(ctx context.Context, log *Logger, v *viper.Viper) *App {
l := log.logger
var ( var (
key *keys.PrivateKey key *keys.PrivateKey
err error err error
@ -245,8 +261,8 @@ func (a *App) Wait() {
a.log.Info("application finished") a.log.Info("application finished")
} }
// Server runs HTTP server to handle S3 API requests. // Serve runs HTTP server to handle S3 API requests.
func (a *App) Server(ctx context.Context) { func (a *App) Serve(ctx context.Context) {
var ( var (
err error err error
lis net.Listener lis net.Listener
@ -299,7 +315,18 @@ func (a *App) Server(ctx context.Context) {
} }
}() }()
<-ctx.Done() sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGHUP)
LOOP:
for {
select {
case <-ctx.Done():
break LOOP
case <-sigs:
a.configReload()
}
}
ctx, cancel := context.WithTimeout(context.Background(), defaultShutdownTimeout) ctx, cancel := context.WithTimeout(context.Background(), defaultShutdownTimeout)
defer cancel() defer cancel()
@ -312,6 +339,31 @@ func (a *App) Server(ctx context.Context) {
close(a.webDone) close(a.webDone)
} }
func (a *App) configReload() {
a.log.Info("SIGHUP config reload started")
if !a.cfg.IsSet(cmdConfig) {
a.log.Warn("failed to reload config because it's missed")
return
}
if err := readConfig(a.cfg); err != nil {
a.log.Warn("failed to reload config", zap.Error(err))
return
}
a.updateSettings()
a.log.Info("SIGHUP config reload completed")
}
func (a *App) updateSettings() {
if lvl, err := getLogLevel(a.cfg); err != nil {
a.log.Warn("log level won't be updated", zap.Error(err))
} else {
a.settings.LogLevel.SetLevel(lvl)
}
}
func getNotificationsOptions(v *viper.Viper, l *zap.Logger) *notifications.Options { func getNotificationsOptions(v *viper.Viper, l *zap.Logger) *notifications.Options {
cfg := notifications.Options{} cfg := notifications.Options{}
cfg.URL = v.GetString(cfgNATSEndpoint) cfg.URL = v.GetString(cfgNATSEndpoint)

View file

@ -15,6 +15,7 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
"github.com/spf13/viper" "github.com/spf13/viper"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore"
) )
const ( const (
@ -183,7 +184,7 @@ func newSettings() *viper.Viper {
flags.StringP(cmdWallet, "w", "", `path to the wallet`) flags.StringP(cmdWallet, "w", "", `path to the wallet`)
flags.String(cmdAddress, "", `address of wallet account`) flags.String(cmdAddress, "", `address of wallet account`)
config := flags.String(cmdConfig, "", "config path") flags.String(cmdConfig, "", "config path")
flags.Duration(cfgHealthcheckTimeout, defaultHealthcheckTimeout, "set timeout to check node health during rebalance") flags.Duration(cfgHealthcheckTimeout, defaultHealthcheckTimeout, "set timeout to check node health during rebalance")
flags.Duration(cfgConnectTimeout, defaultConnectTimeout, "set timeout to connect to NeoFS nodes") flags.Duration(cfgConnectTimeout, defaultConnectTimeout, "set timeout to connect to NeoFS nodes")
@ -291,12 +292,79 @@ func newSettings() *viper.Viper {
} }
if v.IsSet(cmdConfig) { if v.IsSet(cmdConfig) {
if cfgFile, err := os.Open(*config); err != nil { if err := readConfig(v); err != nil {
panic(err)
} else if err := v.ReadConfig(cfgFile); err != nil {
panic(err) panic(err)
} }
} }
return v return v
} }
func readConfig(v *viper.Viper) error {
cfgFileName := v.GetString(cmdConfig)
cfgFile, err := os.Open(cfgFileName)
if err != nil {
return err
}
if err = v.ReadConfig(cfgFile); err != nil {
return err
}
return cfgFile.Close()
}
// newLogger constructs a Logger instance for the current application.
// Panics on failure.
//
// Logger contains a logger is built from zap's production logging configuration with:
// - parameterized level (debug by default)
// - console encoding
// - ISO8601 time encoding
//
// and atomic log level to dynamically change it.
//
// Logger records a stack trace for all messages at or above fatal level.
//
// See also zapcore.Level, zap.NewProductionConfig, zap.AddStacktrace.
func newLogger(v *viper.Viper) *Logger {
lvl, err := getLogLevel(v)
if err != nil {
panic(err)
}
c := zap.NewProductionConfig()
c.Level = zap.NewAtomicLevelAt(lvl)
c.Encoding = "console"
c.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
l, err := c.Build(
zap.AddStacktrace(zap.NewAtomicLevelAt(zap.FatalLevel)),
)
if err != nil {
panic(fmt.Sprintf("build zap logger instance: %v", err))
}
return &Logger{
logger: l,
lvl: c.Level,
}
}
func getLogLevel(v *viper.Viper) (zapcore.Level, error) {
var lvl zapcore.Level
lvlStr := v.GetString(cfgLoggerLevel)
err := lvl.UnmarshalText([]byte(lvlStr))
if err != nil {
return lvl, fmt.Errorf("incorrect logger level configuration %s (%v), "+
"value should be one of %v", lvlStr, err, [...]zapcore.Level{
zapcore.DebugLevel,
zapcore.InfoLevel,
zapcore.WarnLevel,
zapcore.ErrorLevel,
zapcore.DPanicLevel,
zapcore.PanicLevel,
zapcore.FatalLevel,
})
}
return lvl, nil
}

View file

@ -2,68 +2,19 @@ package main
import ( import (
"context" "context"
"fmt"
"os/signal" "os/signal"
"syscall" "syscall"
"github.com/spf13/viper"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
) )
// newLogger constructs a zap.Logger instance for the current application.
// Panics on failure.
//
// Logger is built from zap's production logging configuration with:
// - parameterized level (debug by default)
// - console encoding
// - ISO8601 time encoding
//
// Logger records a stack trace for all messages at or above fatal level.
//
// See also zapcore.Level, zap.NewProductionConfig, zap.AddStacktrace.
func newLogger(v *viper.Viper) *zap.Logger {
var lvl zapcore.Level
lvlStr := v.GetString(cfgLoggerLevel)
err := lvl.UnmarshalText([]byte(lvlStr))
if err != nil {
panic(fmt.Sprintf("incorrect logger level configuration %s (%v), "+
"value should be one of %v", lvlStr, err, [...]zapcore.Level{
zapcore.DebugLevel,
zapcore.InfoLevel,
zapcore.WarnLevel,
zapcore.ErrorLevel,
zapcore.DPanicLevel,
zapcore.PanicLevel,
zapcore.FatalLevel,
}))
}
c := zap.NewProductionConfig()
c.Level = zap.NewAtomicLevelAt(lvl)
c.Encoding = "console"
c.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
l, err := c.Build(
zap.AddStacktrace(zap.NewAtomicLevelAt(zap.FatalLevel)),
)
if err != nil {
panic(fmt.Sprintf("build zap logger instance: %v", err))
}
return l
}
func main() { func main() {
var ( g, _ := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
v = newSettings()
l = newLogger(v)
g, _ = signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
a = newApp(g, l, v)
)
go a.Server(g) v := newSettings()
l := newLogger(v)
a := newApp(g, l, v)
go a.Serve(g)
a.Wait() a.Wait()
} }