[#1770] logger: Support runtime level reloading

Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
This commit is contained in:
Pavel Karpy 2022-09-28 10:59:23 +03:00 committed by Pavel Karpy
parent f037022a7a
commit d7c7022bbd
2 changed files with 59 additions and 14 deletions

View file

@ -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

View file

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