Files
open-cluster-management/vendor/github.com/openshift/library-go/pkg/serviceability/panic.go
2020-04-01 12:43:36 -04:00

94 lines
2.6 KiB
Go

package serviceability
import (
"encoding/json"
"strings"
"time"
"k8s.io/klog"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/version"
)
// BehaviorOnPanic is a helper for setting the crash mode of OpenShift when a panic is caught.
// It returns a function that should be the defer handler for the caller.
func BehaviorOnPanic(modeString string, productVersion version.Info) func() {
modes := []string{}
if err := json.Unmarshal([]byte(modeString), &modes); err != nil {
return behaviorOnPanic(modeString, productVersion)
}
fns := []func(){}
for _, mode := range modes {
fns = append(fns, behaviorOnPanic(mode, productVersion))
}
return func() {
for _, fn := range fns {
fn()
}
}
}
func behaviorOnPanic(mode string, productVersion version.Info) func() {
doNothing := func() {}
switch {
case mode == "crash":
klog.Infof("Process will terminate as soon as a panic occurs.")
utilruntime.ReallyCrash = true
return doNothing
case strings.HasPrefix(mode, "crash-after-delay:"):
delayDurationString := strings.TrimPrefix(mode, "crash-after-delay:")
delayDuration, err := time.ParseDuration(delayDurationString)
if err != nil {
klog.Errorf("Unable to start crash-after-delay. Crashing immediately instead: %v", err)
utilruntime.ReallyCrash = true
return doNothing
}
klog.Infof("Process will terminate %v after a panic occurs.", delayDurationString)
utilruntime.ReallyCrash = false
utilruntime.PanicHandlers = append(utilruntime.PanicHandlers, crashOnDelay(delayDuration, delayDurationString))
return doNothing
case strings.HasPrefix(mode, "sentry:"):
url := strings.TrimPrefix(mode, "sentry:")
m, err := NewSentryMonitor(url, productVersion)
if err != nil {
klog.Errorf("Unable to start Sentry for panic tracing: %v", err)
return doNothing
}
klog.Infof("Process will log all panics and errors to Sentry.")
utilruntime.ReallyCrash = false
utilruntime.PanicHandlers = append(utilruntime.PanicHandlers, m.CapturePanic)
utilruntime.ErrorHandlers = append(utilruntime.ErrorHandlers, m.CaptureError)
return func() {
if r := recover(); r != nil {
m.CapturePanicAndWait(r, 2*time.Second)
panic(r)
}
}
case len(mode) == 0:
// default panic behavior
utilruntime.ReallyCrash = false
return doNothing
default:
klog.Errorf("Unrecognized panic behavior")
return doNothing
}
}
func crashOnDelay(delay time.Duration, delayString string) func(interface{}) {
return func(in interface{}) {
go func() {
klog.Errorf("Panic happened. Process will crash in %v.", delayString)
time.Sleep(delay)
panic(in)
}()
}
}