github.com/ethersphere/bee/v2@v2.2.0/pkg/log/registry.go (about) 1 // Copyright 2022 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package log 6 7 import ( 8 "fmt" 9 "io" 10 "os" 11 "regexp" 12 "sync" 13 14 "github.com/hashicorp/go-multierror" 15 ) 16 17 // defaults specifies the default global options for log 18 // package which every new logger will inherit on its creation. 19 var defaults = struct { 20 pin sync.Once // pin pins the options and formatter settings. 21 options *Options 22 formatter *formatter 23 }{ 24 options: &Options{ 25 sink: os.Stderr, 26 verbosity: VerbosityDebug, 27 fmtOptions: fmtOptions{ 28 timestampLayout: "2006-01-02 15:04:05.000000", 29 maxLogDepth: 16, 30 }, 31 }, 32 } 33 34 // ModifyDefaults modifies the global default options for this log package 35 // that each new logger inherits when it is created. The default values can 36 // be modified only once, so further calls to this function will be ignored. 37 // This function should be called before the first call to the NewLogger 38 // factory constructor, otherwise it will have no effect. 39 func ModifyDefaults(opts ...Option) { 40 defaults.pin.Do(func() { 41 for _, modify := range opts { 42 modify(defaults.options) 43 } 44 defaults.formatter = newFormatter(defaults.options.fmtOptions) 45 }) 46 } 47 48 // loggers is the central register for Logger instances. 49 var loggers = new(sync.Map) 50 51 // NewLogger is a factory constructor which returns a new logger instance 52 // based on the given name. If such an instance already exists in the 53 // logger registry, then this existing instance is returned instead. 54 // The given options take precedence over the default options set 55 // by the ModifyDefaults function. 56 func NewLogger(name string, opts ...Option) Logger { 57 // Pin the default settings if 58 // they are not already pinned. 59 ModifyDefaults() 60 61 options := *defaults.options 62 for _, modify := range opts { 63 modify(&options) 64 } 65 66 if options.sink == io.Discard { 67 return Noop 68 } 69 70 formatter := defaults.formatter 71 if options.fmtOptions != defaults.options.fmtOptions { 72 formatter = newFormatter(options.fmtOptions) 73 } 74 75 val, ok := loggers.Load(hash(name, 0, "", options.sink)) 76 if ok { 77 return val.(*logger) 78 } 79 80 l := &logger{ 81 formatter: formatter, 82 verbosity: options.verbosity, 83 sink: options.sink, 84 levelHooks: options.levelHooks, 85 metrics: options.logMetrics, 86 } 87 l.builder = &builder{ 88 l: l, 89 names: []string{name}, 90 namesStr: name, 91 } 92 return l 93 } 94 95 // SetVerbosity sets the level 96 // of verbosity of the given logger. 97 func SetVerbosity(l Logger, v Level) error { 98 bl := l.(*logger) 99 switch newLvl, max := v.get(), Level(bl.v); { 100 case newLvl == VerbosityAll: 101 bl.setVerbosity(max) 102 case newLvl > max: 103 return fmt.Errorf("maximum verbosity %d exceeded for logger: %s", bl.v, bl.id) 104 default: 105 bl.setVerbosity(newLvl) 106 } 107 return nil 108 } 109 110 // SetVerbosityByExp sets all loggers to the given 111 // verbosity level v that match the given expression 112 // e, which can be a logger id or a regular expression. 113 // An error is returned if e fails to compile. 114 func SetVerbosityByExp(e string, v Level) error { 115 val, ok := loggers.Load(e) 116 if ok { 117 val.(*logger).setVerbosity(v) 118 return nil 119 } 120 121 rex, err := regexp.Compile(e) 122 if err != nil { 123 return err 124 } 125 126 var merr *multierror.Error 127 loggers.Range(func(key, val interface{}) bool { 128 if rex.MatchString(key.(string)) { 129 merr = multierror.Append(merr, SetVerbosity(val.(*logger), v)) 130 } 131 return true 132 }) 133 return merr.ErrorOrNil() 134 } 135 136 // RegistryIterate iterates through all registered loggers. 137 func RegistryIterate(fn func(id, path string, verbosity Level, v uint) (next bool)) { 138 loggers.Range(func(_, val interface{}) bool { 139 l := val.(*logger) 140 return fn(l.id, l.namesStr, l.verbosity.get(), l.v) 141 }) 142 }