log.go 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. package framework
  2. import (
  3. "bytes"
  4. "fmt"
  5. "regexp"
  6. "runtime/debug"
  7. "time"
  8. "github.com/onsi/ginkgo"
  9. e2eginkgowrapper "github.com/fatedier/frp/test/e2e/framework/ginkgowrapper"
  10. )
  11. func nowStamp() string {
  12. return time.Now().Format(time.StampMilli)
  13. }
  14. func log(level string, format string, args ...interface{}) {
  15. fmt.Fprintf(ginkgo.GinkgoWriter, nowStamp()+": "+level+": "+format+"\n", args...)
  16. }
  17. // Logf logs the info.
  18. func Logf(format string, args ...interface{}) {
  19. log("INFO", format, args...)
  20. }
  21. // Failf logs the fail info, including a stack trace.
  22. func Failf(format string, args ...interface{}) {
  23. FailfWithOffset(1, format, args...)
  24. }
  25. // FailfWithOffset calls "Fail" and logs the error with a stack trace that starts at "offset" levels above its caller
  26. // (for example, for call chain f -> g -> FailfWithOffset(1, ...) error would be logged for "f").
  27. func FailfWithOffset(offset int, format string, args ...interface{}) {
  28. msg := fmt.Sprintf(format, args...)
  29. skip := offset + 1
  30. log("FAIL", "%s\n\nFull Stack Trace\n%s", msg, PrunedStack(skip))
  31. e2eginkgowrapper.Fail(nowStamp()+": "+msg, skip)
  32. }
  33. // Fail is a replacement for ginkgo.Fail which logs the problem as it occurs
  34. // together with a stack trace and then calls ginkgowrapper.Fail.
  35. func Fail(msg string, callerSkip ...int) {
  36. skip := 1
  37. if len(callerSkip) > 0 {
  38. skip += callerSkip[0]
  39. }
  40. log("FAIL", "%s\n\nFull Stack Trace\n%s", msg, PrunedStack(skip))
  41. e2eginkgowrapper.Fail(nowStamp()+": "+msg, skip)
  42. }
  43. var codeFilterRE = regexp.MustCompile(`/github.com/onsi/ginkgo/`)
  44. // PrunedStack is a wrapper around debug.Stack() that removes information
  45. // about the current goroutine and optionally skips some of the initial stack entries.
  46. // With skip == 0, the returned stack will start with the caller of PruneStack.
  47. // From the remaining entries it automatically filters out useless ones like
  48. // entries coming from Ginkgo.
  49. //
  50. // This is a modified copy of PruneStack in https://github.com/onsi/ginkgo/blob/f90f37d87fa6b1dd9625e2b1e83c23ffae3de228/internal/codelocation/code_location.go#L25:
  51. // - simplified API and thus renamed (calls debug.Stack() instead of taking a parameter)
  52. // - source code filtering updated to be specific to Kubernetes
  53. // - optimized to use bytes and in-place slice filtering from
  54. // https://github.com/golang/go/wiki/SliceTricks#filter-in-place
  55. func PrunedStack(skip int) []byte {
  56. fullStackTrace := debug.Stack()
  57. stack := bytes.Split(fullStackTrace, []byte("\n"))
  58. // Ensure that the even entries are the method names and the
  59. // the odd entries the source code information.
  60. if len(stack) > 0 && bytes.HasPrefix(stack[0], []byte("goroutine ")) {
  61. // Ignore "goroutine 29 [running]:" line.
  62. stack = stack[1:]
  63. }
  64. // The "+2" is for skipping over:
  65. // - runtime/debug.Stack()
  66. // - PrunedStack()
  67. skip += 2
  68. if len(stack) > 2*skip {
  69. stack = stack[2*skip:]
  70. }
  71. n := 0
  72. for i := 0; i < len(stack)/2; i++ {
  73. // We filter out based on the source code file name.
  74. if !codeFilterRE.Match([]byte(stack[i*2+1])) {
  75. stack[n] = stack[i*2]
  76. stack[n+1] = stack[i*2+1]
  77. n += 2
  78. }
  79. }
  80. stack = stack[:n]
  81. return bytes.Join(stack, []byte("\n"))
  82. }