-
Notifications
You must be signed in to change notification settings - Fork 60
/
Copy patherrors.go
110 lines (95 loc) · 2.64 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
101
102
103
104
105
106
107
108
109
110
// Copyright 2020 Palantir Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package githubapp
import (
"fmt"
"io"
"runtime"
"github.com/rcrowley/go-metrics"
)
const (
MetricsKeyHandlerError = "github.handler.error"
)
var (
// HandlerRecoverStackDepth is the max depth of stack trace to recover on a
// handler panic.
HandlerRecoverStackDepth = 32
)
func errorCounter(r metrics.Registry, event string) metrics.Counter {
if r == nil {
return metrics.NilCounter{}
}
key := MetricsKeyHandlerError
if event != "" {
key = fmt.Sprintf("%s[event:%s]", key, event)
}
return metrics.GetOrRegisterCounter(key, r)
}
// HandlerPanicError is an error created from a recovered handler panic.
type HandlerPanicError struct {
value interface{}
stack []runtime.Frame
}
// Value returns the exact value with which panic() was called.
func (e HandlerPanicError) Value() interface{} {
return e.value
}
// StackTrace returns the stack of the panicking goroutine.
func (e HandlerPanicError) StackTrace() []runtime.Frame {
return e.stack
}
// Format formats the error optionally including the stack trace.
//
// %s the error message
// %v the error message and the source file and line number for each stack frame
//
// Format accepts the following flags:
//
// %+v the error message and the function, file, and line for each stack frame
func (e HandlerPanicError) Format(s fmt.State, verb rune) {
switch verb {
case 's':
_, _ = io.WriteString(s, e.Error())
case 'v':
_, _ = io.WriteString(s, e.Error())
for _, f := range e.stack {
_, _ = io.WriteString(s, "\n")
if s.Flag('+') {
_, _ = fmt.Fprintf(s, "%s\n\t", f.Function)
}
_, _ = fmt.Fprintf(s, "%s:%d", f.File, f.Line)
}
}
}
func (e HandlerPanicError) Error() string {
v := e.value
if err, ok := v.(error); ok {
v = err.Error()
}
return fmt.Sprintf("panic: %v", v)
}
func getStack(skip int) []runtime.Frame {
rpc := make([]uintptr, HandlerRecoverStackDepth)
n := runtime.Callers(skip+2, rpc)
frames := runtime.CallersFrames(rpc[0:n])
var stack []runtime.Frame
for {
f, more := frames.Next()
if !more {
break
}
stack = append(stack, f)
}
return stack
}