在Golang项目里,底层函数返回的错误常常只包含一句简单描述,当错误向上传递多层之后,调用方很难知道这次失败发生在什么业务场景、传入了哪些参数。为错误添加上下文信息是改善可观测性的基础手段。
使用fmt.Errorf进行错误包装
Go 1.13之后,标准库支持在fmt.Errorf中使用%w动词来包装错误,这样既能附加上下文,又保留原始错误以便后续判定。
package main
import (
"errors"
"fmt"
)
func readFile(name string) error {
return errors.New("file not found")
}
func process(name string) error {
err := readFile(name)
if err != nil {
// 使用 %w 包装错误并添加上下文
return fmt.Errorf("处理文件 %s 失败: %w", name, err)
}
return nil
}
func main() {
err := process("config.yaml")
if err != nil {
fmt.Println(err)
}
}
利用errors包函数提取信息
包装后的错误可通过errors.Is判断根源错误类型,用errors.As提取特定错误结构体,用errors.Unwrap获取下一层错误。
package main
import (
"errors"
"fmt"
)
var ErrNotFound = errors.New("not found")
func dao() error {
return ErrNotFound
}
func service() error {
err := dao()
if err != nil {
return fmt.Errorf("查询用户失败: %w", err)
}
return nil
}
func main() {
err := service()
if errors.Is(err, ErrNotFound) {
fmt.Println("根源是未找到错误")
}
// 逐层解包打印
for err != nil {
fmt.Println("层级错误:", err)
err = errors.Unwrap(err)
}
}
自定义错误类型携带上下文
如果需要携带更结构化的上下文,例如请求ID或参数,可以定义自己的错误类型并实现Error方法。
package main
import "fmt"
type AppError struct {
Op string
Param string
Err error
}
func (e *AppError) Error() string {
return fmt.Sprintf("操作 %s 参数 %s 出错: %v", e.Op, e.Param, e.Err)
}
func (e *AppError) Unwrap() error {
return e.Err
}
func call() error {
return &AppError{
Op: "login",
Param: "user_id=1",
Err: fmt.Errorf("超时"),
}
}
func main() {
err := call()
fmt.Println(err)
}
实践建议
- 在边界处包装错误,添加业务动作和关键参数,但不要泄露敏感信息。
- 底层错误使用哨兵变量或自定义类型,方便上层用errors.Is或errors.As识别。
- 日志中打印完整包装错误,同时保留原始错误类型用于监控告警。
通过上述方式,可以在不破坏Go错误模型的前提下,为错误增加清晰的上下文,让排查问题更直观高效。
Golangerror_wrapcontext_info修改时间:2026-07-29 22:27:20