add an ability to set levels separately for named loggers

with glob support
This commit is contained in:
Roman Khafizianov 2023-03-06 22:25:43 +01:00 committed by Mikhail Iudin
parent 5e6199d106
commit 77d8c739f2
No known key found for this signature in database
GPG Key ID: FAAAA8BAABDFF1C0
4 changed files with 110 additions and 28 deletions

View File

@ -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"`
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)
}

View File

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

3
go.mod
View File

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

2
go.sum
View File

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