frostfs-node/pkg/util/logger/logger.go
Alejandro Lopez 0c40d98f7a
All checks were successful
ci/woodpecker/push/pre-commit Pipeline was successful
[#375] Add log metrics
Signed-off-by: Alejandro Lopez <a.lopez@yadro.com>
2023-06-13 09:52:45 +00:00

111 lines
2.7 KiB
Go

package logger
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// Logger represents a component
// for writing messages to log.
type Logger struct {
*zap.Logger
lvl zap.AtomicLevel
}
// 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
// MetricsNamespace is the namespace string used for log counter metrics
MetricsNamespace string
// do not support runtime rereading
}
// SetLevelString sets the minimum logging level. Default is
// "info".
//
// 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))
}
// 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;
// - console encoding;
// - ISO8601 time encoding.
//
// Logger records a stack trace for all messages at or above fatal level.
func NewLogger(prm *Prm) (*Logger, error) {
if prm == nil {
prm = defaultPrm()
}
lvl := zap.NewAtomicLevelAt(prm.level)
m := newLogMetrics(prm.MetricsNamespace)
c := zap.NewProductionConfig()
c.Level = lvl
c.Encoding = "console"
c.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
c.Sampling.Hook = func(e zapcore.Entry, sd zapcore.SamplingDecision) {
m.Inc(e.Level, sd == zapcore.LogDropped)
}
lZap, err := c.Build(
zap.AddStacktrace(zap.NewAtomicLevelAt(zap.FatalLevel)),
)
if err != nil {
return nil, err
}
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
}