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 }