2
0

panic.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. package errors
  2. import (
  3. "fmt"
  4. "net/http"
  5. "runtime"
  6. )
  7. //--------------------------------------------------------------------------
  8. // PanicType
  9. //--------------------------------------------------------------------------
  10. // PanicType defines the context in which the panic occurred
  11. type PanicType int
  12. const (
  13. PanicTypeDefault PanicType = iota
  14. PanicTypeHTTP
  15. )
  16. // The string representation of PanicContext
  17. func (pt PanicType) String() string {
  18. return []string{"PanicTypeDefault", "PanicTypeHTTP"}[pt]
  19. }
  20. //--------------------------------------------------------------------------
  21. // Panic
  22. //--------------------------------------------------------------------------
  23. // Panic represents a panic that occurred, captured by a recovery.
  24. type Panic struct {
  25. Error interface{}
  26. Stack string
  27. Type PanicType
  28. }
  29. // PanicHandler is a func that receives a Panic and returns a bool representing whether or not
  30. // the panic should recover or not.
  31. type PanicHandler = func(p Panic) bool
  32. var (
  33. enabled = false
  34. dispatcher = make(chan Panic)
  35. )
  36. // SetPanicHandler sets the handler that is executed when any panic is captured by
  37. // HandlePanic(). Without setting a handler, the panic reporting is disabled.
  38. func SetPanicHandler(handler PanicHandler) error {
  39. if enabled {
  40. return fmt.Errorf("Panic Handler has already been set")
  41. }
  42. enabled = true
  43. // Setup a go routine which receives via the panic channel, passes
  44. // resulting Panic to the handler passed.
  45. go func() {
  46. for {
  47. p := <-dispatcher
  48. // If we do not wish to recover, panic using same error
  49. if !handler(p) {
  50. panic(p.Error)
  51. }
  52. }
  53. }()
  54. return nil
  55. }
  56. // PanicHandlerMiddleware should wrap any of the http handlers to capture panics.
  57. func PanicHandlerMiddleware(handler http.Handler) http.Handler {
  58. return http.HandlerFunc(func(rw http.ResponseWriter, rq *http.Request) {
  59. defer HandleHTTPPanic(rw, rq)
  60. handler.ServeHTTP(rw, rq)
  61. })
  62. }
  63. // HandlePanic should be executed in a deferred method (or deferred directly). It will
  64. // capture any panics that occur in the goroutine it exists, and report to the registered
  65. // global panic handler.
  66. func HandlePanic() {
  67. // NOTE: For each "special" type of panic that is added, you must repeat this pattern. The recover()
  68. // NOTE: call cannot exist in a func outside of the deferred func.
  69. if !enabled {
  70. return
  71. }
  72. if err := recover(); err != nil {
  73. dispatch(err, PanicTypeDefault)
  74. }
  75. }
  76. // HandleHTTPPanic should be executed in a deferred method (or deferred directly) in http middleware.
  77. // It will capture any panics that occur in the goroutine it exists, and report to the registered
  78. // global panic handler. HTTP handler panics will have the errors.PanicTypeHTTP Type.
  79. func HandleHTTPPanic(rw http.ResponseWriter, rq *http.Request) {
  80. // NOTE: For each "special" type of panic that is added, you must repeat this pattern. The recover()
  81. // NOTE: call cannot exist in a func outside of the deferred func.
  82. if !enabled {
  83. return
  84. }
  85. if err := recover(); err != nil {
  86. rw.WriteHeader(http.StatusInternalServerError)
  87. dispatch(err, PanicTypeHTTP)
  88. }
  89. }
  90. // generate stacktrace, dispatch the panic via channel
  91. func dispatch(err interface{}, panicType PanicType) {
  92. stack := make([]byte, 1024*8)
  93. stack = stack[:runtime.Stack(stack, false)]
  94. dispatcher <- Panic{
  95. Error: err,
  96. Stack: string(stack),
  97. Type: panicType,
  98. }
  99. }