Go errors 默认加堆栈信息,有用不?

大家好,我是煎鱼。

在 Go 语言中,错误处理是我们必须涉及和争议比较大的一个功能特性。今天我们不太探讨 if err != nil 的繁杂忧愁。

聚焦在 errors 标准库在排查、定位问题的诉求上。看看大家平时都是怎么做的。

背景

平时我们在返回和处理错误时,一般使用 errors 标准库。其支持以下几个 API:

go 复制代码
func As(err error, target any) bool
func Is(err, target error) bool
func Join(errs ...error) error
func New(text string) error
func Unwrap(err error) error

最简单的 Demo 如下:

go 复制代码
func main() {
	err := errors.New("煎鱼出现错误了!")
	if err != nil {
		fmt.Println(err)
	}
}

输出结果:

复制代码
煎鱼出现错误了!

看着非常基础,也没什么特别的。但如果是在生产等正式环境下出问题时,可能就没法这么愉快了。

但比较头疼的是:其错误信息缺乏了调用堆栈,在复杂程序下,你很难直观的知道是具体哪些关联的代码导致出现了这个报错。只能靠在代码中搜错误的文本,去猜测应该是这里的问题。

第三方库解决

因此很多同学会使用第三方的开源库 go-errors/errors。以下是他的简单代码演示:

go 复制代码
import (
	"fmt"

	"github.com/go-errors/errors"
)

var Crashed = errors.Errorf("煎鱼变炸鱼了!")

func Crash() error {
	return errors.New(Crashed)
}

func main() {
	err := Crash()
	if err != nil {
		fmt.Println(err.(*errors.Error).ErrorStack())
		return
	}
}

输出结果:

bash 复制代码
$ go run main.go
*errors.Error 煎鱼变炸鱼了!
/Users/eddycjy/app/go/demo1/main.go:12 (0x1090645)
	main: return errors.New(Crashed)
/Users/eddycjy/app/go/demo1/main.go:12 (0x1090636)
	Crash: return errors.New(Crashed)
/usr/local/Cellar/go/1.21.1/libexec/src/runtime/internal/atomic/types.go:194 (0x103305b)
	(*Uint32).Load: return Load(&u.value)
/usr/local/Cellar/go/1.21.1/libexec/src/runtime/asm_amd64.s:1650 (0x105d501)
	goexit: BYTE	$0x90	// NOP

该库会默认记录 ErrorStack 并且可以通过相应的方法。

新提案

前面提到的问题和解决方案,一直有人在提和重复造新的轮子。最近 Go 核心团队 @Ian Lance Taylor 提出了新的提案《proposal: errors: let GODEBUG=errstacktrace request stack backtraces》希望以此解决这个问题。

在提案中计划:增加一个新的 Go 运行时选项:GODEBUG=errstacktrace。

在环境中设置该 GODEBUG 项后,errors.New 和 fmt.Errorf 函数将发生变化,将堆栈跟踪纳入信息中。

新补充的堆栈跟踪将成为 Error 方法返回的字符串的一部分,将会是一个多行的字符串内容。

总结

Go 的 errors 总是纷纷扰扰,本次的提案也是拉扯了多年的结果。本次是想往 GODEBUG 上做开关选项,但也有许多同学想在编译时就能指定,不用每次配一堆 GODEBUG。(虽然实际效果差不多)

整体来讲,这个需求如果能够落地,还是不错的。能够解决一些缺失调用堆栈,不太能明确错误抛出在哪的小麻烦。也能解决一些第三方库存在原因,不用再额外维护了。

文章持续更新,可以微信搜【脑子进煎鱼了】阅读,本文 GitHub github.com/eddycjy/blo... 已收录,学习 Go 语言可以看 Go 学习地图和路线,欢迎 Star 催更。

推荐阅读

相关推荐
君万11 小时前
【LeetCode每日一题】回溯:46. 全排列 78. 子集 39. 组合总和
算法·leetcode·go
vipxieliang17 小时前
Go 逃逸分析与内存优化:从读懂 -gcflags=“-m“ 到降低 GC 压力
go
小杨互联网2 天前
Go 现代 CLI 工具离线镜像合集
开发语言·后端·golang·开源·go·命令行
hourinX2 天前
从直接填格子到筛选图案:头像生成这部分是怎样改过来的—— gdenticon 重构日志(三)
go
我不会起名字3222 天前
Redis 从 0 到 1(十三):分布式锁被误删、锁不住?Go 无 Lua 实现 3 级防御
redis·go·分布式锁·watch·setnx
hourinX3 天前
从 v1 到 v2,我重新拆了头像生成的整条链路—— gdenticon 重构日志(二)
go
FungLeo3 天前
成为全栈·Go 后端篇·分类树与标签关系:目录变更为何需要协调
golang·go·gorm·数据库事务·分类树·成为全栈
FungLeo3 天前
成为全栈·Go 后端篇·文章查询与展示投影:安全分页、排序和软删除
golang·go·gorm·分页查询·成为全栈·sql安全
KevinAshen674 天前
用 Go 修复旧下载工具:从接口 403 到凭证截断的完整复盘
go·github
FungLeo5 天前
成为全栈·Go 后端篇·跨语言认证兼容:Node 与 Go 如何互认 JWT 和密码
golang·go·jwt·身份认证·成为全栈·bcrypt