From d7c7022bbd072e2375f98e4a8f83bf01a550d4d4 Mon Sep 17 00:00:00 2001 From: Pavel Karpy Date: Wed, 28 Sep 2022 10:59:23 +0300 Subject: [PATCH] [#1770] logger: Support runtime level reloading Signed-off-by: Pavel Karpy --- cmd/neofs-node/config.go | 2 +- pkg/util/logger/logger.go | 71 ++++++++++++++++++++++++++++++++------- 2 files changed, 59 insertions(+), 14 deletions(-) diff --git a/cmd/neofs-node/config.go b/cmd/neofs-node/config.go index 74941a7f5..38255bf50 100644 --- a/cmd/neofs-node/config.go +++ b/cmd/neofs-node/config.go @@ -492,7 +492,7 @@ func initCfg(appCfg *config.Config) *cfg { ) fatalOnErr(err) - log, err := logger.NewLogger(logPrm) + log, err := logger.NewLogger(&logPrm) fatalOnErr(err) var netAddr network.AddressGroup diff --git a/pkg/util/logger/logger.go b/pkg/util/logger/logger.go index e6afb58d8..4a536368a 100644 --- a/pkg/util/logger/logger.go +++ b/pkg/util/logger/logger.go @@ -1,8 +1,6 @@ package logger import ( - "errors" - "go.uber.org/zap" "go.uber.org/zap/zapcore" ) @@ -15,23 +13,58 @@ type Logger struct { } // Prm groups Logger's parameters. +// Successful passing non-nil parameters to the NewLogger (if returned +// error is nil) connects the parameters with the returned Logger. +// Parameters that have been connected to the Logger support its +// configuration changing. +// +// Passing Prm after a successful connection via the NewLogger, connects +// the Prm to a new instance of the Logger. +// +// See also Reload, SetLevelString. type Prm struct { + // link to the created Logger + // instance; used for a runtime + // reconfiguration + _log *Logger + + // support runtime rereading level zapcore.Level + + // do not support runtime rereading } -// ErrNilLogger is returned by functions that -// expect a non-nil Logger but received nil. -var ErrNilLogger = errors.New("logger is nil") - -// SetLevelString sets the minimum logging level. +// SetLevelString sets the minimum logging level. Default is +// "info". // -// Returns error of s is not a string representation of zap.Level -// value (see zapcore.Level docs). +// Returns an error if s is not a string representation of a +// supporting logging level. +// +// Supports runtime rereading. func (p *Prm) SetLevelString(s string) error { return p.level.UnmarshalText([]byte(s)) } -// NewLogger constructs a new zap logger instance. +// Reload reloads configuration of a connected instance of the Logger. +// Returns ErrLoggerNotConnected if no connection has been performed. +// Returns any reconfiguration error from the Logger directly. +func (p Prm) Reload() error { + if p._log == nil { + // incorrect logger usage + panic("parameters are not connected to any Logger") + } + + return p._log.reload(p) +} + +func defaultPrm() *Prm { + return new(Prm) +} + +// NewLogger constructs a new zap logger instance. Constructing with nil +// parameters is safe: default values will be used then. +// Passing non-nil parameters after a successful creation (non-error) allows +// runtime reconfiguration. // // Logger is built from production logging configuration with: // - parameterized level; @@ -39,7 +72,11 @@ func (p *Prm) SetLevelString(s string) error { // - ISO8601 time encoding. // // Logger records a stack trace for all messages at or above fatal level. -func NewLogger(prm Prm) (*Logger, error) { +func NewLogger(prm *Prm) (*Logger, error) { + if prm == nil { + prm = defaultPrm() + } + lvl := zap.NewAtomicLevelAt(prm.level) c := zap.NewProductionConfig() @@ -47,12 +84,20 @@ func NewLogger(prm Prm) (*Logger, error) { c.Encoding = "console" c.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder - l, err := c.Build( + lZap, err := c.Build( zap.AddStacktrace(zap.NewAtomicLevelAt(zap.FatalLevel)), ) if err != nil { return nil, err } - return &Logger{Logger: l, lvl: lvl}, nil + l := &Logger{Logger: lZap, lvl: lvl} + prm._log = l + + return l, nil +} + +func (l *Logger) reload(prm Prm) error { + l.lvl.SetLevel(prm.level) + return nil }