github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/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 } 51 52 // Reset sets to logging to the defaults defined in this package. 53 func Reset() { 54 modules = make(map[string]string) 55 lock = sync.RWMutex{} 56 57 defaultOutput = os.Stderr 58 InitBackend(SetFormat(defaultFormat), defaultOutput) 59 InitFromSpec("") 60 } 61 62 // SetFormat sets the logging format. 63 func SetFormat(formatSpec string) logging.Formatter { 64 if formatSpec == "" { 65 formatSpec = defaultFormat 66 } 67 return logging.MustStringFormatter(formatSpec) 68 } 69 70 // InitBackend sets up the logging backend based on 71 // the provided logging formatter and I/O writer. 72 func InitBackend(formatter logging.Formatter, output io.Writer) { 73 backend := logging.NewLogBackend(output, "", 0) 74 backendFormatter := logging.NewBackendFormatter(backend, formatter) 75 logging.SetBackend(backendFormatter).SetLevel(defaultLevel, "") 76 } 77 78 // DefaultLevel returns the fallback value for loggers to use if parsing fails. 79 func DefaultLevel() string { 80 return defaultLevel.String() 81 } 82 83 // GetModuleLevel gets the current logging level for the specified module. 84 func GetModuleLevel(module string) string { 85 // logging.GetLevel() returns the logging level for the module, if defined. 86 // Otherwise, it returns the default logging level, as set by 87 // `flogging/logging.go`. 88 level := logging.GetLevel(module).String() 89 return level 90 } 91 92 // SetModuleLevel sets the logging level for the modules that match the supplied 93 // regular expression. Can be used to dynamically change the log level for the 94 // module. 95 func SetModuleLevel(moduleRegExp string, level string) (string, error) { 96 return setModuleLevel(moduleRegExp, level, true, false) 97 } 98 99 func setModuleLevel(moduleRegExp string, level string, isRegExp bool, revert bool) (string, error) { 100 var re *regexp.Regexp 101 logLevel, err := logging.LogLevel(level) 102 if err != nil { 103 logger.Warningf("Invalid logging level '%s' - ignored", level) 104 } else { 105 if !isRegExp || revert { 106 logging.SetLevel(logLevel, moduleRegExp) 107 logger.Debugf("Module '%s' logger enabled for log level '%s'", moduleRegExp, level) 108 } else { 109 re, err = regexp.Compile(moduleRegExp) 110 if err != nil { 111 logger.Warningf("Invalid regular expression: %s", moduleRegExp) 112 return "", err 113 } 114 lock.Lock() 115 defer lock.Unlock() 116 for module := range modules { 117 if re.MatchString(module) { 118 logging.SetLevel(logging.Level(logLevel), module) 119 modules[module] = logLevel.String() 120 logger.Debugf("Module '%s' logger enabled for log level '%s'", module, logLevel) 121 } 122 } 123 } 124 } 125 return logLevel.String(), err 126 } 127 128 // MustGetLogger is used in place of `logging.MustGetLogger` to allow us to 129 // store a map of all modules and submodules that have loggers in the system. 130 func MustGetLogger(module string) *logging.Logger { 131 l := logging.MustGetLogger(module) 132 lock.Lock() 133 defer lock.Unlock() 134 modules[module] = GetModuleLevel(module) 135 return l 136 } 137 138 // InitFromSpec initializes the logging based on the supplied spec. It is 139 // exposed externally so that consumers of the flogging package may parse their 140 // own logging specification. The logging specification has the following form: 141 // [<module>[,<module>...]=]<level>[:[<module>[,<module>...]=]<level>...] 142 func InitFromSpec(spec string) string { 143 levelAll := defaultLevel 144 var err error 145 146 if spec != "" { 147 fields := strings.Split(spec, ":") 148 for _, field := range fields { 149 split := strings.Split(field, "=") 150 switch len(split) { 151 case 1: 152 if levelAll, err = logging.LogLevel(field); err != nil { 153 logger.Warningf("Logging level '%s' not recognized, defaulting to '%s': %s", field, defaultLevel, err) 154 levelAll = defaultLevel // need to reset cause original value was overwritten 155 } 156 case 2: 157 // <module>[,<module>...]=<level> 158 levelSingle, err := logging.LogLevel(split[1]) 159 if err != nil { 160 logger.Warningf("Invalid logging level in '%s' ignored", field) 161 continue 162 } 163 164 if split[0] == "" { 165 logger.Warningf("Invalid logging override specification '%s' ignored - no module specified", field) 166 } else { 167 modules := strings.Split(split[0], ",") 168 for _, module := range modules { 169 logger.Debugf("Setting logging level for module '%s' to '%s'", module, levelSingle) 170 logging.SetLevel(levelSingle, module) 171 } 172 } 173 default: 174 logger.Warningf("Invalid logging override '%s' ignored - missing ':'?", field) 175 } 176 } 177 } 178 179 logging.SetLevel(levelAll, "") // set the logging level for all modules 180 181 // iterate through modules to reload their level in the modules map based on 182 // the new default level 183 for k := range modules { 184 MustGetLogger(k) 185 } 186 // register flogging logger in the modules map 187 MustGetLogger(pkgLogID) 188 189 return levelAll.String() 190 } 191 192 // SetPeerStartupModulesMap saves the modules and their log levels. 193 // this function should only be called at the end of peer startup. 194 func SetPeerStartupModulesMap() { 195 lock.Lock() 196 defer lock.Unlock() 197 198 once.Do(func() { 199 peerStartModules = make(map[string]string) 200 for k, v := range modules { 201 peerStartModules[k] = v 202 } 203 }) 204 } 205 206 // GetPeerStartupLevel returns the peer startup level for the specified module. 207 // It will return an empty string if the input parameter is empty or the module 208 // is not found 209 func GetPeerStartupLevel(module string) string { 210 if module != "" { 211 if level, ok := peerStartModules[module]; ok { 212 return level 213 } 214 } 215 216 return "" 217 } 218 219 // RevertToPeerStartupLevels reverts the log levels for all modules to the level 220 // defined at the end of peer startup. 221 func RevertToPeerStartupLevels() error { 222 lock.RLock() 223 defer lock.RUnlock() 224 for key := range peerStartModules { 225 _, err := setModuleLevel(key, peerStartModules[key], false, true) 226 if err != nil { 227 return err 228 } 229 } 230 logger.Info("Log levels reverted to the levels defined at the end of peer startup") 231 return nil 232 }