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 }