kaspad/util/panics/panics.go
Ori Newman 5a99e4d2f3
[NOD-806] Exit early after panic (#650)
* [NOD-806] After panic, gracefully stop logs, and then exit immediately

* [NOD-806] Convert non-kaspad applications to use the new spawn

* [NOD-806] Fix disabled log at rpcclient

* [NOD-806] Refactor HandlePanic

* [NOD-806] Cancel Logger interface

* [NOD-806] Remove redundant spawn checks from waitgroup_test.go

* [NOD-806] Use caller subsystem when logging panics

* [NOD-806] Fix go vet errors
2020-03-08 11:24:37 +02:00

60 lines
1.5 KiB
Go

package panics
import (
"fmt"
"github.com/kaspanet/kaspad/logs"
"os"
"runtime/debug"
"time"
)
// HandlePanic recovers panics, log them, runs an optional panicHandler,
// and then initiates a clean shutdown.
func HandlePanic(log *logs.Logger, goroutineStackTrace []byte) {
err := recover()
if err == nil {
return
}
panicHandlerDone := make(chan struct{})
go func() {
log.Criticalf("Fatal error: %+v", err)
if goroutineStackTrace != nil {
log.Criticalf("Goroutine stack trace: %s", goroutineStackTrace)
}
log.Criticalf("Stack trace: %s", debug.Stack())
log.Backend().Close()
panicHandlerDone <- struct{}{}
}()
const panicHandlerTimeout = 5 * time.Second
select {
case <-time.Tick(panicHandlerTimeout):
fmt.Fprintln(os.Stderr, "Couldn't handle a fatal error. Exiting...")
case <-panicHandlerDone:
}
os.Exit(1)
}
// GoroutineWrapperFunc returns a goroutine wrapper function that handles panics and writes them to the log.
func GoroutineWrapperFunc(log *logs.Logger) func(func()) {
return func(f func()) {
stackTrace := debug.Stack()
go func() {
defer HandlePanic(log, stackTrace)
f()
}()
}
}
// AfterFuncWrapperFunc returns a time.AfterFunc wrapper function that handles panics.
func AfterFuncWrapperFunc(log *logs.Logger) func(d time.Duration, f func()) *time.Timer {
return func(d time.Duration, f func()) *time.Timer {
stackTrace := debug.Stack()
return time.AfterFunc(d, func() {
defer HandlePanic(log, stackTrace)
f()
})
}
}