go语言中通过自定义错误类型可增强错误的上下文信息和可追溯性,具体做法是定义包含错误码、消息、详情、时间、文件行号和traceid等字段的结构体并实现error()方法,同时提供自动捕获调用位置的构造函数,支持通过cause字段和unwrap()方法进行错误包装与解包,结合errors.is和errors.as进行错误判断,最佳实践中建议合理设计错误码、分离日志与错误、避免过度包装,并在分布式系统中传递traceid以实现全链路追踪,从而提升错误处理的可读性、调试能力和维护效率。

在 Go 语言中,错误处理是通过返回
error
errors.New
fmt.Errorf
下面介绍如何实现一个带有上下文信息的自定义错误结构。
我们可以定义一个结构体来封装丰富的错误信息:
立即学习“go语言免费学习笔记(深入)”;
package main
import (
"fmt"
"runtime"
"time"
)
type MyError struct {
Code int // 错误码
Msg string // 错误消息
Details string // 详细描述
Time time.Time // 发生时间
File string // 出错文件
Line int // 出错行号
TraceID string // 请求追踪ID
}
func (e *MyError) Error() string {
return fmt.Sprintf("[%s] ERROR %d: %s (details: %s) at %s:%d traceID=%s",
e.Time.Format("2006-01-02 15:04:05"),
e.Code,
e.Msg,
e.Details,
e.File,
e.Line,
e.TraceID,
)
}这个结构体实现了
error
Error() string
为了方便使用,可以封装一个构造函数,自动捕获文件名和行号:
func NewMyError(code int, msg, details, traceID string) *MyError {
_, file, line, _ := runtime.Caller(1) // 调用者的文件和行号
return &MyError{
Code: code,
Msg: msg,
Details: details,
Time: time.Now(),
File: file,
Line: line,
TraceID: traceID,
}
}runtime.Caller(1)
1
func getData() error {
return NewMyError(1001, "failed to fetch data", "network timeout", "req-12345")
}
func processData() error {
err := getData()
if err != nil {
// 可以包装或透传
return NewMyError(2001, "process failed", err.Error(), "req-12345")
}
return nil
}
func main() {
err := processData()
if err != nil {
fmt.Println(err)
// 输出示例:
// [2025-04-05 10:00:00] ERROR 2001: process failed (details: [2025-04-05 10:00:00] ERROR 1001: failed to fetch data (details: network timeout) at main.go:30 traceID=req-12345) at main.go:40 traceID=req-12345
}
}Go 1.13+ 支持错误包装(
%w
type MyError struct {
Code int
Msg string
Details string
Time time.Time
File string
Line int
TraceID string
Cause error // 包装原始错误
}
func (e *MyError) Error() string {
if e.Cause != nil {
return fmt.Sprintf("[%s] ERROR %d: %s (details: %s) at %s:%d traceID=%s -> caused by: %v",
e.Time.Format("2006-01-02 15:04:05"),
e.Code,
e.Msg,
e.Details,
e.File,
e.Line,
e.TraceID,
e.Cause,
)
}
return fmt.Sprintf("[%s] ERROR %d: %s (details: %s) at %s:%d traceID=%s",
e.Time.Format("2006-01-02 15:04:05"),
e.Code,
e.Msg,
e.Details,
e.File,
e.Line,
e.TraceID,
)
}
// 实现 Unwrap 方法,支持 errors.Is 和 errors.As
func (e *MyError) Unwrap() error {
return e.Cause
}
// 修改构造函数
func NewMyError(code int, msg, details, traceID string, cause error) *MyError {
_, file, line, _ := runtime.Caller(1)
return &MyError{
Code: code,
Msg: msg,
Details: details,
Time: time.Now(),
File: file,
Line: line,
TraceID: traceID,
Cause: cause,
}
}使用方式:
if err := someFunc(); err != nil {
return NewMyError(3001, "operation failed", "cleanup failed", "req-67890", err)
}然后可以用
errors.Is
errors.As
if errors.As(err, &myErr) {
fmt.Println("Custom error code:", myErr.Code)
}Error()
Error()
MyError
TraceID
基本上就这些。通过自定义错误结构,你可以让 Go 程序的错误更清晰、更易排查,特别是在大型服务中非常有用。
以上就是如何自定义Golang错误类型 实现带有上下文信息的错误结构的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号