75 lines
1.6 KiB
Go
75 lines
1.6 KiB
Go
package errorx
|
||||
|
|
|
|||
|
|
import (
|
|||
|
|
"fmt"
|
|||
|
|
"runtime"
|
|||
|
|
"strings"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// selfPackage 用于过滤 errorx 自身的栈帧。
|
|||
|
|
const selfPackage = "github.com/yuninks/errorx."
|
|||
|
|
|
|||
|
|
const stackMaxDepth = 32
|
|||
|
|
|
|||
|
|
// Frame 是一个调用栈帧。
|
|||
|
|
type Frame struct {
|
|||
|
|
Function string
|
|||
|
|
File string
|
|||
|
|
Line int
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// String 返回 "file:line" 形式,方便日志里直接打印。
|
|||
|
|
func (f Frame) String() string {
|
|||
|
|
return fmt.Sprintf("%s:%d", f.File, f.Line)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// StackTrace 是错误创建时的调用栈(已过滤 errorx 自身帧)。
|
|||
|
|
type StackTrace []Frame
|
|||
|
|
|
|||
|
|
// String 按 "函数名\n\t文件:行号" 的格式输出整条调用栈。
|
|||
|
|
func (st StackTrace) String() string {
|
|||
|
|
if len(st) == 0 {
|
|||
|
|
return ""
|
|||
|
|
}
|
|||
|
|
var b strings.Builder
|
|||
|
|
for _, f := range st {
|
|||
|
|
b.WriteString(f.Function)
|
|||
|
|
b.WriteString("\n\t")
|
|||
|
|
b.WriteString(f.String())
|
|||
|
|
b.WriteString("\n")
|
|||
|
|
}
|
|||
|
|
return b.String()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// captureStack 只抓取程序计数器,真正解析成函数名/文件/行号推迟到 StackTrace(),
|
|||
|
|
// 保证创建错误的开销足够小。
|
|||
|
|
func captureStack() []uintptr {
|
|||
|
|
var buf [stackMaxDepth]uintptr
|
|||
|
|
n := runtime.Callers(2, buf[:])
|
|||
|
|
if n <= 0 {
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
pcs := make([]uintptr, n)
|
|||
|
|
copy(pcs, buf[:n])
|
|||
|
|
return pcs
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// resolveStack 把程序计数器解析成可读的栈帧。
|
|||
|
|
func resolveStack(pcs []uintptr) StackTrace {
|
|||
|
|
if len(pcs) == 0 {
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
frames := runtime.CallersFrames(pcs)
|
|||
|
|
out := make(StackTrace, 0, len(pcs))
|
|||
|
|
for {
|
|||
|
|
f, more := frames.Next()
|
|||
|
|
if f.Function != "" && !strings.HasPrefix(f.Function, selfPackage) {
|
|||
|
|
out = append(out, Frame{Function: f.Function, File: f.File, Line: f.Line})
|
|||
|
|
}
|
|||
|
|
if !more {
|
|||
|
|
break
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return out
|
|||
|
|
}
|