diff --git a/app/logger/config.go b/app/logger/config.go index 2f3547ce..7906312c 100644 --- a/app/logger/config.go +++ b/app/logger/config.go @@ -1,11 +1,17 @@ package logger -import "go.uber.org/zap" +import ( + "go.uber.org/zap" + + "github.com/anytypeio/any-sync/util/slice" +) type Config struct { - Production bool `yaml:"production"` - DefaultLevel string `yaml:"defaultLevel"` - NamedLevels map[string]string `yaml:"namedLevels"` + Production bool `yaml:"production"` + DefaultLevel string `yaml:"defaultLevel"` + NamedLevels map[string]string `yaml:"namedLevels"` + AddOutputPaths []string + DisableStdErr bool } func (l Config) ApplyGlobal() { @@ -15,19 +21,33 @@ func (l Config) ApplyGlobal() { } else { conf = zap.NewDevelopmentConfig() } + if len(l.AddOutputPaths) > 0 { + conf.OutputPaths = append(conf.OutputPaths, l.AddOutputPaths...) + } + if l.DisableStdErr { + conf.OutputPaths = slice.Filter(conf.OutputPaths, func(path string) bool { + return path != "stderr" + }) + } + + var err error if defaultLevel, err := zap.ParseAtomicLevel(l.DefaultLevel); err == nil { conf.Level = defaultLevel } - var levels = make(map[string]zap.AtomicLevel) + var lvl = make(map[string]zap.AtomicLevel) for k, v := range l.NamedLevels { - if lev, err := zap.ParseAtomicLevel(v); err != nil { - levels[k] = lev + if lev, err := zap.ParseAtomicLevel(v); err == nil { + lvl[k] = lev + // we need to have a minimum level of all named loggers for the main logger + if lev.Level() < conf.Level.Level() { + conf.Level.SetLevel(lev.Level()) + } } } - defaultLogger, err := conf.Build() + lg, err := conf.Build() if err != nil { Default().Fatal("can't build logger", zap.Error(err)) } - SetDefault(defaultLogger) - SetNamedLevels(levels) + SetDefault(lg) + SetNamedLevels(lvl) } diff --git a/app/logger/log.go b/app/logger/log.go index 028c9988..ce09c3cd 100644 --- a/app/logger/log.go +++ b/app/logger/log.go @@ -1,51 +1,110 @@ package logger import ( - "go.uber.org/zap" "sync" + + "github.com/gobwas/glob" + "go.uber.org/zap" ) var ( - mu sync.Mutex - defaultLogger *zap.Logger - levels = make(map[string]zap.AtomicLevel) - loggers = make(map[string]CtxLogger) + mu sync.Mutex + logger *zap.Logger + loggerConfig zap.Config + namedLevels = make(map[string]zap.AtomicLevel) + namedGlobs = make(map[string]glob.Glob) + namedLoggers = make(map[string]CtxLogger) ) func init() { - defaultLogger, _ = zap.NewDevelopment() - zap.NewProduction() + loggerConfig = zap.NewDevelopmentConfig() + logger, _ = loggerConfig.Build() } +// SetDefault replaces the default logger +// you need to call SetNamedLevels after in case you have named loggers, +// otherwise they will use the old logger func SetDefault(l *zap.Logger) { mu.Lock() defer mu.Unlock() - *defaultLogger = *l - for name, l := range loggers { - *l.Logger = *defaultLogger.Named(name) - } + *logger = *l } +// SetNamedLevels sets the namedLevels for named loggers +// it also supports glob patterns for names, like "app*" +// can be racy in case there are existing named loggers +// so consider to call only once at the beginning func SetNamedLevels(l map[string]zap.AtomicLevel) { mu.Lock() defer mu.Unlock() - levels = l + namedLevels = l + + var minLevel = logger.Level() + for k, l := range namedLevels { + g, err := glob.Compile(k) + if err == nil { + namedGlobs[k] = g + } + namedLevels[k] = l + if l.Level() < minLevel { + minLevel = l.Level() + } + } + + if minLevel < logger.Level() { + // recreate logger if the min level is lower than the current min one + loggerConfig.Level = zap.NewAtomicLevelAt(minLevel) + logger, _ = loggerConfig.Build() + } + + for name, lg := range namedLoggers { + level := getLevel(name) + // this can be racy, but + lg.Logger = zap.New(logger.Core()).WithOptions( + zap.IncreaseLevel(level), + ).Named(name) + } } func Default() *zap.Logger { mu.Lock() defer mu.Unlock() - return defaultLogger + return logger +} + +func getLevel(name string) zap.AtomicLevel { + level, ok := namedLevels[name] + if !ok { + var found bool + for globName, glob := range namedGlobs { + if glob.Match(name) { + found = true + level, _ = namedLevels[globName] + // no need to check ok, because we know that globName exists + break + } + } + if !found { + level = loggerConfig.Level + } + } + return level } func NewNamed(name string, fields ...zap.Field) CtxLogger { mu.Lock() defer mu.Unlock() - l := defaultLogger.Named(name) - if len(fields) > 0 { - l = l.With(fields...) + + if l, nameExists := namedLoggers[name]; nameExists { + return l } + + level := getLevel(name) + l := zap.New(logger.Core()).WithOptions( + zap.IncreaseLevel(level), + ).Named(name) + ctxL := CtxLogger{l} - loggers[name] = ctxL + namedLoggers[name] = ctxL return ctxL } diff --git a/go.mod b/go.mod index a4c42fac..1c763027 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/awalterschulze/gographviz v2.0.3+incompatible github.com/cespare/xxhash v1.1.0 github.com/cheggaaa/mb/v3 v3.0.1 + github.com/gobwas/glob v0.2.3 github.com/goccy/go-graphviz v0.1.0 github.com/gogo/protobuf v1.3.2 github.com/golang/mock v1.6.0 @@ -22,6 +23,7 @@ require ( github.com/ipfs/go-unixfs v0.4.4 github.com/libp2p/go-libp2p v0.24.1 github.com/minio/sha256-simd v1.0.0 + github.com/mr-tron/base58 v1.2.0 github.com/multiformats/go-multibase v0.1.1 github.com/multiformats/go-multihash v0.2.1 github.com/prometheus/client_golang v1.14.0 @@ -74,7 +76,6 @@ require ( github.com/mattn/go-isatty v0.0.17 // indirect github.com/mattn/go-pointer v0.0.1 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/mr-tron/base58 v1.2.0 // indirect github.com/multiformats/go-base32 v0.1.0 // indirect github.com/multiformats/go-base36 v0.2.0 // indirect github.com/multiformats/go-multiaddr v0.8.0 // indirect diff --git a/go.sum b/go.sum index d8708cf2..1cc3b14b 100644 --- a/go.sum +++ b/go.sum @@ -106,6 +106,8 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/goccy/go-graphviz v0.1.0 h1:6OqQoQ5PeAiHYe/YcusyeulqBrOkUb16HQ4ctRdyVUU= github.com/goccy/go-graphviz v0.1.0/go.mod h1:wXVsXxmyMQU6TN3zGRttjNn3h+iCAS7xQFC6TlNvLhk= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=