frostfs-s3-lifecycler/cmd/s3-lifecycler/logger.go
Denis Kirillov 2f7a73ee36
Some checks failed
/ DCO (pull_request) Successful in 48s
/ Vulncheck (pull_request) Successful in 1m17s
/ Builds (pull_request) Failing after 1m5s
/ Lint (pull_request) Successful in 2m32s
/ Tests (pull_request) Successful in 1m7s
[#20] Support logging with tags
Allow to log additionally "tagged" log entries.

Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
2024-09-23 17:39:09 +03:00

139 lines
3.5 KiB
Go

package main
import (
"fmt"
"strings"
"git.frostfs.info/TrueCloudLab/zapjournald"
"github.com/nspcc-dev/neo-go/cli/options"
"github.com/spf13/viper"
"github.com/ssgreg/journald"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
const (
destinationStdout string = "stdout"
destinationJournald string = "journald"
)
type Logger struct {
logger *zap.Logger
lvl zap.AtomicLevel
}
func pickLogger(v *viper.Viper) *Logger {
switch dest := v.GetString(cfgLoggerDestination); dest {
case destinationStdout:
return newStdoutLogger(v)
case destinationJournald:
return newJournaldLogger(v)
default:
panic(fmt.Sprintf("wrong destination for logger: %s", dest))
}
}
func newStdoutLogger(v *viper.Viper) *Logger {
c := newZapLogConfig(v)
out, errSink, err := openZapSinks(c)
if err != nil {
panic(fmt.Sprintf("open zap sinks: %v", err.Error()))
}
core := zapcore.NewCore(zapcore.NewConsoleEncoder(c.EncoderConfig), out, c.Level)
core = options.NewFilteringCore(core, filteringLogOption(v))
l := zap.New(core, zap.AddStacktrace(zap.NewAtomicLevelAt(zap.FatalLevel)), zap.ErrorOutput(errSink))
return &Logger{logger: l, lvl: c.Level}
}
func openZapSinks(cfg zap.Config) (zapcore.WriteSyncer, zapcore.WriteSyncer, error) {
sink, closeOut, err := zap.Open(cfg.OutputPaths...)
if err != nil {
return nil, nil, err
}
errSink, _, err := zap.Open(cfg.ErrorOutputPaths...)
if err != nil {
closeOut()
return nil, nil, err
}
return sink, errSink, nil
}
func newJournaldLogger(v *viper.Viper) *Logger {
c := newZapLogConfig(v)
// We can use NewJSONEncoder instead if, say, frontend
// would like to access journald logs and parse them easily.
encoder := zapjournald.NewPartialEncoder(zapcore.NewConsoleEncoder(c.EncoderConfig), zapjournald.SyslogFields)
core := zapjournald.NewCore(c.Level, encoder, &journald.Journal{}, zapjournald.SyslogFields)
filteringCore := options.NewFilteringCore(core, filteringLogOption(v))
coreWithContext := filteringCore.With([]zapcore.Field{
zapjournald.SyslogFacility(zapjournald.LogDaemon),
zapjournald.SyslogIdentifier(),
zapjournald.SyslogPid(),
})
l := zap.New(coreWithContext, zap.AddStacktrace(zap.NewAtomicLevelAt(zap.FatalLevel)))
return &Logger{logger: l, lvl: c.Level}
}
func newZapLogConfig(v *viper.Viper) zap.Config {
lvl, err := getLogLevel(v.GetString(cfgLoggerLevel))
if err != nil {
panic(err)
}
c := zap.NewProductionConfig()
c.Level = zap.NewAtomicLevelAt(lvl)
c.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
if v.GetBool(cfgLoggerSamplingEnabled) {
c.Sampling = &zap.SamplingConfig{
Initial: v.GetInt(cfgLoggerSamplingInitial),
Thereafter: v.GetInt(cfgLoggerSamplingThereafter),
}
} else {
c.Sampling = nil
}
return c
}
func filteringLogOption(v *viper.Viper) options.FilterFunc {
tags := v.GetStringSlice(cfgLoggerTags)
return func(entry zapcore.Entry) bool {
if !strings.HasPrefix(entry.Message, "tag:") {
return true
}
msg := entry.Message[4:] // len("tag:")
for _, tag := range tags {
if msg == tag {
return true
}
}
return false
}
}
func getLogLevel(lvlStr string) (zapcore.Level, error) {
var lvl zapcore.Level
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
}