You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 10:35:58