[#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"
"net"
"net/http"
"os"
"os/signal"
"strconv"
"syscall"
"time"
"github.com/gorilla/mux"
@ -42,6 +45,17 @@ type (
webDone chan struct{}
wrkDone chan struct{}
settings *appSettings
}
appSettings struct {
LogLevel zap.AtomicLevel
}
Logger struct {
logger *zap.Logger
lvl zap.AtomicLevel
}
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 (
key *keys.PrivateKey
err error
@ -245,8 +261,8 @@ func (a *App) Wait() {
a.log.Info("application finished")
}
// Server runs HTTP server to handle S3 API requests.
func (a *App) Server(ctx context.Context) {
// Serve runs HTTP server to handle S3 API requests.
func (a *App) Serve(ctx context.Context) {
var (
err error
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)
defer cancel()
@ -312,6 +339,31 @@ func (a *App) Server(ctx context.Context) {
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 {
cfg := notifications.Options{}
cfg.URL = v.GetString(cfgNATSEndpoint)

View file

@ -15,6 +15,7 @@ import (
"github.com/spf13/pflag"
"github.com/spf13/viper"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
const (
@ -183,7 +184,7 @@ func newSettings() *viper.Viper {
flags.StringP(cmdWallet, "w", "", `path to the wallet`)
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(cfgConnectTimeout, defaultConnectTimeout, "set timeout to connect to NeoFS nodes")
@ -291,12 +292,79 @@ func newSettings() *viper.Viper {
}
if v.IsSet(cmdConfig) {
if cfgFile, err := os.Open(*config); err != nil {
panic(err)
} else if err := v.ReadConfig(cfgFile); err != nil {
if err := readConfig(v); err != nil {
panic(err)
}
}
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 (
"context"
"fmt"
"os/signal"
"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() {
var (
v = newSettings()
l = newLogger(v)
g, _ = signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
a = newApp(g, l, v)
)
g, _ := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
go a.Server(g)
v := newSettings()
l := newLogger(v)
a := newApp(g, l, v)
go a.Serve(g)
a.Wait()
}