

新闻资讯
技术教程errors.Wrap 比 fmt.Errorf 更可靠,因其嵌入调用栈(文件名、行号)便于定位错误位置,而 fmt.Errorf 即使使用 %w 也丢失当前出错位置。
errors.Wrap 比直接 fmt.Errorf 更可靠Go 原生错误没有调用栈或上下文链路,fmt.Errorf("failed to read file: %w", err) 虽然保留了原始错误,但丢失了当前出错位置;而 errors.Wrap(err, "reading config file") 会在错误中嵌入调用点(文件名、行号),便于快速定位哪一层逻辑出了问题。
实操建议:
github.com/pkg/errors 或 Go 1.20+ 的 errors.Join/fmt.Errorf("%w", ...) + errors.WithStack(需额外库)来包裹底层错误fmt.Errorf("something went wrong") 丢弃 %w,否则上层无法 errors.Is 或 errors.As
fmt.Errorf("handling request: %w", err) 配合 errors.Unwrap 调试,但注意它不自动记录栈帧
Web 服务里,单看错误本身无法关联到具体请求。需要把 trace 信息注入错误对象,而不是只打日志。
实操建议:
type RequestError struct {
ReqID string
Path string
Cause error
Code int
}
func (e *RequestError) Error() string {
return fmt.Sprintf("req=%s path=%s: %v", e.ReqID, e.Path, e.Cause)
}
func (e *RequestError) Unwrap() error { return e.Cause }*RequestError 再返回,确保 http.Error 输出时仍可被上层分类处理errors.As(err, &e) 提取errors.Is 和 errors.As 时为何经常判断失败根本原因:错误链被多次 fmt.Errorf 包裹后,若任意一层没用 %w,链就断了;或者自定义错误没实现 Unwrap() 方法。
常见错误现象:
errors.Is(err, io.EOF) 返回 false,尽管底层确实是 EOF —— 因为中间某次 fmt.Errorf("read failed: %v", err)(漏了 %w)导致链断裂errors.As(err, &myErr) 失败,因为自定义错误类型没写 Unwrap() error 方法,或返回了 nil 而非实际原因errors.New("xxx") 替代 fmt.Errorf("xxx: %w", err),彻底切断错误溯源能力fmt.Errorf 的 %w 和老版本 pkg/errors 怎么选新项目直接用标准库 %w 即可,但要注意它不附带栈信息;老项目迁移时,不能简单替换 errors.Wrap 为 fmt.Errorf("%w", ...),否则丢失调试线索。
实操建议:
github.com/pkg/errors,或改用 golang.org/x/exp/errors(实验性,含 errors.Append)github.com/cockroachdb/errors,它兼容标准库接口且默认记录栈fmt.Errorf 都要下意识检查有没有写错成 %v 而不是 %w。