|
|
@@ -2,13 +2,36 @@ package errors
|
|
|
|
|
|
import (
|
|
|
"fmt"
|
|
|
+ "net/http"
|
|
|
"runtime"
|
|
|
)
|
|
|
|
|
|
+//--------------------------------------------------------------------------
|
|
|
+// PanicType
|
|
|
+//--------------------------------------------------------------------------
|
|
|
+
|
|
|
+// PanicType defines the context in which the panic occurred
|
|
|
+type PanicType int
|
|
|
+
|
|
|
+const (
|
|
|
+ PanicTypeDefault PanicType = iota
|
|
|
+ PanicTypeHTTP
|
|
|
+)
|
|
|
+
|
|
|
+// The string representation of PanicContext
|
|
|
+func (pt PanicType) String() string {
|
|
|
+ return []string{"PanicTypeDefault", "PanicTypeHTTP"}[pt]
|
|
|
+}
|
|
|
+
|
|
|
+//--------------------------------------------------------------------------
|
|
|
+// Panic
|
|
|
+//--------------------------------------------------------------------------
|
|
|
+
|
|
|
// Panic represents a panic that occurred, captured by a recovery.
|
|
|
type Panic struct {
|
|
|
Error interface{}
|
|
|
Stack string
|
|
|
+ Type PanicType
|
|
|
}
|
|
|
|
|
|
// PanicHandler is a func that receives a Panic and returns a bool representing whether or not
|
|
|
@@ -44,27 +67,55 @@ func SetPanicHandler(handler PanicHandler) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
+// PanicHandlerMiddleware should wrap any of the http handlers to capture panics.
|
|
|
+func PanicHandlerMiddleware(handler http.Handler) http.Handler {
|
|
|
+ return http.HandlerFunc(func(rw http.ResponseWriter, rq *http.Request) {
|
|
|
+ defer HandleHTTPPanic(rw, rq)
|
|
|
+
|
|
|
+ handler.ServeHTTP(rw, rq)
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
// HandlePanic should be executed in a deferred method (or deferred directly). It will
|
|
|
// capture any panics that occur in the goroutine it exists, and report to the registered
|
|
|
// global panic handler.
|
|
|
func HandlePanic() {
|
|
|
- // Do not handle/recover if disabled
|
|
|
+ // NOTE: For each "special" type of panic that is added, you must repeat this pattern. The recover()
|
|
|
+ // NOTE: call cannot exist in a func outside of the deferred func.
|
|
|
if !enabled {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
if err := recover(); err != nil {
|
|
|
- dispatch(err)
|
|
|
+ dispatch(err, PanicTypeDefault)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// HandleHTTPPanic should be executed in a deferred method (or deferred directly) in http middleware.
|
|
|
+// It will capture any panics that occur in the goroutine it exists, and report to the registered
|
|
|
+// global panic handler. HTTP handler panics will have the errors.PanicTypeHTTP Type.
|
|
|
+func HandleHTTPPanic(rw http.ResponseWriter, rq *http.Request) {
|
|
|
+ // NOTE: For each "special" type of panic that is added, you must repeat this pattern. The recover()
|
|
|
+ // NOTE: call cannot exist in a func outside of the deferred func.
|
|
|
+ if !enabled {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := recover(); err != nil {
|
|
|
+ rw.WriteHeader(http.StatusInternalServerError)
|
|
|
+
|
|
|
+ dispatch(err, PanicTypeHTTP)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// generate stacktrace, dispatch the panic via channel
|
|
|
-func dispatch(err interface{}) {
|
|
|
+func dispatch(err interface{}, panicType PanicType) {
|
|
|
stack := make([]byte, 1024*8)
|
|
|
stack = stack[:runtime.Stack(stack, false)]
|
|
|
|
|
|
dispatcher <- Panic{
|
|
|
Error: err,
|
|
|
Stack: string(stack),
|
|
|
+ Type: panicType,
|
|
|
}
|
|
|
}
|