panic.go 1.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. package errors
  2. import (
  3. "fmt"
  4. "runtime"
  5. )
  6. // Panic represents a panic that occurred, captured by a recovery.
  7. type Panic struct {
  8. Error interface{}
  9. Stack string
  10. }
  11. // PanicHandler is a func that receives a Panic and returns a bool representing whether or not
  12. // the panic should recover or not.
  13. type PanicHandler = func(p Panic) bool
  14. var (
  15. enabled = false
  16. dispatcher = make(chan Panic)
  17. )
  18. // SetPanicHandler sets the handler that is executed when
  19. func SetPanicHandler(handler PanicHandler) error {
  20. if enabled {
  21. return fmt.Errorf("Panic Handler has already been set")
  22. }
  23. enabled = true
  24. // Setup a go routine which receives via the panic channel, passes
  25. // resulting Panic to the handler passed.
  26. go func() {
  27. for {
  28. p := <-dispatcher
  29. // If we do not wish to recover, panic using same error
  30. if !handler(p) {
  31. panic(p.Error)
  32. }
  33. }
  34. }()
  35. return nil
  36. }
  37. // HandlePanic should be executed in a deferred method (or deferred directly). It will
  38. // capture any panics that occur in the goroutine it exists, and report to the registered
  39. // global panic handler.
  40. func HandlePanic() {
  41. // Do not handle/recover if disabled
  42. if !enabled {
  43. return
  44. }
  45. if err := recover(); err != nil {
  46. dispatch(err)
  47. }
  48. }
  49. // generate stacktrace, dispatch the panic via channel
  50. func dispatch(err interface{}) {
  51. stack := make([]byte, 1024*8)
  52. stack = stack[:runtime.Stack(stack, false)]
  53. dispatcher <- Panic{
  54. Error: err,
  55. Stack: string(stack),
  56. }
  57. }