/
errorx.go
98 lines (89 loc) · 2.25 KB
/
errorx.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
// Copyright (c) 2021 Terminus, Inc.
//
// This program is free software: you can use, redistribute, and/or modify
// it under the terms of the GNU Affero General Public License, version 3
// or later ("AGPL"), as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package errorx
import (
"bytes"
"fmt"
"runtime"
"strings"
)
// 最大可纪录的调用堆栈的深度
var MaxStackDepth = 5
type TracedError interface {
error
Callers() []uintptr // 返回堆栈调用信息
Unpack() error // 返回去掉堆栈调用信息的错误
}
// TracedError 记录调用堆栈信息的错误类型
type tracedError struct {
err interface{}
stack []uintptr
}
// NewTracedError 创建一个具有堆栈调用信息的错误
func NewTracedError(err interface{}) TracedError {
if err == nil {
return nil
}
stack := make([]uintptr, MaxStackDepth)
stack = stack[:runtime.Callers(2, stack[:])]
return &tracedError{
err: err,
stack: stack,
}
}
func (err *tracedError) Error() string {
buf := bytes.Buffer{}
switch val := err.err.(type) {
case string:
buf.WriteString(val)
buf.WriteString(" :\n")
case error:
buf.WriteString(val.Error())
buf.WriteString(" :\n")
case nil:
return ""
}
for _, pc := range err.stack {
if pc == 0 {
continue
}
fn := runtime.FuncForPC(pc)
if fn == nil {
continue
}
file, lineNum := fn.FileLine(pc - 1)
if lastslash := strings.LastIndex(file, "/"); lastslash >= 0 {
file = file[lastslash+1:]
}
name := fn.Name()
if lastslash := strings.LastIndex(name, "/"); lastslash >= 0 {
name = name[lastslash+1:]
}
buf.WriteString(fmt.Sprintf("* [%s:%d] %s\n", file, lineNum, name))
}
return string(buf.Bytes()[:buf.Len()-1])
}
func (err *tracedError) Callers() []uintptr {
return err.stack
}
func (err *tracedError) Unpack() error {
switch val := err.err.(type) {
case string:
return StringError(val)
case error:
return val
case nil:
return nil
}
return err
}