-
Notifications
You must be signed in to change notification settings - Fork 18
/
panics.go
98 lines (81 loc) · 2.49 KB
/
panics.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors.
//
// SPDX-License-Identifier: Apache-2.0
package panics
import (
"fmt"
"runtime"
"strings"
"github.com/open-component-model/ocm/v2/pkg/errors"
"github.com/open-component-model/ocm/v2/pkg/logging"
)
var ReallyCrash = true
type PanicHandler func(interface{}) error
var PanicHandlers = []PanicHandler{logHandler}
// PropagatePanicAsError is intended to be called via defer to
// map a recovered panic to an error and propagate it to the
// error return value of the calling function.
// Use: defer panics.PropagatePanicAsError(&err, false)
// It incorporates the originally returned error by the surrounding function
// and all errors provided by the panic handlers plus the mapped recovered panic.
func PropagatePanicAsError(errp *error, callStandardHandlers bool, additionalHandlers ...PanicHandler) {
if r := recover(); r != nil {
list := errors.ErrList().Add(mapRecovered(r))
if callStandardHandlers {
// gather errors from standard handler
for _, fn := range PanicHandlers {
list.Add(fn(r))
}
}
// add errors from explicit handlers
for _, fn := range additionalHandlers {
list.Add(fn(r))
}
*errp = list.Result()
}
}
func HandlePanic(additionalHandlers ...PanicHandler) {
if r := recover(); r != nil {
for _, fn := range PanicHandlers {
_ = fn(r)
}
for _, fn := range additionalHandlers {
_ = fn(r)
}
if ReallyCrash {
panic(r)
}
}
}
// RegisterPanicHandler adds handlers to the panic handler.
func RegisterPanicHandler(handler PanicHandler) {
PanicHandlers = append(PanicHandlers, handler)
}
func mapRecovered(r interface{}) error {
if err, ok := r.(error); ok {
return err
} else {
// Same as stdlib http server code. Manually allocate stack trace buffer size
// to prevent excessively large logs
const size = 64 << 10
stacktrace := make([]byte, size)
stacktrace = stacktrace[:runtime.Stack(stacktrace, false)]
stack := string(stacktrace)
lines := strings.Split(stack, "\n")
offset := 1
for offset < len(lines) && !strings.HasPrefix(lines[offset], "panic(") {
offset++
}
if offset < len(lines) {
stack = strings.Join(append(lines[:1], lines[offset:]...), "\n")
}
if _, ok := r.(string); ok {
return fmt.Errorf("Observed a panic: %#v\n%s", r, stack)
}
return fmt.Errorf("Observed a panic: %#v (%v)\n%s", r, r, stack)
}
}
func logHandler(r interface{}) error {
logging.Logger().Error(mapRecovered(r).Error())
return nil
}