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  }