go.dedis.ch/onet/v4@v4.0.0-pre1/log/lvl.go (about)

     1  package log
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"regexp"
     9  	"runtime"
    10  	"runtime/pprof"
    11  	"strconv"
    12  	"sync"
    13  	"testing"
    14  	"time"
    15  
    16  	"golang.org/x/xerrors"
    17  )
    18  
    19  const (
    20  	lvlWarning = iota - 20
    21  	lvlError
    22  	lvlFatal
    23  	lvlPanic
    24  	lvlInfo
    25  	lvlPrint
    26  )
    27  
    28  // These formats can be used in place of the debugVisible
    29  const (
    30  	// FormatPython uses [x] and others to indicate what is shown
    31  	FormatPython = -1
    32  	// FormatNone is just pure print
    33  	FormatNone = 0
    34  )
    35  
    36  // defaultMainTest indicates what debug-level should be used when `go test -v`
    37  // is called.
    38  const defaultMainTest = 2
    39  
    40  // MainTestWait is the maximum time the MainTest-method waits before aborting
    41  // and printing a stack-trace of all functions.
    42  // This is deprecated and should not be used anymore.
    43  var MainTestWait = 0 * time.Minute
    44  
    45  // NamePadding - the padding of functions to make a nice debug-output - this is automatically updated
    46  // whenever there are longer functions and kept at that new maximum. If you prefer
    47  // to have a fixed output and don't remember oversized names, put a negative value
    48  // in here.
    49  var NamePadding = 40
    50  
    51  // LinePadding of line-numbers for a nice debug-output - used in the same way as
    52  // NamePadding.
    53  // Deprecated: the caller is merged thus no line padding is necessary anymore.
    54  var LinePadding = 3
    55  
    56  // StaticMsg - if this variable is set, it will be outputted between the
    57  // position and the message.
    58  var StaticMsg = ""
    59  
    60  // outputLines can be false to suppress outputting of lines in tests.
    61  var outputLines = true
    62  
    63  var debugMut sync.RWMutex
    64  
    65  var regexpPaths, _ = regexp.Compile(".*/")
    66  
    67  func init() {
    68  	stdLogger, err := newStdLogger()
    69  	if err != nil {
    70  		panic(err)
    71  	}
    72  	stdKey := RegisterLogger(stdLogger)
    73  	if stdKey != 0 {
    74  		panic(xerrors.New("Cannot add a logger before the standard logger"))
    75  	}
    76  	ParseEnv()
    77  }
    78  
    79  func lvl(lvl, skip int, args ...interface{}) {
    80  	debugMut.Lock()
    81  	defer debugMut.Unlock()
    82  	for _, l := range loggers {
    83  		// Get the *LoggerInfo that contains how should the formatting go.
    84  		lInfo := l.GetLoggerInfo()
    85  
    86  		if lvl > lInfo.DebugLvl {
    87  			continue
    88  		}
    89  
    90  		pc, fn, line, _ := runtime.Caller(skip)
    91  		funcName := filepath.Base(runtime.FuncForPC(pc).Name())
    92  		name := fn
    93  		if !lInfo.AbsoluteFilePath {
    94  			name = filepath.Base(fn)
    95  		}
    96  
    97  		// For the testing-framework, we check the resulting string. So as not to
    98  		// have the tests fail every time somebody moves the functions, we put
    99  		// the line-# to 0
   100  		if !outputLines {
   101  			line = 0
   102  			name = "fake_name.go"
   103  		}
   104  
   105  		caller := fmt.Sprintf("%s:%d (%s)", name, line, funcName)
   106  
   107  		if lInfo.UseColors {
   108  			// Only adjust the name and line padding if we also have color.
   109  			if len(caller) > NamePadding && NamePadding > 0 {
   110  				NamePadding = len(caller)
   111  			}
   112  		}
   113  
   114  		namePadding := 0
   115  		if lInfo.Padding {
   116  			namePadding = NamePadding
   117  		}
   118  		if StaticMsg != "" {
   119  			caller += "@" + StaticMsg
   120  		}
   121  		message := fmt.Sprintln(args...)
   122  
   123  		lvlAbs := lvl
   124  		if lvl < 0 {
   125  			lvlAbs *= -1
   126  		}
   127  		lvlStr := strconv.Itoa(lvlAbs)
   128  		if lvl < 0 {
   129  			lvlStr += "!"
   130  		}
   131  		switch lvl {
   132  		case lvlPrint:
   133  			lvlStr = "I"
   134  		case lvlInfo:
   135  			lvlStr = "I"
   136  		case lvlWarning:
   137  			lvlStr = "W"
   138  		case lvlError:
   139  			lvlStr = "E"
   140  		case lvlFatal:
   141  			lvlStr = "F"
   142  		case lvlPanic:
   143  			lvlStr = "P"
   144  		}
   145  		fmtstr := fmt.Sprintf(": %%-%ds - %%s", namePadding)
   146  		str := fmt.Sprintf(fmtstr, caller, message)
   147  		if lInfo.ShowTime {
   148  			ti := time.Now()
   149  			str = fmt.Sprintf("%s.%09d%s", ti.Format("06/02/01 15:04:05"), ti.Nanosecond(), str)
   150  		}
   151  		str = fmt.Sprintf("%-2s%s", lvlStr, str)
   152  
   153  		l.Log(lvl, str)
   154  	}
   155  }
   156  
   157  // Needs two functions to keep the caller-depth the same and find who calls us
   158  // Lvlf1 -> Lvlf -> lvl
   159  // or
   160  // Lvl1 -> lvld -> lvl
   161  func lvlf(l int, f string, args ...interface{}) {
   162  	if l > DebugVisible() {
   163  		return
   164  	}
   165  	lvl(l, 3, fmt.Sprintf(f, args...))
   166  }
   167  func lvld(l int, args ...interface{}) {
   168  	lvl(l, 3, args...)
   169  }
   170  
   171  // Lvl1 debug output is informational and always displayed
   172  func Lvl1(args ...interface{}) {
   173  	lvld(1, args...)
   174  }
   175  
   176  // Lvl2 is more verbose but doesn't spam the stdout in case
   177  // there is a big simulation
   178  func Lvl2(args ...interface{}) {
   179  	lvld(2, args...)
   180  }
   181  
   182  // Lvl3 gives debug-output that can make it difficult to read
   183  // for big simulations with more than 100 hosts
   184  func Lvl3(args ...interface{}) {
   185  	lvld(3, args...)
   186  }
   187  
   188  // Lvl4 is only good for test-runs with very limited output
   189  func Lvl4(args ...interface{}) {
   190  	lvld(4, args...)
   191  }
   192  
   193  // Lvl5 is for big data
   194  func Lvl5(args ...interface{}) {
   195  	lvld(5, args...)
   196  }
   197  
   198  // Lvlf1 is like Lvl1 but with a format-string
   199  func Lvlf1(f string, args ...interface{}) {
   200  	lvlf(1, f, args...)
   201  }
   202  
   203  // Lvlf2 is like Lvl2 but with a format-string
   204  func Lvlf2(f string, args ...interface{}) {
   205  	lvlf(2, f, args...)
   206  }
   207  
   208  // Lvlf3 is like Lvl3 but with a format-string
   209  func Lvlf3(f string, args ...interface{}) {
   210  	lvlf(3, f, args...)
   211  }
   212  
   213  // Lvlf4 is like Lvl4 but with a format-string
   214  func Lvlf4(f string, args ...interface{}) {
   215  	lvlf(4, f, args...)
   216  }
   217  
   218  // Lvlf5 is like Lvl5 but with a format-string
   219  func Lvlf5(f string, args ...interface{}) {
   220  	lvlf(5, f, args...)
   221  }
   222  
   223  // LLvl1 *always* prints
   224  func LLvl1(args ...interface{}) { lvld(-1, args...) }
   225  
   226  // LLvl2 *always* prints
   227  func LLvl2(args ...interface{}) { lvld(-2, args...) }
   228  
   229  // LLvl3 *always* prints
   230  func LLvl3(args ...interface{}) { lvld(-3, args...) }
   231  
   232  // LLvl4 *always* prints
   233  func LLvl4(args ...interface{}) { lvld(-4, args...) }
   234  
   235  // LLvl5 *always* prints
   236  func LLvl5(args ...interface{}) { lvld(-5, args...) }
   237  
   238  // LLvlf1 *always* prints
   239  func LLvlf1(f string, args ...interface{}) { lvlf(-1, f, args...) }
   240  
   241  // LLvlf2 *always* prints
   242  func LLvlf2(f string, args ...interface{}) { lvlf(-2, f, args...) }
   243  
   244  // LLvlf3 *always* prints
   245  func LLvlf3(f string, args ...interface{}) { lvlf(-3, f, args...) }
   246  
   247  // LLvlf4 *always* prints
   248  func LLvlf4(f string, args ...interface{}) { lvlf(-4, f, args...) }
   249  
   250  // LLvlf5 *always* prints
   251  func LLvlf5(f string, args ...interface{}) { lvlf(-5, f, args...) }
   252  
   253  // TestOutput sets the DebugVisible to 0 if 'show'
   254  // is false, else it will set DebugVisible to 'level'
   255  //
   256  // Usage: TestOutput( test.Verbose(), 2 )
   257  func TestOutput(show bool, level int) {
   258  	debugMut.Lock()
   259  	defer debugMut.Unlock()
   260  
   261  	if show {
   262  		loggers[0].GetLoggerInfo().DebugLvl = level
   263  	} else {
   264  		loggers[0].GetLoggerInfo().DebugLvl = 0
   265  	}
   266  }
   267  
   268  // SetDebugVisible set the global debug output level in a go-rountine-safe way
   269  func SetDebugVisible(lvl int) {
   270  	debugMut.Lock()
   271  	defer debugMut.Unlock()
   272  	loggers[0].GetLoggerInfo().DebugLvl = lvl
   273  }
   274  
   275  // DebugVisible returns the actual visible debug-level
   276  func DebugVisible() int {
   277  	debugMut.RLock()
   278  	defer debugMut.RUnlock()
   279  	return loggers[0].GetLoggerInfo().DebugLvl
   280  }
   281  
   282  // SetShowTime allows for turning on the flag that adds the current
   283  // time to the debug-output
   284  func SetShowTime(show bool) {
   285  	debugMut.Lock()
   286  	defer debugMut.Unlock()
   287  	loggers[0].GetLoggerInfo().ShowTime = show
   288  }
   289  
   290  // ShowTime returns the current setting for showing the time in the debug
   291  // output
   292  func ShowTime() bool {
   293  	debugMut.Lock()
   294  	defer debugMut.Unlock()
   295  	return loggers[0].GetLoggerInfo().ShowTime
   296  }
   297  
   298  // SetAbsoluteFilePath allows to print the absolute file path
   299  // for logging calls.
   300  func SetAbsoluteFilePath(absolute bool) {
   301  	debugMut.Lock()
   302  	defer debugMut.Unlock()
   303  	loggers[0].GetLoggerInfo().AbsoluteFilePath = absolute
   304  }
   305  
   306  // AbsoluteFilePath returns the current setting for showing the absolute
   307  // path inside a log.
   308  func AbsoluteFilePath() bool {
   309  	debugMut.Lock()
   310  	defer debugMut.Unlock()
   311  	return loggers[0].GetLoggerInfo().AbsoluteFilePath
   312  }
   313  
   314  // SetUseColors can turn off or turn on the use of colors in the debug-output
   315  func SetUseColors(useColors bool) {
   316  	debugMut.Lock()
   317  	defer debugMut.Unlock()
   318  	loggers[0].GetLoggerInfo().UseColors = useColors
   319  }
   320  
   321  // UseColors returns the actual setting of the color-usage in log
   322  func UseColors() bool {
   323  	debugMut.Lock()
   324  	defer debugMut.Unlock()
   325  	return loggers[0].GetLoggerInfo().UseColors
   326  }
   327  
   328  // SetPadding can turn off or turn on the use of padding in log
   329  func SetPadding(padding bool) {
   330  	debugMut.Lock()
   331  	defer debugMut.Unlock()
   332  	loggers[0].GetLoggerInfo().Padding = padding
   333  }
   334  
   335  // Padding returns the actual setting of the padding in log
   336  func Padding() bool {
   337  	debugMut.Lock()
   338  	defer debugMut.Unlock()
   339  	return loggers[0].GetLoggerInfo().Padding
   340  }
   341  
   342  // MainTest can be called from TestMain. It will parse the flags and
   343  // set the DebugVisible to defaultMainTest, then run the tests and check for
   344  // remaining go-routines.
   345  // If you give it an integer as optional parameter, it will set the
   346  // debig-lvl to that integer. As `go test` will only output whenever
   347  // `-v` is given, this gives no disadvantage over setting the default
   348  // output-level.
   349  func MainTest(m *testing.M, ls ...int) {
   350  	flag.Parse()
   351  	l := defaultMainTest
   352  	if len(ls) > 0 {
   353  		l = ls[0]
   354  	}
   355  	TestOutput(testing.Verbose(), l)
   356  	done := make(chan int)
   357  	go func() {
   358  		code := m.Run()
   359  		done <- code
   360  	}()
   361  	select {
   362  	case code := <-done:
   363  		if code == 0 {
   364  			AfterTest(nil)
   365  		}
   366  		os.Exit(code)
   367  	case <-time.After(interpretWait()):
   368  		Error("Didn't finish in time")
   369  		pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
   370  		os.Exit(1)
   371  	}
   372  }
   373  
   374  // ParseEnv looks at the following environment-variables:
   375  //   DEBUG_LVL - for the actual debug-lvl - default is 1
   376  //   DEBUG_TIME - whether to show the timestamp - default is false
   377  //   DEBUG_COLOR - whether to color the output - default is false
   378  //   DEBUG_PADDING - whether to pad the output nicely - default is true
   379  func ParseEnv() {
   380  	dv := os.Getenv("DEBUG_LVL")
   381  	if dv != "" {
   382  		dvInt, err := strconv.Atoi(dv)
   383  		Lvl3("Setting level to", dv, dvInt, err)
   384  		SetDebugVisible(dvInt)
   385  		if err != nil {
   386  			Error("Couldn't convert", dv, "to debug-level")
   387  		}
   388  	}
   389  
   390  	dt := os.Getenv("DEBUG_TIME")
   391  	if dt != "" {
   392  		dtInt, err := strconv.ParseBool(dt)
   393  		Lvl3("Setting showTime to", dt, dtInt, err)
   394  		SetShowTime(dtInt)
   395  		if err != nil {
   396  			Error("Couldn't convert", dt, "to boolean")
   397  		}
   398  	}
   399  
   400  	dfp := os.Getenv("DEBUG_FILEPATH")
   401  	if dfp != "" {
   402  		dfpInt, err := strconv.ParseBool(dfp)
   403  		Lvl3("Setting absoluteFilePath to", dfp, dfpInt, err)
   404  		SetAbsoluteFilePath(dfpInt)
   405  		if err != nil {
   406  			Error("Couldn't convert", dfp, "to boolean")
   407  		}
   408  	}
   409  
   410  	dc := os.Getenv("DEBUG_COLOR")
   411  	if dc != "" {
   412  		ucInt, err := strconv.ParseBool(dc)
   413  		Lvl3("Setting useColor to", dc, ucInt, err)
   414  		SetUseColors(ucInt)
   415  		if err != nil {
   416  			Error("Couldn't convert", dc, "to boolean")
   417  		}
   418  	}
   419  
   420  	dp := os.Getenv("DEBUG_PADDING")
   421  	if dp != "" {
   422  		dpBool, err := strconv.ParseBool(dp)
   423  		Lvl3("Setting padding to", dp, dpBool, err)
   424  		SetPadding(dpBool)
   425  		if err != nil {
   426  			Error("Couldn't convert", dp, "to boolean")
   427  		}
   428  	}
   429  }
   430  
   431  // RegisterFlags adds the flags and the variables for the debug-control
   432  // (standard logger) using the standard flag-package.
   433  func RegisterFlags() {
   434  	ParseEnv()
   435  	defaultDebugLvl := DebugVisible()
   436  	defaultShowTime := ShowTime()
   437  	defaultUseColors := UseColors()
   438  	defaultPadding := Padding()
   439  	debugMut.Lock()
   440  	defer debugMut.Unlock()
   441  	flag.IntVar(&loggers[0].GetLoggerInfo().DebugLvl, "debug", defaultDebugLvl, "Change debug level (0-5)")
   442  	flag.BoolVar(&loggers[0].GetLoggerInfo().ShowTime, "debug-time", defaultShowTime, "Shows the time of each message")
   443  	flag.BoolVar(&loggers[0].GetLoggerInfo().UseColors, "debug-color", defaultUseColors, "Colors each message")
   444  	flag.BoolVar(&loggers[0].GetLoggerInfo().Padding, "debug-padding", defaultPadding, "Pads each message nicely")
   445  	flag.BoolVar(&loggers[0].GetLoggerInfo().AbsoluteFilePath, "debug-filepath", DefaultStdAbsoluteFilePath, "extends the filename with the absolute path")
   446  }
   447  
   448  var timeoutFlagMutex sync.Mutex
   449  
   450  // interpretWait will use the test.timeout flag and MainTestWait to get
   451  // the time to wait. From highest preference to lowest preference:
   452  //  1. "-timeout 1m"
   453  //  2. MainTestWait = 2*time.Minute
   454  //  3. 10*time.Minute
   455  // interpretWait will throw a warning if MainTestWait has been set.
   456  func interpretWait() time.Duration {
   457  	timeoutFlagMutex.Lock()
   458  	defer timeoutFlagMutex.Unlock()
   459  	toFlag := flag.Lookup("test.timeout")
   460  	if toFlag == nil {
   461  		Fatal("MainTest should not be called outside of tests")
   462  	}
   463  	dur := MainTestWait
   464  	var err error
   465  	if dur != 0*time.Second {
   466  		Warn("Usage of MainTestWait is deprecated!")
   467  	} else {
   468  		dur = 10 * time.Minute
   469  	}
   470  	if toStr := toFlag.Value.String(); toStr != "0s" {
   471  		dur, err = time.ParseDuration(toStr)
   472  		ErrFatal(err, "couldn't parse passed timeout value")
   473  	}
   474  	return dur
   475  }