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  }