-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstack.go
More file actions
94 lines (85 loc) · 2.09 KB
/
Copy pathstack.go
File metadata and controls
94 lines (85 loc) · 2.09 KB
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
package xerror
import (
"runtime"
"strconv"
"strings"
"sync"
"github.com/gomooth/xerror/xcode"
)
const stackDepth = 32
// stack 代表调用栈,仅存储 PC 切片,延迟格式化以减少创建时的分配
type stack struct {
pcs [stackDepth]uintptr
n int
once sync.Once
str string
}
// captureStack 捕获当前调用栈,skip 表示跳过的栈帧数
func captureStack(skip int) *stack {
s := &stack{}
s.n = runtime.Callers(skip+2, s.pcs[:])
return s
}
// String 格式化调用栈。必须在指针接收者上调用,sync.Once 要求地址不变。
// 注意:stack 在 WithFields 等操作中被共享(只读),不可改为值接收者。
func (s *stack) String() string {
s.once.Do(func() {
if s.n == 0 {
return
}
var b strings.Builder
b.Grow(2048)
frames := runtime.CallersFrames(s.pcs[:s.n])
for {
f, more := frames.Next()
b.WriteString(f.Function)
b.WriteByte('\n')
b.WriteByte('\t')
b.WriteString(f.File)
b.WriteByte(':')
b.WriteString(strconv.Itoa(f.Line))
b.WriteByte('\n')
if !more {
break
}
}
if s.n == stackDepth {
b.WriteString("... (truncated)\n\t:-1\n")
}
s.str = b.String()
})
return s.str
}
// ChainMessenger 可提供不含错误码前缀的链式消息。
type ChainMessenger interface {
ChainMessage() string
}
// chainMessageOf 获取 error 的链式消息(不含 code 前缀)
func chainMessageOf(err error) string {
if cm, ok := err.(ChainMessenger); ok {
return cm.ChainMessage()
}
return err.Error()
}
// WithStackX 为已有 error 附加调用栈,始终返回 XError。
// 如果 err 已经是 XError,原样返回(XError 构造时已捕获调用栈)。
// 对于 plain error,创建一个包装的 XError(CodeNone + 原始消息 + 调用栈)。
func WithStackX(err error) XError {
if err == nil {
return nil
}
if xe, ok := asXErrorInternal(err); ok {
return xe
}
msg := err.Error()
code := xcode.NewMessage(msg)
return &xError{
errorBase: errorBase{
code: code,
stack: captureStack(1),
chainMsg: msg,
errStr: newErrStr(code, msg),
},
error: err,
}
}