Files
errorx/stack.go
T

75 lines
1.6 KiB
Go
Raw Permalink Normal View History

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
}