github.com/MetalBlockchain/metalgo@v1.11.9/utils/logging/factory.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package logging
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"path"
    10  	"sync"
    11  
    12  	"go.uber.org/zap"
    13  	"go.uber.org/zap/zapcore"
    14  	"golang.org/x/exp/maps"
    15  	"gopkg.in/natefinch/lumberjack.v2"
    16  )
    17  
    18  var _ Factory = (*factory)(nil)
    19  
    20  // Factory creates new instances of different types of Logger
    21  type Factory interface {
    22  	// Make creates a new logger with name [name]
    23  	Make(name string) (Logger, error)
    24  
    25  	// MakeChain creates a new logger to log the events of chain [chainID]
    26  	MakeChain(chainID string) (Logger, error)
    27  
    28  	// SetLogLevels sets log levels for all loggers in factory with given logger name, level pairs.
    29  	SetLogLevel(name string, level Level) error
    30  
    31  	// SetDisplayLevels sets log display levels for all loggers in factory with given logger name, level pairs.
    32  	SetDisplayLevel(name string, level Level) error
    33  
    34  	// GetLogLevels returns all log levels in factory as name, level pairs
    35  	GetLogLevel(name string) (Level, error)
    36  
    37  	// GetDisplayLevels returns all log display levels in factory as name, level pairs
    38  	GetDisplayLevel(name string) (Level, error)
    39  
    40  	// GetLoggerNames returns the names of all logs created by this factory
    41  	GetLoggerNames() []string
    42  
    43  	// Close stops and clears all of a Factory's instantiated loggers
    44  	Close()
    45  }
    46  
    47  type logWrapper struct {
    48  	logger       Logger
    49  	displayLevel zap.AtomicLevel
    50  	logLevel     zap.AtomicLevel
    51  }
    52  
    53  type factory struct {
    54  	config Config
    55  	lock   sync.RWMutex
    56  
    57  	// For each logger created by this factory:
    58  	// Logger name --> the logger.
    59  	loggers map[string]logWrapper
    60  }
    61  
    62  // NewFactory returns a new instance of a Factory producing loggers configured with
    63  // the values set in the [config] parameter
    64  func NewFactory(config Config) Factory {
    65  	return &factory{
    66  		config:  config,
    67  		loggers: make(map[string]logWrapper),
    68  	}
    69  }
    70  
    71  // Assumes [f.lock] is held
    72  func (f *factory) makeLogger(config Config) (Logger, error) {
    73  	if _, ok := f.loggers[config.LoggerName]; ok {
    74  		return nil, fmt.Errorf("logger with name %q already exists", config.LoggerName)
    75  	}
    76  	consoleEnc := config.LogFormat.ConsoleEncoder()
    77  	fileEnc := config.LogFormat.FileEncoder()
    78  
    79  	consoleCore := NewWrappedCore(config.DisplayLevel, os.Stdout, consoleEnc)
    80  	consoleCore.WriterDisabled = config.DisableWriterDisplaying
    81  
    82  	rw := &lumberjack.Logger{
    83  		Filename:   path.Join(config.Directory, config.LoggerName+".log"),
    84  		MaxSize:    config.MaxSize,  // megabytes
    85  		MaxAge:     config.MaxAge,   // days
    86  		MaxBackups: config.MaxFiles, // files
    87  		Compress:   config.Compress,
    88  	}
    89  	fileCore := NewWrappedCore(config.LogLevel, rw, fileEnc)
    90  	prefix := config.LogFormat.WrapPrefix(config.MsgPrefix)
    91  
    92  	l := NewLogger(prefix, consoleCore, fileCore)
    93  	f.loggers[config.LoggerName] = logWrapper{
    94  		logger:       l,
    95  		displayLevel: consoleCore.AtomicLevel,
    96  		logLevel:     fileCore.AtomicLevel,
    97  	}
    98  	return l, nil
    99  }
   100  
   101  func (f *factory) Make(name string) (Logger, error) {
   102  	f.lock.Lock()
   103  	defer f.lock.Unlock()
   104  
   105  	config := f.config
   106  	config.LoggerName = name
   107  	return f.makeLogger(config)
   108  }
   109  
   110  func (f *factory) MakeChain(chainID string) (Logger, error) {
   111  	f.lock.Lock()
   112  	defer f.lock.Unlock()
   113  
   114  	config := f.config
   115  	config.MsgPrefix = chainID + " Chain"
   116  	config.LoggerName = chainID
   117  	return f.makeLogger(config)
   118  }
   119  
   120  func (f *factory) SetLogLevel(name string, level Level) error {
   121  	f.lock.RLock()
   122  	defer f.lock.RUnlock()
   123  
   124  	logger, ok := f.loggers[name]
   125  	if !ok {
   126  		return fmt.Errorf("logger with name %q not found", name)
   127  	}
   128  	logger.logLevel.SetLevel(zapcore.Level(level))
   129  	return nil
   130  }
   131  
   132  func (f *factory) SetDisplayLevel(name string, level Level) error {
   133  	f.lock.RLock()
   134  	defer f.lock.RUnlock()
   135  
   136  	logger, ok := f.loggers[name]
   137  	if !ok {
   138  		return fmt.Errorf("logger with name %q not found", name)
   139  	}
   140  	logger.displayLevel.SetLevel(zapcore.Level(level))
   141  	return nil
   142  }
   143  
   144  func (f *factory) GetLogLevel(name string) (Level, error) {
   145  	f.lock.RLock()
   146  	defer f.lock.RUnlock()
   147  
   148  	logger, ok := f.loggers[name]
   149  	if !ok {
   150  		return -1, fmt.Errorf("logger with name %q not found", name)
   151  	}
   152  	return Level(logger.logLevel.Level()), nil
   153  }
   154  
   155  func (f *factory) GetDisplayLevel(name string) (Level, error) {
   156  	f.lock.RLock()
   157  	defer f.lock.RUnlock()
   158  
   159  	logger, ok := f.loggers[name]
   160  	if !ok {
   161  		return -1, fmt.Errorf("logger with name %q not found", name)
   162  	}
   163  	return Level(logger.displayLevel.Level()), nil
   164  }
   165  
   166  func (f *factory) GetLoggerNames() []string {
   167  	f.lock.RLock()
   168  	defer f.lock.RUnlock()
   169  
   170  	return maps.Keys(f.loggers)
   171  }
   172  
   173  func (f *factory) Close() {
   174  	f.lock.Lock()
   175  	defer f.lock.Unlock()
   176  
   177  	for _, lw := range f.loggers {
   178  		lw.logger.Stop()
   179  	}
   180  	f.loggers = nil
   181  }