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 }