panic.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  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
  37. func SetPanicHandler(handler PanicHandler) error {
  38. if enabled {
  39. return fmt.Errorf("Panic Handler has already been set")
  40. }
  41. enabled = true
  42. // Setup a go routine which receives via the panic channel, passes
  43. // resulting Panic to the handler passed.
  44. go func() {
  45. for {
  46. p := <-dispatcher
  47. // If we do not wish to recover, panic using same error
  48. if !handler(p) {
  49. panic(p.Error)
  50. }
  51. }
  52. }()
  53. return nil
  54. }
  55. // PanicHandlerMiddleware should wrap any of the http handlers to capture panics.
  56. func PanicHandlerMiddleware(handler http.Handler) http.Handler {
  57. return http.HandlerFunc(func(rw http.ResponseWriter, rq *http.Request) {
  58. defer HandleHTTPPanic(rw, rq)
  59. handler.ServeHTTP(rw, rq)
  60. })
  61. }
  62. // HandlePanic should be executed in a deferred method (or deferred directly). It will
  63. // capture any panics that occur in the goroutine it exists, and report to the registered
  64. // global panic handler.
  65. func HandlePanic() {
  66. // NOTE: For each "special" type of panic that is added, you must repeat this pattern. The recover()
  67. // NOTE: call cannot exist in a func outside of the deferred func.
  68. if !enabled {
  69. return
  70. }
  71. if err := recover(); err != nil {
  72. dispatch(err, PanicTypeDefault)
  73. }
  74. }
  75. // HandleHTTPPanic should be executed in a deferred method (or deferred directly) in http middleware.
  76. // It will capture any panics that occur in the goroutine it exists, and report to the registered
  77. // global panic handler. HTTP handler panics will have the errors.PanicTypeHTTP Type.
  78. func HandleHTTPPanic(rw http.ResponseWriter, rq *http.Request) {
  79. // NOTE: For each "special" type of panic that is added, you must repeat this pattern. The recover()
  80. // NOTE: call cannot exist in a func outside of the deferred func.
  81. if !enabled {
  82. return
  83. }
  84. if err := recover(); err != nil {
  85. rw.WriteHeader(http.StatusInternalServerError)
  86. dispatch(err, PanicTypeHTTP)
  87. }
  88. }
  89. // generate stacktrace, dispatch the panic via channel
  90. func dispatch(err interface{}, panicType PanicType) {
  91. stack := make([]byte, 1024*8)
  92. stack = stack[:runtime.Stack(stack, false)]
  93. dispatcher <- Panic{
  94. Error: err,
  95. Stack: string(stack),
  96. Type: panicType,
  97. }
  98. }