github.com/kchristidis/fabric@v1.0.4-0.20171028114726-837acd08cde1/common/flogging/logging.go (about) 1 /* 2 Copyright IBM Corp. 2017 All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package flogging 18 19 import ( 20 "io" 21 "os" 22 "regexp" 23 "strings" 24 "sync" 25 26 "github.com/op/go-logging" 27 ) 28 29 const ( 30 pkgLogID = "flogging" 31 defaultFormat = "%{color}%{time:2006-01-02 15:04:05.000 MST} [%{module}] %{shortfunc} -> %{level:.4s} %{id:03x}%{color:reset} %{message}" 32 defaultLevel = logging.INFO 33 ) 34 35 var ( 36 logger *logging.Logger 37 38 defaultOutput *os.File 39 40 modules map[string]string // Holds the map of all modules and their respective log level 41 peerStartModules map[string]string 42 43 lock sync.RWMutex 44 once sync.Once 45 ) 46 47 func init() { 48 logger = logging.MustGetLogger(pkgLogID) 49 Reset() 50 initgrpclogger() 51 } 52 53 // Reset sets to logging to the defaults defined in this package. 54 func Reset() { 55 modules = make(map[string]string) 56 lock = sync.RWMutex{} 57 58 defaultOutput = os.Stderr 59 InitBackend(SetFormat(defaultFormat), defaultOutput) 60 InitFromSpec("") 61 } 62 63 // SetFormat sets the logging format. 64 func SetFormat(formatSpec string) logging.Formatter { 65 if formatSpec == "" { 66 formatSpec = defaultFormat 67 } 68 return logging.MustStringFormatter(formatSpec) 69 } 70 71 // InitBackend sets up the logging backend based on 72 // the provided logging formatter and I/O writer. 73 func InitBackend(formatter logging.Formatter, output io.Writer) { 74 backend := logging.NewLogBackend(output, "", 0) 75 backendFormatter := logging.NewBackendFormatter(backend, formatter) 76 logging.SetBackend(backendFormatter).SetLevel(defaultLevel, "") 77 } 78 79 // DefaultLevel returns the fallback value for loggers to use if parsing fails. 80 func DefaultLevel() string { 81 return defaultLevel.String() 82 } 83 84 // GetModuleLevel gets the current logging level for the specified module. 85 func GetModuleLevel(module string) string { 86 // logging.GetLevel() returns the logging level for the module, if defined. 87 // Otherwise, it returns the default logging level, as set by 88 // `flogging/logging.go`. 89 level := logging.GetLevel(module).String() 90 return level 91 } 92 93 // SetModuleLevel sets the logging level for the modules that match the supplied 94 // regular expression. Can be used to dynamically change the log level for the 95 // module. 96 func SetModuleLevel(moduleRegExp string, level string) (string, error) { 97 return setModuleLevel(moduleRegExp, level, true, false) 98 } 99 100 func setModuleLevel(moduleRegExp string, level string, isRegExp bool, revert bool) (string, error) { 101 var re *regexp.Regexp 102 logLevel, err := logging.LogLevel(level) 103 if err != nil { 104 logger.Warningf("Invalid logging level '%s' - ignored", level) 105 } else { 106 if !isRegExp || revert { 107 logging.SetLevel(logLevel, moduleRegExp) 108 logger.Debugf("Module '%s' logger enabled for log level '%s'", moduleRegExp, level) 109 } else { 110 re, err = regexp.Compile(moduleRegExp) 111 if err != nil { 112 logger.Warningf("Invalid regular expression: %s", moduleRegExp) 113 return "", err 114 } 115 lock.Lock() 116 defer lock.Unlock() 117 for module := range modules { 118 if re.MatchString(module) { 119 logging.SetLevel(logging.Level(logLevel), module) 120 modules[module] = logLevel.String() 121 logger.Debugf("Module '%s' logger enabled for log level '%s'", module, logLevel) 122 } 123 } 124 } 125 } 126 return logLevel.String(), err 127 } 128 129 // MustGetLogger is used in place of `logging.MustGetLogger` to allow us to 130 // store a map of all modules and submodules that have loggers in the system. 131 func MustGetLogger(module string) *logging.Logger { 132 l := logging.MustGetLogger(module) 133 lock.Lock() 134 defer lock.Unlock() 135 modules[module] = GetModuleLevel(module) 136 return l 137 } 138 139 // InitFromSpec initializes the logging based on the supplied spec. It is 140 // exposed externally so that consumers of the flogging package may parse their 141 // own logging specification. The logging specification has the following form: 142 // [<module>[,<module>...]=]<level>[:[<module>[,<module>...]=]<level>...] 143 func InitFromSpec(spec string) string { 144 levelAll := defaultLevel 145 var err error 146 147 if spec != "" { 148 fields := strings.Split(spec, ":") 149 for _, field := range fields { 150 split := strings.Split(field, "=") 151 switch len(split) { 152 case 1: 153 if levelAll, err = logging.LogLevel(field); err != nil { 154 logger.Warningf("Logging level '%s' not recognized, defaulting to '%s': %s", field, defaultLevel, err) 155 levelAll = defaultLevel // need to reset cause original value was overwritten 156 } 157 case 2: 158 // <module>[,<module>...]=<level> 159 levelSingle, err := logging.LogLevel(split[1]) 160 if err != nil { 161 logger.Warningf("Invalid logging level in '%s' ignored", field) 162 continue 163 } 164 165 if split[0] == "" { 166 logger.Warningf("Invalid logging override specification '%s' ignored - no module specified", field) 167 } else { 168 modules := strings.Split(split[0], ",") 169 for _, module := range modules { 170 logger.Debugf("Setting logging level for module '%s' to '%s'", module, levelSingle) 171 logging.SetLevel(levelSingle, module) 172 } 173 } 174 default: 175 logger.Warningf("Invalid logging override '%s' ignored - missing ':'?", field) 176 } 177 } 178 } 179 180 logging.SetLevel(levelAll, "") // set the logging level for all modules 181 182 // iterate through modules to reload their level in the modules map based on 183 // the new default level 184 for k := range modules { 185 MustGetLogger(k) 186 } 187 // register flogging logger in the modules map 188 MustGetLogger(pkgLogID) 189 190 return levelAll.String() 191 } 192 193 // SetPeerStartupModulesMap saves the modules and their log levels. 194 // this function should only be called at the end of peer startup. 195 func SetPeerStartupModulesMap() { 196 lock.Lock() 197 defer lock.Unlock() 198 199 once.Do(func() { 200 peerStartModules = make(map[string]string) 201 for k, v := range modules { 202 peerStartModules[k] = v 203 } 204 }) 205 } 206 207 // GetPeerStartupLevel returns the peer startup level for the specified module. 208 // It will return an empty string if the input parameter is empty or the module 209 // is not found 210 func GetPeerStartupLevel(module string) string { 211 if module != "" { 212 if level, ok := peerStartModules[module]; ok { 213 return level 214 } 215 } 216 217 return "" 218 } 219 220 // RevertToPeerStartupLevels reverts the log levels for all modules to the level 221 // defined at the end of peer startup. 222 func RevertToPeerStartupLevels() error { 223 lock.RLock() 224 defer lock.RUnlock() 225 for key := range peerStartModules { 226 _, err := setModuleLevel(key, peerStartModules[key], false, true) 227 if err != nil { 228 return err 229 } 230 } 231 logger.Info("Log levels reverted to the levels defined at the end of peer startup") 232 return nil 233 }