[#1619] logger: Filter entries by tags provided in config
Signed-off-by: Anton Nikiforov <an.nikiforov@yadro.com>
This commit is contained in:
parent
c692fc9071
commit
fc00efad23
8 changed files with 166 additions and 13 deletions
|
@ -44,7 +44,11 @@ func reloadConfig() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Reload(logPrm)
|
||||
logPrm.AllowedTags = cfg.GetStringSlice("logger.allowed_tags")
|
||||
err = log.Reload(logPrm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -80,6 +80,7 @@ func main() {
|
|||
exitErr(err)
|
||||
logPrm.SamplingHook = metrics.LogMetrics().GetSamplingHook()
|
||||
logPrm.PrependTimestamp = cfg.GetBool("logger.timestamp")
|
||||
logPrm.AllowedTags = cfg.GetStringSlice("logger.allowed_tags")
|
||||
|
||||
log, err = logger.NewLogger(logPrm)
|
||||
exitErr(err)
|
||||
|
|
|
@ -106,6 +106,7 @@ type applicationConfiguration struct {
|
|||
level string
|
||||
destination string
|
||||
timestamp bool
|
||||
allowedTags []string
|
||||
}
|
||||
|
||||
ObjectCfg struct {
|
||||
|
@ -230,6 +231,7 @@ func (a *applicationConfiguration) readConfig(c *config.Config) error {
|
|||
a.LoggerCfg.level = loggerconfig.Level(c)
|
||||
a.LoggerCfg.destination = loggerconfig.Destination(c)
|
||||
a.LoggerCfg.timestamp = loggerconfig.Timestamp(c)
|
||||
a.LoggerCfg.allowedTags = loggerconfig.AllowedTags(c)
|
||||
|
||||
// Object
|
||||
|
||||
|
@ -1073,6 +1075,7 @@ func (c *cfg) loggerPrm() (logger.Prm, error) {
|
|||
return logger.Prm{}, errors.New("incorrect log destination format: " + c.LoggerCfg.destination)
|
||||
}
|
||||
prm.PrependTimestamp = c.LoggerCfg.timestamp
|
||||
prm.AllowedTags = c.LoggerCfg.allowedTags
|
||||
|
||||
return prm, nil
|
||||
}
|
||||
|
@ -1374,8 +1377,7 @@ func (c *cfg) getComponents(ctx context.Context) []dCmp {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.log.Reload(prm)
|
||||
return nil
|
||||
return c.log.Reload(prm)
|
||||
}})
|
||||
components = append(components, dCmp{"runtime", func() error {
|
||||
setRuntimeParameters(ctx, c)
|
||||
|
|
|
@ -60,6 +60,12 @@ func Timestamp(c *config.Config) bool {
|
|||
return config.BoolSafe(c.Sub(subsection), "timestamp")
|
||||
}
|
||||
|
||||
// AllowedTags returns the value of "allowed_tags" config parameter
|
||||
// from "logger" section.
|
||||
func AllowedTags(c *config.Config) []string {
|
||||
return config.StringSliceSafe(c.Sub(subsection), "allowed_tags")
|
||||
}
|
||||
|
||||
// ToLokiConfig extracts loki config.
|
||||
func ToLokiConfig(c *config.Config) loki.Config {
|
||||
hostname, _ := os.Hostname()
|
||||
|
|
|
@ -30,6 +30,11 @@ func validateConfig(c *config.Config) error {
|
|||
return fmt.Errorf("invalid logger destination: %w", err)
|
||||
}
|
||||
|
||||
err = logger.ValidateAllowedTags(loggerconfig.AllowedTags(c))
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid list of allowed tags: %w", err)
|
||||
}
|
||||
|
||||
// shard configuration validation
|
||||
|
||||
shardNum := 0
|
||||
|
|
60
pkg/util/logger/allowed_tags.go
Normal file
60
pkg/util/logger/allowed_tags.go
Normal file
|
@ -0,0 +1,60 @@
|
|||
package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
const (
|
||||
TagMain uint8 = iota
|
||||
|
||||
tagMain = "main"
|
||||
)
|
||||
|
||||
// tagToMask return bit mask for the tag, encoded in uint32.
|
||||
func tagToMask(str string) (uint32, error) {
|
||||
switch str {
|
||||
case tagMain:
|
||||
return 1 << TagMain, nil
|
||||
default:
|
||||
return math.MaxUint32, fmt.Errorf("unsupported tag %s", str)
|
||||
}
|
||||
}
|
||||
|
||||
// parseAllowedTags returns:
|
||||
// - parsed allowed tags encoded in uint32, each bit indicates is corresponding tag allowed(1) or not(0);
|
||||
// - map(always instantiated) of bit mask for the tag to custom log level for that tag;
|
||||
// - error if it occurred(parsed allowed tags set to math.MaxUint32, map is empty).
|
||||
func parseAllowedTags(tags []string) (uint32, map[uint32]zapcore.Level, error) {
|
||||
m := make(map[uint32]zapcore.Level)
|
||||
if len(tags) == 0 {
|
||||
return math.MaxUint32, m, nil
|
||||
}
|
||||
var v uint32
|
||||
for _, str := range tags {
|
||||
tag, level, _ := strings.Cut(str, ":")
|
||||
mask, err := tagToMask(tag)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
v |= mask
|
||||
|
||||
if len(level) > 0 {
|
||||
var l zapcore.Level
|
||||
err = l.UnmarshalText([]byte(level))
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
m[mask] = l
|
||||
}
|
||||
}
|
||||
return v, m, nil
|
||||
}
|
||||
|
||||
func ValidateAllowedTags(tags []string) error {
|
||||
_, _, err := parseAllowedTags(tags)
|
||||
return err
|
||||
}
|
|
@ -6,21 +6,34 @@ import (
|
|||
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/tracing"
|
||||
qos "git.frostfs.info/TrueCloudLab/frostfs-qos/tagging"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
func (l *Logger) Debug(ctx context.Context, msg string, fields ...zap.Field) {
|
||||
if l.denyLogEntry(zapcore.DebugLevel) {
|
||||
return
|
||||
}
|
||||
l.z.Debug(msg, appendContext(ctx, fields...)...)
|
||||
}
|
||||
|
||||
func (l *Logger) Info(ctx context.Context, msg string, fields ...zap.Field) {
|
||||
if l.denyLogEntry(zapcore.InfoLevel) {
|
||||
return
|
||||
}
|
||||
l.z.Info(msg, appendContext(ctx, fields...)...)
|
||||
}
|
||||
|
||||
func (l *Logger) Warn(ctx context.Context, msg string, fields ...zap.Field) {
|
||||
if l.denyLogEntry(zapcore.WarnLevel) {
|
||||
return
|
||||
}
|
||||
l.z.Warn(msg, appendContext(ctx, fields...)...)
|
||||
}
|
||||
|
||||
func (l *Logger) Error(ctx context.Context, msg string, fields ...zap.Field) {
|
||||
if l.denyLogEntry(zapcore.ErrorLevel) {
|
||||
return
|
||||
}
|
||||
l.z.Error(msg, appendContext(ctx, fields...)...)
|
||||
}
|
||||
|
||||
|
@ -33,3 +46,14 @@ func appendContext(ctx context.Context, fields ...zap.Field) []zap.Field {
|
|||
}
|
||||
return fields
|
||||
}
|
||||
|
||||
func (l *Logger) denyLogEntry(level zapcore.Level) bool {
|
||||
if l.at.Load()&l.m != l.m {
|
||||
return true
|
||||
}
|
||||
tl := l.tl.Load().(map[uint32]zapcore.Level)
|
||||
if lvl, ok := tl[l.m]; ok {
|
||||
return level < lvl
|
||||
}
|
||||
return level < l.lvl.Level()
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ package logger
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"sync/atomic"
|
||||
|
||||
"git.frostfs.info/TrueCloudLab/zapjournald"
|
||||
"github.com/ssgreg/journald"
|
||||
|
@ -14,6 +16,9 @@ import (
|
|||
type Logger struct {
|
||||
z *zap.Logger
|
||||
lvl zap.AtomicLevel
|
||||
at *atomic.Uint32
|
||||
m uint32
|
||||
tl *atomic.Value
|
||||
}
|
||||
|
||||
// Prm groups Logger's parameters.
|
||||
|
@ -35,6 +40,9 @@ type Prm struct {
|
|||
|
||||
// PrependTimestamp specifies whether to prepend a timestamp in the log
|
||||
PrependTimestamp bool
|
||||
|
||||
// AllowedTags list of allowed tags with log level
|
||||
AllowedTags []string
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -87,10 +95,8 @@ func NewLogger(prm Prm) (*Logger, error) {
|
|||
}
|
||||
|
||||
func newConsoleLogger(prm Prm) (*Logger, error) {
|
||||
lvl := zap.NewAtomicLevelAt(prm.level)
|
||||
|
||||
c := zap.NewProductionConfig()
|
||||
c.Level = lvl
|
||||
c.Level = zap.NewAtomicLevelAt(zapcore.DebugLevel)
|
||||
c.Encoding = "console"
|
||||
if prm.SamplingHook != nil {
|
||||
c.Sampling.Hook = prm.SamplingHook
|
||||
|
@ -110,14 +116,23 @@ func newConsoleLogger(prm Prm) (*Logger, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
l := &Logger{z: lZap, lvl: lvl}
|
||||
pat, tl, err := parseAllowedTags(prm.AllowedTags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var at atomic.Uint32
|
||||
at.Store(pat)
|
||||
|
||||
v := atomic.Value{}
|
||||
v.Store(tl)
|
||||
|
||||
l := &Logger{z: lZap, lvl: zap.NewAtomicLevelAt(prm.level), m: 1 << TagMain, at: &at, tl: &v}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func newJournaldLogger(prm Prm) (*Logger, error) {
|
||||
lvl := zap.NewAtomicLevelAt(prm.level)
|
||||
|
||||
lvl := zap.NewAtomicLevelAt(zapcore.DebugLevel)
|
||||
c := zap.NewProductionConfig()
|
||||
c.Level = lvl
|
||||
c.Encoding = "console"
|
||||
|
@ -142,13 +157,32 @@ func newJournaldLogger(prm Prm) (*Logger, error) {
|
|||
|
||||
lZap := zap.New(coreWithContext, zap.AddStacktrace(zap.NewAtomicLevelAt(zap.FatalLevel)), zap.AddCallerSkip(1))
|
||||
|
||||
l := &Logger{z: lZap, lvl: lvl}
|
||||
pat, tl, err := parseAllowedTags(prm.AllowedTags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var at atomic.Uint32
|
||||
at.Store(pat)
|
||||
|
||||
v := atomic.Value{}
|
||||
v.Store(tl)
|
||||
|
||||
l := &Logger{z: lZap, lvl: zap.NewAtomicLevelAt(prm.level), m: 1 << TagMain, at: &at, tl: &v}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func (l *Logger) Reload(prm Prm) {
|
||||
func (l *Logger) Reload(prm Prm) error {
|
||||
at, tl, err := parseAllowedTags(prm.AllowedTags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.lvl.SetLevel(prm.level)
|
||||
l.at.Store(at)
|
||||
|
||||
l.tl.Store(tl)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Logger) WithOptions(options ...zap.Option) {
|
||||
|
@ -156,11 +190,28 @@ func (l *Logger) WithOptions(options ...zap.Option) {
|
|||
}
|
||||
|
||||
func (l *Logger) With(fields ...zap.Field) *Logger {
|
||||
return &Logger{z: l.z.With(fields...)}
|
||||
c := *l
|
||||
c.z = l.z.With(fields...)
|
||||
return &c
|
||||
}
|
||||
|
||||
func (l *Logger) WithTag(bit uint8) *Logger {
|
||||
c := *l
|
||||
c.m = uint32(1 << bit)
|
||||
return &c
|
||||
}
|
||||
|
||||
func NewLoggerWrapper(z *zap.Logger) *Logger {
|
||||
at := &atomic.Uint32{}
|
||||
at.Store(math.MaxUint32)
|
||||
|
||||
tl := &atomic.Value{}
|
||||
tl.Store(make(map[uint32]zapcore.Level))
|
||||
|
||||
return &Logger{
|
||||
z: z.WithOptions(zap.AddCallerSkip(1)),
|
||||
at: at,
|
||||
tl: tl,
|
||||
lvl: zap.NewAtomicLevelAt(zapcore.DebugLevel),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue