-
Notifications
You must be signed in to change notification settings - Fork 0
/
errors.go
121 lines (99 loc) · 2.68 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
111
112
113
114
115
116
117
118
119
120
121
// Copyright 2015 Bowery, Inc.
package errors
import (
"errors"
"fmt"
"reflect"
"runtime"
"strconv"
"strings"
)
// New creates a new error, this solves issue of name collision with
// errors pkg.
func New(args ...interface{}) error {
return errors.New(strings.TrimRight(fmt.Sprintln(args...), "\n"))
}
// Newf creates a new error, from an existing error template.
func Newf(format string, args ...interface{}) error {
return fmt.Errorf(format, args...)
}
// StackError is an error with stack information.
type StackError struct {
Err error
Trace *Trace
}
// IsStackError returns the error as a StackError if it's a StackError, nil
// otherwise.
func IsStackError(err error) *StackError {
se, ok := err.(*StackError)
if ok {
return se
}
return nil
}
// NewStackError creates a stack error including the stack.
func NewStackError(err error) error {
se := &StackError{
Err: err,
Trace: &Trace{
Frames: make([]*Frame, 0),
Exception: &Exception{Message: err.Error(), Class: errClass(err)},
},
}
// Get stack frames excluding the current one.
for i := 1; ; i++ {
pc, file, line, ok := runtime.Caller(i)
if !ok {
// Couldn't get another frame, so we're finished.
break
}
f := &Frame{File: file, Line: line, Method: routineName(pc)}
se.Trace.Frames = append(se.Trace.Frames, f)
}
return se
}
func (se *StackError) Error() string {
return se.Err.Error()
}
// Stack prints the stack trace in a readable format.
func (se *StackError) Stack() string {
stack := ""
for i, frame := range se.Trace.Frames {
stack += strconv.Itoa(i+1) + ": File \"" + frame.File + "\" line "
stack += strconv.Itoa(frame.Line) + " in " + frame.Method + "\n"
}
stack += se.Trace.Exception.Class + ": " + se.Trace.Exception.Message
return stack
}
// Trace contains the stack frames, and the exception information.
type Trace struct {
Frames []*Frame `json:"frames"`
Exception *Exception `json:"exception"`
}
// Exception contains the error message and it's class origin.
type Exception struct {
Class string `json:"class"`
Message string `json:"message"`
}
// Frame contains line, file and method info for a stack frame.
type Frame struct {
File string `json:"filename"`
Line int `json:"lineno"`
Method string `json:"method"`
}
// errClass retrieves the string representation for the errors type.
func errClass(err error) string {
class := strings.TrimPrefix(reflect.TypeOf(err).String(), "*")
if class == "" {
class = "panic"
}
return class
}
// routineName returns the routines name for a given program counter.
func routineName(pc uintptr) string {
fc := runtime.FuncForPC(pc)
if fc == nil {
return "???"
}
return fc.Name() // Includes the package info.
}