package logger import ( "sync" "github.com/gobwas/glob" "go.uber.org/zap" ) var ( 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) namedSugarLoggers = make(map[string]*zap.SugaredLogger) ) func init() { 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() *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() 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, nl := range namedLoggers { level := getLevel(name) newCore := zap.New(logger.Core()).Named(name).WithOptions( zap.IncreaseLevel(level), ) *(nl.Logger) = *newCore } for name, nl := range namedSugarLoggers { level := getLevel(name) newCore := zap.New(logger.Core()).Named(name).WithOptions( zap.IncreaseLevel(level), ).Sugar() *(nl) = *newCore } } func Default() *zap.Logger { mu.Lock() defer mu.Unlock() 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() if l, nameExists := namedLoggers[name]; nameExists { return l } level := getLevel(name) l := zap.New(logger.Core()).Named(name).WithOptions(zap.IncreaseLevel(level), zap.Fields(fields...)) ctxL := CtxLogger{Logger: l, name: name} namedLoggers[name] = ctxL return ctxL } func NewNamedSugared(name string) *zap.SugaredLogger { mu.Lock() defer mu.Unlock() if l, nameExists := namedSugarLoggers[name]; nameExists { return l } level := getLevel(name) l := zap.New(logger.Core()).Named(name).Sugar().WithOptions(zap.IncreaseLevel(level)) namedSugarLoggers[name] = l return l }