runtime.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package runtime
  15. import (
  16. "fmt"
  17. "runtime"
  18. "sync"
  19. "time"
  20. "yunion.io/x/log"
  21. )
  22. var (
  23. // ReallyCrash controls the behavior of HandleCrash and now defaults
  24. // true. It's still exposed so components can optionally set to false
  25. // to restore prior behavior.
  26. ReallyCrash = true
  27. )
  28. // PanicHandlers is a list of functions which will be invoked when a panic happens.
  29. var PanicHandlers = []func(interface{}){logPanic}
  30. // HandleCrash simply catches a crash and logs an error. Meant to be called via
  31. // defer. Additional context-specific handlers can be provided, and will be
  32. // called in case of panic. HandleCrash actually crashes, after calling the
  33. // handlers and logging the panic message.
  34. func HandleCrash(additionalHandlers ...func(interface{})) {
  35. if r := recover(); r != nil {
  36. for _, fn := range PanicHandlers {
  37. fn(r)
  38. }
  39. for _, fn := range additionalHandlers {
  40. fn(r)
  41. }
  42. if ReallyCrash {
  43. // Actually proceed to panic.
  44. panic(r)
  45. }
  46. }
  47. }
  48. // logPanic logs the caller tree when a panic occurs.
  49. func logPanic(r interface{}) {
  50. callers := getCallers(r)
  51. log.Errorf("Observed a panic: %#v (%v)\n%v", r, r, callers)
  52. }
  53. func getCallers(r interface{}) string {
  54. callers := ""
  55. for i := 0; true; i++ {
  56. _, file, line, ok := runtime.Caller(i)
  57. if !ok {
  58. break
  59. }
  60. callers = callers + fmt.Sprintf("%v:%v\n", file, line)
  61. }
  62. return callers
  63. }
  64. // ErrorHandlers is a list of functions which will be invoked when an unreturnable
  65. // error occurs.
  66. var ErrorHandlers = []func(error){
  67. logError,
  68. (&rudimentaryErrorBackoff{
  69. lastErrorTime: time.Now(),
  70. // 1ms was the number folks were able to stomach as a global rate limit.
  71. // If you need to log errors more than 1000 times a second you
  72. // should probably consider fixing your code instead. :)
  73. minPeriod: time.Millisecond,
  74. }).OnError,
  75. }
  76. // HandlerError is a method to invoke when a non-user facing piece of code cannot
  77. // return an error and needs to indicate it has been ignored. Invoking this method
  78. // is preferable to logging the error - the default behavior is to log but the
  79. // errors may be sent to a remote server for analysis.
  80. func HandleError(err error) {
  81. // this is sometimes called with a nil error. We probably shouldn't fail and should do nothing instead
  82. if err == nil {
  83. return
  84. }
  85. for _, fn := range ErrorHandlers {
  86. fn(err)
  87. }
  88. }
  89. // logError prints an error with the call stack of the location it was reported
  90. func logError(err error) {
  91. log.Errorf("logError: %v", err)
  92. }
  93. type rudimentaryErrorBackoff struct {
  94. minPeriod time.Duration // immutable
  95. lastErrorTimeLock sync.Mutex
  96. lastErrorTime time.Time
  97. }
  98. // OnError will block if it is called more often than the embedded period time.
  99. // This will prevent overly tight hot error loops.
  100. func (r *rudimentaryErrorBackoff) OnError(error) {
  101. r.lastErrorTimeLock.Lock()
  102. defer r.lastErrorTimeLock.Unlock()
  103. d := time.Since(r.lastErrorTime)
  104. if d < r.minPeriod {
  105. time.Sleep(r.minPeriod - d)
  106. }
  107. r.lastErrorTime = time.Now()
  108. }
  109. // GetCaller returns the caller of the function that calls it.
  110. func GetCaller() string {
  111. var pc [1]uintptr
  112. runtime.Callers(3, pc[:])
  113. f := runtime.FuncForPC(pc[0])
  114. if f == nil {
  115. return fmt.Sprintf("Unable to find caller")
  116. }
  117. return f.Name()
  118. }
  119. // RecoverFromPanic replaces the specified error with an error containing the
  120. // original error, and the call tree when a panic occurs. This enables error
  121. // handlers to handle errors and panics the same way.
  122. func RecoverFromPanic(err *error) {
  123. if r := recover(); r != nil {
  124. callers := getCallers(r)
  125. *err = fmt.Errorf(
  126. "recovered from panic %q. (err=%v) Call stack:\n%v",
  127. r,
  128. *err,
  129. callers)
  130. }
  131. }