github.com/rohankumardubey/proxyfs@v0.0.0-20210108201508-653efa9ab00e/logger/api.go (about)

     1  // Package logger provides logging wrappers
     2  //
     3  // These wrappers allow us to standardize logging while still using a third-party
     4  // logging package.
     5  //
     6  // This package is currently implemented on top of the sirupsen/logrus package:
     7  //   https://github.com/sirupsen/logrus
     8  //
     9  // The APIs here add package and calling function to all logs.
    10  //
    11  // Where possible, fields passed to logs are parameterized, to standardize log
    12  // formatting to make log searches based on these fields easier.
    13  //
    14  // Logging of trace and debug logs are enabled/disabled on a per package basis.
    15  package logger
    16  
    17  import (
    18  	"fmt"
    19  	"io"
    20  	"os"
    21  	"regexp"
    22  	"sync"
    23  
    24  	log "github.com/sirupsen/logrus"
    25  
    26  	"github.com/swiftstack/ProxyFS/utils"
    27  )
    28  
    29  type Level int
    30  
    31  // Our logging levels - These are the different logging levels supported by this package.
    32  //
    33  // We have more detailed logging levels than the logrus log package.
    34  // As a result, when we do our logging we need to map from our levels
    35  // to the logrus ones before calling logrus APIs.
    36  const (
    37  	// PanicLevel corresponds to logrus.PanicLevel; Logrus will log and then call panic with the log message
    38  	PanicLevel Level = iota
    39  	// FatalLevel corresponds to logrus.FatalLevel; Logrus will log and then calls `os.Exit(1)`.
    40  	// It will exit even if the logging level is set to Panic.
    41  	FatalLevel
    42  	// ErrorLevel corresponds to logrus.ErrorLevel
    43  	ErrorLevel
    44  	// WarnLevel corresponds to logrus.WarnLevel
    45  	WarnLevel
    46  	// InfoLevel corresponds to logrus.InfoLevel; These are general operational entries about what's going on inside the application.
    47  	InfoLevel
    48  
    49  	// TraceLevel is used for operational logs that trace success path through the application.
    50  	// Whether these are logged is controlled on a per-package basis by settings in this file.
    51  	// When enabled, these are logged at logrus.InfoLevel.
    52  	TraceLevel
    53  
    54  	// DebugLevel is used for very verbose logging, intended to debug internal operations of a
    55  	// particular area. Whether these are logged is controlled on a per-package basis by settings
    56  	// in this file.
    57  	// When enabled, these are logged at logrus.DebugLevel.
    58  	DebugLevel
    59  )
    60  
    61  // Stats-related constants
    62  var (
    63  	panicStat = "logging.level.panic"
    64  	fatalStat = "logging.level.fatal"
    65  	errorStat = "logging.level.error"
    66  	warnStat  = "logging.level.warn"
    67  	infoStat  = "logging.level.info"
    68  	traceStat = "logging.level.trace"
    69  	debugStat = "logging.level.debug"
    70  	noStat    = ""
    71  )
    72  
    73  // Level to stat string conversion
    74  func (level Level) statString() *string {
    75  	switch level {
    76  	case PanicLevel:
    77  		return &panicStat
    78  	case FatalLevel:
    79  		return &fatalStat
    80  	case ErrorLevel:
    81  		return &errorStat
    82  	case WarnLevel:
    83  		return &warnStat
    84  	case TraceLevel:
    85  		return &traceStat
    86  	case InfoLevel:
    87  		return &infoStat
    88  	case DebugLevel:
    89  		return &debugStat
    90  	}
    91  	return &noStat
    92  }
    93  
    94  // Flag to disable all logging, for performance testing.
    95  var disableLoggingForPerfTesting = false
    96  
    97  // Enable/disable for trace and debug levels.
    98  // These are defaulted to disabled unless otherwise specified in .conf file
    99  var traceLevelEnabled = false
   100  var debugLevelEnabled = false
   101  
   102  // packageTraceSettings controls whether tracing is enabled for particular packages.
   103  // If a package is in this map and is set to "true", then tracing for that package is
   104  // considered to be enabled and trace logs for that package will be emitted. If the
   105  // package is in this list and is set to "false", OR if the package is not in this list,
   106  // trace logs for that package will NOT be emitted.
   107  //
   108  // Note: In order to enable tracing for a package using the "Logging.TraceLevelLogging"
   109  // config variable, the package must be in this map with a value of false (or true).
   110  //
   111  var packageTraceSettings = map[string]bool{
   112  	"dlm":         false,
   113  	"fs":          false,
   114  	"fuse":        false,
   115  	"headhunter":  false,
   116  	"inode":       false,
   117  	"jrpcfs":      false,
   118  	"logger":      false,
   119  	"proxyfsd":    false,
   120  	"sortedmap":   false,
   121  	"swiftclient": false,
   122  	"transitions": false,
   123  }
   124  
   125  func setTraceLoggingLevel(confStrSlice []string) {
   126  	if len(confStrSlice) == 0 {
   127  		traceLevelEnabled = false
   128  	}
   129  
   130  HandlePkgs:
   131  	for _, pkg := range confStrSlice {
   132  		switch pkg {
   133  		case "none":
   134  			traceLevelEnabled = false
   135  			break HandlePkgs
   136  		default:
   137  			if _, ok := packageTraceSettings[pkg]; ok {
   138  				// Package exists in the map
   139  				packageTraceSettings[pkg] = true
   140  
   141  				// If any trace level is enabled, need to enable trace level in general.
   142  				// This flag lets us avoid the performance hit of trace-level API calls
   143  				// if the trace level is disabled.
   144  				traceLevelEnabled = true
   145  			}
   146  		}
   147  	}
   148  
   149  	// Log the packages that are enabled, if any
   150  	if traceLevelEnabled {
   151  		for pkg, isEnabled := range packageTraceSettings {
   152  			if isEnabled {
   153  				Infof("Package %v trace logging is enabled.", pkg)
   154  			}
   155  		}
   156  	}
   157  }
   158  
   159  func traceEnabled(pkg string) bool {
   160  	// Return whether tracing is enabled for the specified package.
   161  	// If not found in the package trace map, traces are considered to be turned off.
   162  	if isEnabled, ok := packageTraceSettings[pkg]; ok {
   163  		return isEnabled
   164  	} else {
   165  		return false
   166  	}
   167  }
   168  
   169  // traceEnabledForPackage returns whether tracing is enabled for the package stored in the context.
   170  // If not found in the package trace map, traces are considered to be turned off.
   171  func (ctx *FuncCtx) traceEnabledForPackage() bool {
   172  	pkg := ctx.getPackage()
   173  	return traceEnabled(pkg)
   174  }
   175  
   176  // packageDebugSettings controls which debug logs are enabled for particular packages.
   177  //
   178  // If a package is in this map, then debug logging for that package is
   179  // considered to be enabled for debug tags in the package's map entry.
   180  // If a debug log's tag is NOT in this list, OR if the package is not in the map,
   181  // debug logs for that package will NOT be emitted.
   182  //
   183  // Unlike trace settings, debug settings are stored as a list of enabled debug tags.
   184  // Bear in mind that these tags are evaluated on a package + tag basis, so the same tag
   185  // can be used on different packages without conflict.
   186  const DbgInodeInternal string = "debug_inode_internal"
   187  const DbgInternal string = "debug_internal"
   188  const DbgTesting string = "debug_test"
   189  
   190  var packageDebugSettings = map[string][]string{
   191  	"ldlm": []string{
   192  		//DbgInternal,
   193  		//DbgTesting,
   194  	},
   195  	"fs": []string{
   196  		//DbgInternal,
   197  	},
   198  	"jrpcfs": []string{
   199  		//DbgInternal,
   200  		//DbgTesting,
   201  	},
   202  	"inode": []string{
   203  		//DbgInodeInternal,
   204  	},
   205  }
   206  
   207  func setDebugLoggingLevel(confStrSlice []string) {
   208  	if len(confStrSlice) == 0 {
   209  		debugLevelEnabled = false
   210  	}
   211  
   212  HandlePkgs:
   213  	for _, pkg := range confStrSlice {
   214  		switch pkg {
   215  		case "none":
   216  			debugLevelEnabled = false
   217  			break HandlePkgs
   218  		default:
   219  			if _, ok := packageDebugSettings[pkg]; ok {
   220  				// Package exists in the map
   221  
   222  				// XXX TODO: Add .conf support for specifying which debug IDs to enable.
   223  				//           For now, enabling internal and testing by default.
   224  				packageDebugSettings[pkg] = []string{DbgInternal, DbgTesting}
   225  
   226  				// If any debug level is enabled, need to enable debug level in general.
   227  				// This flag lets us avoid the performance hit of debug-level API calls
   228  				// if the debug level is disabled.
   229  				debugLevelEnabled = true
   230  			}
   231  		}
   232  	}
   233  
   234  	// Log the packages that are enabled, if any
   235  	if debugLevelEnabled {
   236  		for pkg, ids := range packageDebugSettings {
   237  			if len(ids) > 0 {
   238  				Infof("Package %v debug logging is enabled.", pkg)
   239  			}
   240  		}
   241  	}
   242  }
   243  
   244  // debugEnabledForPackage returns whether debug logs are enabled for the package stored in the context.
   245  func (ctx *FuncCtx) debugEnabledForPackage(debugID string) bool {
   246  	pkg := ctx.getPackage()
   247  
   248  	// Return whether debug is enabled for the package and id.
   249  	// If not found in the package debug map, debug logging is considered to be turned off.
   250  	if idList, ok := packageDebugSettings[pkg]; ok {
   251  		for _, id := range idList {
   252  			if id == debugID {
   253  				return true
   254  			}
   255  		}
   256  	}
   257  	return false
   258  }
   259  
   260  // Log fields supported by logger:
   261  const packageKey string = "package"
   262  const functionKey string = "function"
   263  const errorKey string = "error"
   264  const gidKey string = "goroutine"
   265  const pidKey string = "pid"
   266  
   267  var ssTransIDKey string = "ss_transid" // transaction ID for sock_swift stuff
   268  
   269  // Potential fields we could add to our logger:
   270  // - IO CONTEXT id, use to track through system, multinode eventually.
   271  //    => will need to add this to all JSON RPCs and all the way through system
   272  //       some sort of context struct that is easily passed around?
   273  //    => this could even help now, to match writes to flushes...
   274  const contextKey string = "context"
   275  const nodeidKey string = "nodeid"     // Node ID of node that generated the log
   276  const inodeNumKey string = "inode"    // Inode number, for package inode logic
   277  const filenameKey string = "filename" // Filename, for package fs logic
   278  
   279  // This struct is an optimization so that package and function are only
   280  // extracted once per function.
   281  type FuncCtx struct {
   282  	funcContext *log.Entry // Struct allows us to save fields common between log calls within a function
   283  }
   284  
   285  // getPackage extracts the package name from the FuncCtx
   286  func (ctx *FuncCtx) getPackage() string {
   287  	pkg, ok := ctx.funcContext.Data[packageKey].(string)
   288  	if ok {
   289  		return pkg
   290  	}
   291  	return ""
   292  }
   293  
   294  // getFunc extracts the function name from the FuncCtx
   295  func (ctx *FuncCtx) getFunc() string {
   296  	fn, ok := ctx.funcContext.Data[functionKey].(string)
   297  	if ok {
   298  		return fn
   299  	}
   300  	return ""
   301  }
   302  
   303  var nullCtx = FuncCtx{funcContext: nil}
   304  
   305  // newFuncCtx creates a new function logging context, extracting the calling
   306  // function from the call stack.
   307  func newFuncCtx(level int) (ctx *FuncCtx) {
   308  
   309  	if disableLoggingForPerfTesting {
   310  		return &nullCtx
   311  	}
   312  
   313  	// Extract package and function from the call stack
   314  	fn, pkg, gid := utils.GetFuncPackage(level + 1)
   315  
   316  	// Get PID as a string (since our PID only changes in fork(2) and
   317  	// clone(2) this could be optimized)
   318  	pid := fmt.Sprint(os.Getpid())
   319  
   320  	// Save fields
   321  	fields := make(log.Fields)
   322  	fields[functionKey] = fn
   323  	fields[packageKey] = pkg
   324  	fields[gidKey] = gid
   325  	fields[pidKey] = pid
   326  
   327  	ctx = &FuncCtx{funcContext: log.WithFields(fields)}
   328  	return ctx
   329  }
   330  
   331  // newFuncCtxWithField creates a new function logging context including a field,
   332  // extracting the calling function from the call stack.
   333  func newFuncCtxWithField(level int, key string, value interface{}) (ctx *FuncCtx) {
   334  
   335  	if disableLoggingForPerfTesting {
   336  		return &nullCtx
   337  	}
   338  
   339  	// Extract package and function from the call stack
   340  	fn, pkg, gid := utils.GetFuncPackage(level + 1)
   341  
   342  	// Save fields
   343  	fields := make(log.Fields)
   344  	fields[key] = value
   345  	fields[functionKey] = fn
   346  	fields[packageKey] = pkg
   347  	fields[gidKey] = gid
   348  
   349  	ctx = &FuncCtx{funcContext: log.WithFields(fields)}
   350  	return ctx
   351  }
   352  
   353  // newFuncCtxWithFields creates a new function logging context including multiple fields,
   354  // extracting the calling function from the call stack.
   355  func newFuncCtxWithFields(level int, fields log.Fields) (ctx *FuncCtx) {
   356  
   357  	if disableLoggingForPerfTesting {
   358  		return &nullCtx
   359  	}
   360  
   361  	// Extract package and function from the call stack
   362  	fn, pkg, gid := utils.GetFuncPackage(level + 1)
   363  
   364  	// add the function and package fields to the ones passed in
   365  	fields[functionKey] = fn
   366  	fields[packageKey] = pkg
   367  	fields[gidKey] = gid
   368  
   369  	ctx = &FuncCtx{funcContext: log.WithFields(fields)}
   370  	return ctx
   371  }
   372  
   373  func newLogEntry(level int) *log.Entry {
   374  
   375  	if disableLoggingForPerfTesting {
   376  		return nil
   377  	}
   378  
   379  	// Extract package and function from the call stack
   380  	fn, pkg, gid := utils.GetFuncPackage(level + 1)
   381  
   382  	// XXX TODO: add setting of nodeid here as well. Where would we get this info from?
   383  
   384  	// Save fields
   385  	fields := make(log.Fields)
   386  	fields[functionKey] = fn
   387  	fields[packageKey] = pkg
   388  	fields[gidKey] = gid
   389  
   390  	return log.WithFields(fields)
   391  }
   392  
   393  var backtraceOneLevel int = 1
   394  
   395  // EXTERNAL logging APIs
   396  // These APIs are in the style of those provided by the logrus package.
   397  
   398  // Logger intentionally does not provide a Debug()  API; use DebugID()  instead.
   399  // Logger intentionally does not provide a Debugf() API; use DebugfID() instead.
   400  // Logger intentionally does not provide a DebugWithError() API; use DebugfWithError() instead.
   401  // Logger intentionally does not provide a DebugfWithError() API; use DebugfIDWithError() instead.
   402  
   403  func logEnabled(level Level) bool {
   404  	if disableLoggingForPerfTesting {
   405  		return false
   406  	}
   407  	if (level == TraceLevel) && !traceLevelEnabled {
   408  		return false
   409  	}
   410  	if (level == DebugLevel) && !debugLevelEnabled {
   411  		return false
   412  	}
   413  	return true
   414  }
   415  
   416  func DebugID(id string, args ...interface{}) {
   417  	level := DebugLevel
   418  	if !logEnabled(level) {
   419  		return
   420  	}
   421  	logString := fmt.Sprint(args...)
   422  	ctx := newFuncCtx(backtraceOneLevel)
   423  	ctx.logWithID(level, id, logString)
   424  }
   425  
   426  func Error(args ...interface{}) {
   427  	level := ErrorLevel
   428  	if !logEnabled(level) {
   429  		return
   430  	}
   431  	logString := fmt.Sprint(args...)
   432  	ctx := newFuncCtx(backtraceOneLevel)
   433  	ctx.log(level, logString)
   434  }
   435  
   436  func Info(args ...interface{}) {
   437  	level := InfoLevel
   438  	if !logEnabled(level) {
   439  		return
   440  	}
   441  	logString := fmt.Sprint(args...)
   442  	ctx := newFuncCtx(backtraceOneLevel)
   443  	ctx.log(level, logString)
   444  }
   445  
   446  func DebugfID(id string, format string, args ...interface{}) {
   447  	level := DebugLevel
   448  	if !logEnabled(level) {
   449  		return
   450  	}
   451  	logString := fmt.Sprintf(format, args...)
   452  	ctx := newFuncCtx(backtraceOneLevel)
   453  	ctx.logWithID(level, id, logString)
   454  }
   455  
   456  func DebugfIDWithTxnID(id string, transIDStr string, format string, args ...interface{}) {
   457  	level := DebugLevel
   458  	if !logEnabled(level) {
   459  		return
   460  	}
   461  	logString := fmt.Sprintf(format, args...)
   462  	ctx := newFuncCtxWithField(backtraceOneLevel, ssTransIDKey, transIDStr)
   463  	ctx.logWithID(level, id, logString)
   464  }
   465  
   466  func Errorf(format string, args ...interface{}) {
   467  	level := ErrorLevel
   468  	if !logEnabled(level) {
   469  		return
   470  	}
   471  	logString := fmt.Sprintf(format, args...)
   472  	ctx := newFuncCtx(backtraceOneLevel)
   473  	ctx.log(level, logString)
   474  }
   475  
   476  func Fatalf(format string, args ...interface{}) {
   477  	level := FatalLevel
   478  	if !logEnabled(level) {
   479  		return
   480  	}
   481  	logString := fmt.Sprintf(format, args...)
   482  	ctx := newFuncCtx(backtraceOneLevel)
   483  	ctx.log(level, logString)
   484  }
   485  
   486  func Infof(format string, args ...interface{}) {
   487  	level := InfoLevel
   488  	if !logEnabled(level) {
   489  		return
   490  	}
   491  	logString := fmt.Sprintf(format, args...)
   492  	ctx := newFuncCtx(backtraceOneLevel)
   493  	ctx.log(level, logString)
   494  }
   495  
   496  func Tracef(format string, args ...interface{}) {
   497  	level := TraceLevel
   498  	if !logEnabled(level) {
   499  		return
   500  	}
   501  	logString := fmt.Sprintf(format, args...)
   502  	ctx := newFuncCtx(backtraceOneLevel)
   503  	ctx.log(level, logString)
   504  }
   505  
   506  func Warnf(format string, args ...interface{}) {
   507  	level := WarnLevel
   508  	if !logEnabled(level) {
   509  		return
   510  	}
   511  	logString := fmt.Sprintf(format, args...)
   512  	ctx := newFuncCtx(backtraceOneLevel)
   513  	ctx.log(level, logString)
   514  }
   515  
   516  func ErrorWithError(err error, args ...interface{}) {
   517  	level := ErrorLevel
   518  	if !logEnabled(level) {
   519  		return
   520  	}
   521  	logString := fmt.Sprint(args...)
   522  	ctx := newFuncCtxWithField(backtraceOneLevel, errorKey, err)
   523  	ctx.log(level, logString)
   524  }
   525  
   526  func FatalWithError(err error, args ...interface{}) {
   527  	level := FatalLevel
   528  	if !logEnabled(level) {
   529  		return
   530  	}
   531  	logString := fmt.Sprint(args...)
   532  	ctx := newFuncCtxWithField(backtraceOneLevel, errorKey, err)
   533  	ctx.log(level, logString)
   534  }
   535  
   536  func InfoWithError(err error, args ...interface{}) {
   537  	level := InfoLevel
   538  	if !logEnabled(level) {
   539  		return
   540  	}
   541  	logString := fmt.Sprint(args...)
   542  	ctx := newFuncCtxWithField(backtraceOneLevel, errorKey, err)
   543  	ctx.log(level, logString)
   544  }
   545  
   546  func TraceWithError(err error, args ...interface{}) {
   547  	level := TraceLevel
   548  	if !logEnabled(level) {
   549  		return
   550  	}
   551  	logString := fmt.Sprint(args...)
   552  	ctx := newFuncCtxWithField(backtraceOneLevel, errorKey, err)
   553  	ctx.log(level, logString)
   554  }
   555  
   556  func WarnWithError(err error, args ...interface{}) {
   557  	level := WarnLevel
   558  	if !logEnabled(level) {
   559  		return
   560  	}
   561  	logString := fmt.Sprint(args...)
   562  	ctx := newFuncCtxWithField(backtraceOneLevel, errorKey, err)
   563  	ctx.log(level, logString)
   564  }
   565  
   566  func DebugfIDWithError(id string, err error, format string, args ...interface{}) {
   567  	level := DebugLevel
   568  	if !logEnabled(level) {
   569  		return
   570  	}
   571  	logString := fmt.Sprintf(format, args...)
   572  	ctx := newFuncCtxWithField(backtraceOneLevel, errorKey, err)
   573  	ctx.logWithID(level, id, logString)
   574  }
   575  
   576  func ErrorfWithError(err error, format string, args ...interface{}) {
   577  	level := ErrorLevel
   578  	if !logEnabled(level) {
   579  		return
   580  	}
   581  	logString := fmt.Sprintf(format, args...)
   582  	ctx := newFuncCtxWithField(backtraceOneLevel, errorKey, err)
   583  	ctx.log(level, logString)
   584  }
   585  
   586  func ErrorfWithErrorTxnID(err error, transIDStr string, format string, args ...interface{}) {
   587  	level := ErrorLevel
   588  	if !logEnabled(level) {
   589  		return
   590  	}
   591  	logString := fmt.Sprintf(format, args...)
   592  	ctx := newFuncCtxWithFields(backtraceOneLevel, log.Fields{errorKey: err, ssTransIDKey: transIDStr})
   593  	ctx.log(level, logString)
   594  }
   595  
   596  func FatalfWithError(err error, format string, args ...interface{}) {
   597  	level := FatalLevel
   598  	if !logEnabled(level) {
   599  		return
   600  	}
   601  	logString := fmt.Sprintf(format, args...)
   602  	ctx := newFuncCtxWithField(backtraceOneLevel, errorKey, err)
   603  	ctx.log(level, logString)
   604  }
   605  
   606  func InfofWithError(err error, format string, args ...interface{}) {
   607  	level := InfoLevel
   608  	if !logEnabled(level) {
   609  		return
   610  	}
   611  	logString := fmt.Sprintf(format, args...)
   612  	ctx := newFuncCtxWithField(backtraceOneLevel, errorKey, err)
   613  	ctx.log(level, logString)
   614  }
   615  
   616  func InfofWithErrorTxnID(err error, transIDStr string, format string, args ...interface{}) {
   617  	level := InfoLevel
   618  	if !logEnabled(level) {
   619  		return
   620  	}
   621  	logString := fmt.Sprintf(format, args...)
   622  	ctx := newFuncCtxWithFields(backtraceOneLevel, log.Fields{errorKey: err, ssTransIDKey: transIDStr})
   623  	ctx.log(level, logString)
   624  }
   625  
   626  func PanicfWithError(err error, format string, args ...interface{}) {
   627  	level := PanicLevel
   628  	if !logEnabled(level) {
   629  		return
   630  	}
   631  	logString := fmt.Sprintf(format, args...)
   632  	ctx := newFuncCtxWithField(backtraceOneLevel, errorKey, err)
   633  	ctx.log(level, logString)
   634  }
   635  
   636  func TracefWithError(err error, format string, args ...interface{}) {
   637  	level := TraceLevel
   638  	if !logEnabled(level) {
   639  		return
   640  	}
   641  	if disableLoggingForPerfTesting || !traceLevelEnabled {
   642  		return
   643  	}
   644  	logString := fmt.Sprintf(format, args...)
   645  	ctx := newFuncCtxWithField(backtraceOneLevel, errorKey, err)
   646  	ctx.log(level, logString)
   647  }
   648  
   649  func WarnfWithError(err error, format string, args ...interface{}) {
   650  	level := WarnLevel
   651  	if !logEnabled(level) {
   652  		return
   653  	}
   654  	logString := fmt.Sprintf(format, args...)
   655  	ctx := newFuncCtxWithField(backtraceOneLevel, errorKey, err)
   656  	ctx.log(level, logString)
   657  }
   658  
   659  func TraceEnter(argsPrefix string, args ...interface{}) (ctx FuncCtx) {
   660  	level := TraceLevel
   661  	if !logEnabled(level) {
   662  		return
   663  	}
   664  
   665  	// We are the first to use the function context, so fill it in.
   666  	ctx.funcContext = newLogEntry(backtraceOneLevel)
   667  
   668  	// Do logging
   669  	ctx.traceEnterInternal(argsPrefix, args...)
   670  
   671  	return ctx
   672  }
   673  
   674  // TraceExit generates a function exit trace with the provided parameters, and using
   675  // the package and function set in FuncCtx (if set).
   676  //
   677  // The implementation of this function assumes that it will be called deferred.
   678  // This assumption only matters in the case where this API is called without having
   679  // called TraceEnter* first, since that is the only case where this function will
   680  // attempt to determine the calling function.
   681  func (ctx *FuncCtx) TraceExit(argsPrefix string, args ...interface{}) {
   682  	level := TraceLevel
   683  	if !logEnabled(level) {
   684  		return
   685  	}
   686  
   687  	// This should really be set already, assuming that one would call TraceEnter before TraceExit.
   688  	// But I don't really want to trigger a panic for just a log call, so we'll check just in case.
   689  	if ctx.funcContext == nil {
   690  		// We are the first to use the function context, so fill it in.
   691  		ctx.funcContext = newLogEntry(2)
   692  	}
   693  
   694  	// Do logging
   695  	ctx.traceExitInternal(argsPrefix, args...)
   696  }
   697  
   698  func (ctx *FuncCtx) TraceExitErr(argsPrefix string, err error, args ...interface{}) {
   699  	level := TraceLevel
   700  	if !logEnabled(level) {
   701  		return
   702  	}
   703  
   704  	// This should really be set already, assuming that one would call TraceEnter before TraceExit.
   705  	// But I don't really want to trigger a panic for just a log call, so we'll check just in case.
   706  	if ctx.funcContext == nil {
   707  		// We are the first to use the function context, so fill it in.
   708  		ctx.funcContext = newLogEntry(2)
   709  
   710  	}
   711  
   712  	// Add error to the fields
   713  	// We throw away the temporary context with the error, in case the
   714  	// context we got called with gets reused.
   715  	newCtx := FuncCtx{funcContext: ctx.funcContext.WithField(errorKey, err)}
   716  
   717  	// Do logging
   718  	newCtx.traceExitInternal(argsPrefix, args...)
   719  }
   720  
   721  func (ctx *FuncCtx) traceInternal(formatPrefix string, argsPrefix string, args ...interface{}) {
   722  
   723  	numParams := len(args)
   724  
   725  	// Build format string
   726  	format := formatPrefix
   727  
   728  	// prepend argsPrefix to the args that were passed in
   729  	newArgs := append([]interface{}{argsPrefix}, args...)
   730  
   731  	// XXX TODO: make more generic, perhaps a for loop to add "%v" based on numParams
   732  	switch numParams {
   733  	case 0:
   734  		format += " %s"
   735  	case 1:
   736  		format += " %s %+v"
   737  	case 2:
   738  		format += " %s %+v %+v"
   739  	case 3:
   740  		format += " %s %+v %+v %+v"
   741  	case 4:
   742  		format += " %s %+v %+v %+v %+v"
   743  	case 5:
   744  		format += " %s %+v %+v %+v %+v %+v"
   745  	case 6:
   746  		format += " %s %+v %+v %+v %+v %+v %+v"
   747  	case 7:
   748  		format += " %s %+v %+v %+v %+v %+v %+v %+v"
   749  	case 8:
   750  		format += " %s %+v %+v %+v %+v %+v %+v %+v %+v"
   751  	case 9:
   752  		format += " %s %+v %+v %+v %+v %+v %+v %+v %+v %+v"
   753  	case 10:
   754  		format += " %s %+v %+v %+v %+v %+v %+v %+v %+v %+v %+v"
   755  	default:
   756  		format += " %s %+v %+v %+v %+v %+v %+v %+v %+v %+v %+v"
   757  	}
   758  	ctx.log(TraceLevel, fmt.Sprintf(format, newArgs...))
   759  }
   760  
   761  func (ctx *FuncCtx) traceEnterInternal(argsPrefix string, args ...interface{}) {
   762  
   763  	ctx.traceInternal(">> called", argsPrefix, args...)
   764  }
   765  
   766  func (ctx *FuncCtx) traceExitInternal(argsPrefix string, args ...interface{}) {
   767  
   768  	ctx.traceInternal("<< returning", argsPrefix, args...)
   769  }
   770  
   771  // log is our equivalent to logrus.entry.go's log function, and is intended to
   772  // be the common low-level logging function used internal to this package.
   773  //
   774  // Following the example of logrus.entry.go's equivalent function, "this function
   775  // is not declared with a pointer value because otherwise race conditions will
   776  // occur when using multiple goroutines"
   777  //
   778  // XXX TODO: It could be convenient to export a non-level-specific interface
   779  //           like this, though logrus specifically does not.
   780  //           Until we understand better why that is, let's leave this as an
   781  //           internal function.
   782  //
   783  func (ctx FuncCtx) log(level Level, args ...interface{}) {
   784  
   785  	// Note that certain levels will always be on, like panic/fatal/error/warn.
   786  
   787  	// Return if trace level not enabled for this package
   788  	if (level == TraceLevel) && !ctx.traceEnabledForPackage() {
   789  		return
   790  	}
   791  	// NOTE: Debug level checking is done in logWithID; all debug logging should
   792  	//       come through that API and not directly to this one.
   793  
   794  	switch level {
   795  	case PanicLevel:
   796  		ctx.funcContext.Panic(args...)
   797  	case FatalLevel:
   798  		ctx.funcContext.Fatal(args...)
   799  	case ErrorLevel:
   800  		ctx.funcContext.Error(args...)
   801  	case WarnLevel:
   802  		ctx.funcContext.Warn(args...)
   803  	case TraceLevel:
   804  		ctx.funcContext.Info(args...)
   805  	case InfoLevel:
   806  		ctx.funcContext.Info(args...)
   807  	case DebugLevel:
   808  		ctx.funcContext.Debug(args...)
   809  	}
   810  }
   811  
   812  func (ctx FuncCtx) logWithID(level Level, id string, args ...interface{}) {
   813  
   814  	if (level == DebugLevel) && !ctx.debugEnabledForPackage(id) {
   815  		//log.Infof("logWithID: NOT logging debug log with id=%v.", id)
   816  		return
   817  	}
   818  
   819  	// Otherwise just call the log API
   820  	ctx.log(level, args...)
   821  }
   822  
   823  // Add another target for log messages to be written to.  writer is an object
   824  // with an io.Writer interface that's called once for each log message.
   825  //
   826  // Logger.Up() must be called before this function is used.
   827  //
   828  func AddLogTarget(writer io.Writer) {
   829  	addLogTarget(writer)
   830  }
   831  
   832  // An example of a log target that captures the most recent n lines of log into
   833  // an array.  Useful for writing test cases.
   834  //
   835  // There should really be a lock or clever RCU mechanism to coordinate
   836  // access/updates to the array, but its not really necessary for test case code
   837  // (and adds overhead).
   838  //
   839  type LogBuffer struct {
   840  	sync.Mutex
   841  	LogEntries   []string // most recent log entry is [0]
   842  	TotalEntries int      // count of all entries seen
   843  }
   844  
   845  type LogTarget struct {
   846  	LogBuf *LogBuffer
   847  }
   848  
   849  // Initialize a LogTarget to hold the last nEntry log entries.
   850  //
   851  func (log *LogTarget) Init(nEntry int) {
   852  	log.LogBuf = &LogBuffer{TotalEntries: 0}
   853  	log.LogBuf.LogEntries = make([]string, nEntry)
   854  }
   855  
   856  // Called by logger for each log entry
   857  //
   858  func (log LogTarget) Write(p []byte) (n int, err error) {
   859  	return log.write(p)
   860  }
   861  
   862  // Given a log entry captured by LogTarget (above) parse out the various fields
   863  // and return them as a map.
   864  //
   865  // The fields parsed are "time", "level", "msg", "error", "function",
   866  // "goroutine", and "package".  Not all fields are present in every message,
   867  // in which case the field name is not a key in the map.
   868  //
   869  func ParseLogEntry(entry string) (map[string]string, error) {
   870  	return parseLogEntry(entry)
   871  }
   872  
   873  // Parse the log entries, starting with the most recent, looking for a message
   874  // generated by a function named funcName and a "msg=" that matches the regular
   875  // expression logEntryRE, within the most recent maxEntries lines of the log.
   876  //
   877  // If found, return the parsed fields from the log message, which are a
   878  // combination of the fields returned by ParseLogEntry() and the fields in the
   879  // passed regular expression (which must use names for the matching parts).
   880  // funcName must match the contents of the field "function" returned by
   881  // ParseLogEntry().  entryIdx is the index of the entry in the log with 0 being
   882  // the most recent.
   883  //
   884  // If not found, return an error.
   885  //
   886  // Example: logEntryRE can be very simple, but here is a complicated one from
   887  // trackedlock/api_test.go that matches the following message (where "\n" in the
   888  // message represents embededded newlines).
   889  //
   890  // msg="trackedlock watcher: *trackedlock.Mutex at 0xc420110000 locked for 2.0 sec; stack at Lock() call:\ngoroutine 19 [running]:..."
   891  //
   892  // logEntryRe is `^trackedlock watcher: (?P<type>[*a-zA-Z0-9_.]+) at (?P<ptr>0x[0-9a-f]+) locked for (?P<time>[0-9.]+) sec rank 0; stack at call to (?P<locker>[a-zA-Z0-9_()]+):\\n(?P<lockStack>.*)$`
   893  //
   894  func ParseLogForFunc(logcopy LogTarget, funcName string, logEntryRE *regexp.Regexp, maxEntries int) (fields map[string]string, entryIdx int, err error) {
   895  	return parseLogForFunc(logcopy, funcName, logEntryRE, maxEntries)
   896  }