github.com/tuingking/flamingo@v0.0.0-20220403134817-2796ae0e84ca/pkg/panics/panics.go (about) 1 package panics 2 3 import ( 4 "errors" 5 "fmt" 6 "net/http" 7 "net/http/httputil" 8 "os" 9 "runtime/debug" 10 11 "github.com/eapache/go-resiliency/breaker" 12 ) 13 14 var ( 15 // circuit breaker 16 cb *breaker.Breaker 17 ) 18 19 // HTTPRecoveryMiddleware act as middleware that capture panics standard in http handler 20 func HTTPRecoveryMiddleware(next http.Handler) http.Handler { 21 fn := func(w http.ResponseWriter, r *http.Request) { 22 _, _ = httputil.DumpRequest(r, true) 23 defer func() { 24 if !recoveryBreak() { 25 rcv := panicRecover(recover()) 26 if rcv != nil { 27 // log the panic 28 fmt.Fprintf(os.Stderr, "Panic: %+v\n", rcv) 29 debug.PrintStack() 30 // publishError(rcv, request, true) 31 http.Error(w, rcv.Error(), http.StatusInternalServerError) 32 } 33 } 34 }() 35 36 next.ServeHTTP(w, r) 37 } 38 39 return http.HandlerFunc(fn) 40 } 41 42 func recoveryBreak() bool { 43 if cb == nil { 44 return false 45 } 46 47 if err := cb.Run(func() error { 48 return nil 49 }); err == breaker.ErrBreakerOpen { 50 return true 51 } 52 return false 53 } 54 55 func panicRecover(rc interface{}) error { 56 if cb != nil { 57 r := cb.Run(func() error { 58 return recovery(rc) 59 }) 60 return r 61 } 62 return recovery(rc) 63 } 64 65 func recovery(r interface{}) error { 66 var err error 67 if r != nil { 68 switch t := r.(type) { 69 case string: 70 err = errors.New(t) 71 case error: 72 err = t 73 default: 74 err = errors.New("Unknown error") 75 } 76 } 77 return err 78 }