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  }