refactor!: 错误值改为不可变,修复并发崩溃与兜底缺陷
BREAKING CHANGE: 相对 v0.0.1 的 API 变更 - 删除全部 Set* 方法,改为返回新值的 With*;错误值不可变,包级常量可并发使用 - 错误值不再持有 context,只保存语言标识(WithLang),渲染时再传 ctx - 删除兼容别名 Errf / ErrfKV / Msgf / NewLanguage / NewStruct 与空操作 Copy() - Msgf 改为 MsgWith,NewErrorf 改为 NewErrorWith;ErrorLanguage 不再实现 ErrorInterface 修复 - Copy 浅拷贝共享 format map,并发下 fatal error: concurrent map writes - 包级常量被 Set* 污染,请求之间互相改写错误文案 - langx.RegisterCode / RegisterTrans 覆盖注册表后,运行时错误码退化为默认码、文案退化为裸 key - 未注册 key 静默返回默认码 + 裸 key,现返回 500 + 未注册提示 - NewError(nil, ...) 与非 string 的 Accept-Language 触发 panic 新增 - Wrap / Unwrap / errors.Is 按 key 匹配、调用栈(惰性解析)、%+v 输出 cause 与栈 - 错误常量自带默认文案兜底 + errorx 侧常量注册表(不依赖 langx 可变状态) - 根 README、3 个可运行示例、17 个断言测试(语句覆盖率 97.2%)、基准测试 依赖 - github.com/yuninks/langx v0.0.7 -> v0.1.0
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user