-
Notifications
You must be signed in to change notification settings - Fork 0
/
errors.go
100 lines (84 loc) · 2.98 KB
/
errors.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
99
100
package slogbugsnag
import (
"errors"
"fmt"
"runtime"
"strings"
bserrors "github.com/bugsnag/bugsnag-go/v2/errors"
perrors "github.com/pkg/errors"
)
// withCallers is an error-with-stack-trace trace interface that
// [github.com/bugsnag/bugsnag-go/v2/errors.Error] supports
type withCallers interface {
Callers() []uintptr
}
// withCallers is an error-with-stack-trace interface that
// [github.com/bugsnag/bugsnag-go/v2/errors.Error] supports
type withBSStackFrames interface {
StackFrames() []bserrors.StackFrame
}
// withCallers is an error-with-stack-trace interface that
// [github.com/bugsnag/bugsnag-go/v2/errors.Error] supports
type withPStackTrace interface {
StackTrace() perrors.StackTrace
}
var _ errorWithCallers = errorWithCallers{} // Validate implements interface
// errorWithCallers exists to let us add a caller stack trace onto any error
// that is missing one, starting at the point where the log method is called.
type errorWithCallers struct {
error
stack []uintptr
}
// Callers returns the raw stack frames as returned by runtime.Callers()
func (e errorWithCallers) Callers() []uintptr {
return e.stack[:]
}
// Unwrap provides compatibility for Go 1.13 error chains.
func (e errorWithCallers) Unwrap() error { return e.error }
// String returns the string representation of this error with its stacktrace
func (e errorWithCallers) String() string {
return fmt.Sprintf("%T: %v\n\t%s", e.error, e.error, strings.Join(debugStack(e.stack), "\n\t"))
}
// debugStack returns the frame stack in string format for debugging
func debugStack(pcs []uintptr) []string {
var trace []string
fs := runtime.CallersFrames(pcs)
for f, more := fs.Next(); ; f, more = fs.Next() {
trace = append(trace, fmt.Sprintf("%s:%d", f.Function, f.Line))
if !more {
break
}
}
return trace
}
// newErrorWithStack ensures we have a non-nil error that includes a full stack
// trace, using either the one it came with or generating one from the log line
func newErrorWithStack(errForBugsnag error, msg string, pc uintptr) error {
// Ensure the error is not nil. Use the log message for the error if not.
if errForBugsnag == nil {
errForBugsnag = errors.New(msg)
}
// Ensure our error has a caller/stack/frame trace
switch errForBugsnag.(type) {
case *bserrors.Error, withCallers, withBSStackFrames, withPStackTrace:
// Do nothing, these errors already have a full stack
return errForBugsnag
}
// Recreate the callers stack trace, based on the log program counter
stack := make([]uintptr, bserrors.MaxStackDepth)
length := runtime.Callers(0, stack[:])
stack = stack[:length]
// Iterate until we find our log line program counter, then return a
// wrapped error with the remaining stack callers
for idx, ptr := range stack {
if ptr == pc {
return errorWithCallers{
error: errForBugsnag,
stack: stack[idx:],
}
}
}
// This can only happen if a handler edited the PC. In that case, let
// bugsnag create a full stack trace, which will include the log handlers.
return errForBugsnag
}