Files
yun 21293673e1 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
2026-09-21 00:43:43 +08:00

75 lines
1.6 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
}