如何使用Go的log/slog包记录错误及堆栈跟踪信息?
使用log/slog记录错误的最佳实践及堆栈跟踪实现
官方文档介绍了Go语言新的结构化日志包log/slog的使用方法,但未提及如何记录错误。以下为示例代码:
package main import ( "fmt" "log/slog" "os" ) func demoFunction() error { return fmt.Errorf("oh no: %v", 123) } func main() { logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) slog.SetDefault(logger) slog.Info("info demo", "count", 3) slog.Warn("warn demo", slog.String("somekey", "somevalue")) slog.Error("error demo", slog.Int("someintkey", 123)) err := demoFunction() if err != nil { // 此处我将错误作为字符串记录,但推测存在更优方式 // 例如可同时记录堆栈跟踪信息 slog.Error("the demo function got an error.", slog.String("error", err.Error())) } }
一、更优的错误记录方式
不需要将错误转换为字符串后再记录,直接将错误对象作为键值对的值传入即可,slog会自动处理error类型的输出:
if err != nil { // 最简写法 slog.Error("demo function encountered an error", "error", err) // 显式指定类型(效果一致) slog.Error("demo function encountered an error", slog.Any("error", err)) }
这种方式既能保留错误的原始类型信息,又能让日志输出更简洁。如果错误实现了slog.LogValuer接口,还可以输出更多结构化的错误详情。
二、记录错误的堆栈跟踪信息
Go标准库的error类型本身不包含堆栈跟踪信息,需要通过以下几种方式实现:
1. 使用第三方错误库生成带堆栈的错误
最常用的方案是使用github.com/pkg/errors包,它可以为错误自动添加堆栈信息:
首先安装依赖:
go get github.com/pkg/errors
修改错误生成逻辑:
import "github.com/pkg/errors" func demoFunction() error { // 用Wrap为原始错误添加堆栈和上下文 return errors.Wrap(fmt.Errorf("oh no: %v", 123), "demo function failed") // 直接生成带堆栈的错误 // return errors.New("oh no: 123") }
记录错误时,slog会自动解析并输出包含堆栈的完整错误信息。
2. 自定义带堆栈的错误类型(无第三方依赖)
如果不想引入外部依赖,可以自定义错误类型并实现slog.LogValuer接口,在日志中输出堆栈:
import ( "fmt" "log/slog" "runtime" ) type StackError struct { Err error Stack []byte } func (e *StackError) Error() string { return e.Err.Error() } func (e *StackError) LogValue() slog.Value { return slog.GroupValue( slog.String("message", e.Err.Error()), slog.String("stack_trace", string(e.Stack)), ) } // 生成带堆栈的错误 func NewStackError(err error) *StackError { stackBuf := make([]byte, 4096) stackLen := runtime.Stack(stackBuf, false) return &StackError{Err: err, Stack: stackBuf[:stackLen]} } // 使用示例 func demoFunction() error { return NewStackError(fmt.Errorf("oh no: %v", 123)) } // 记录错误 if err != nil { slog.Error("demo function got an error", "error", err) }
此时日志会输出包含错误消息和堆栈跟踪的结构化内容。
3. 自定义Handler自动为Error日志添加堆栈
如果希望所有Error级别的日志都自动附带堆栈,可以包装默认的slog Handler,在处理日志时注入堆栈信息:
import ( "context" "log/slog" "os" "runtime" ) type StackHandler struct { slog.Handler } func (h *StackHandler) Handle(ctx context.Context, r slog.Record) error { // 仅为Error级别日志添加堆栈 if r.Level == slog.LevelError { stackBuf := make([]byte, 4096) stackLen := runtime.Stack(stackBuf, false) r.Add("stack_trace", string(stackBuf[:stackLen])) } return h.Handler.Handle(ctx, r) } // 初始化日志 func main() { baseHandler := slog.NewJSONHandler(os.Stdout, nil) logger := slog.New(&StackHandler{Handler: baseHandler}) slog.SetDefault(logger) // 后续的Error日志都会自动带上堆栈 err := demoFunction() if err != nil { slog.Error("demo function failed", "error", err) } }
这种方式不需要修改业务代码中的错误生成逻辑,所有Error日志都会自动包含当前调用栈。
内容的提问来源于stack exchange,提问作者clay
相关产品推荐
相关产品推荐

