forked from TrueCloudLab/frostfs-s3-gw
[#702] Reload config level on SIGHUP
Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
parent
be4600988e
commit
63275f7876
3 changed files with 135 additions and 64 deletions
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue