求教: golang error 和 log 的最佳实践思路

262 天前
 Ayanokouji
用 go 写业务时,遇到 error 时,大多数情况只能一路向上 return err ,我们基于这个场景讨论。

这个场景,和 java 写业务遇到的 checked exception 很类似,写代码时,只能一路向上 throw (或者 catch 住 throw new unchecked exception ),最终由框架统一处理。

如果遇到 go 遇到经验不足的开发者(比如以前的我),就会看到这样的错误日志:

Error: unexpected '>' at the beginning of value

Error: EOF

Error: wrong argument value

嗯。。。这 tm 是啥啊,到底是哪一行代码出错啊。

调用链越长,问题越难排查。

比较通用的 web 业务调用链,一般是 handler -> service -> 中间件(数据库/redis/第三方 api 等)

随着坑踩的多了,现在遇到 err, 一般是 return fmt.Errorf("xxx:%w", err)

日志一般在 handler 层 slog.log("xxx", slog.Any("request", req), slog.Any("err", err))

但是缺少了调用栈,总觉得少了点什么。

请教下各位,如何平衡 error 和 log ,主要是服务于问题排查。

看过 echo 和 huma 的框架 error 处理,都是自定义 err ,框架统一处理

------

ps:那些上来只踩一脚 java 的,还是去多写写代码吧,这种 err ( unexpected '>' at the beginning of value ) 真的比 excetiop (含调用栈) 强吗。
6157 次点击
所在节点    Go 编程语言
81 条回复
guanzhangzhang
262 天前
dwu8555
262 天前
Panic 没什么错,Erlang 有一个思想:“Let it crash”, 就是要让程序 Crash 掉,不要隐藏错误。
securityCoding
262 天前
具体业务代码主动抛 err 需要前置 log.Errorf 打印出来,如果是调用方则无脑 return 即可
dwu8555
262 天前
aababc
262 天前
@tiedan #38 @harleyliao #4 这样真的好吗,我总的感觉就是 要么返回一个 error ,要么记录一个 error ,不要两件事都干
Ayanokouji
262 天前
@dwu8555 嗯。。。这么干,饭都没得吃了
frank000
262 天前
我现在用 zap, 只要遇到 err 错误就打 log ,同时 zap.Error(err)打印,然后返给上层错误。 每层都需要打印 log 和返回错误,一般也不会很多层 。
Ayanokouji
262 天前
@aababc error 可以自定义,java 也有自定义的 excetion ,这样做的目的为了统一错误处理。
可以参考 https://github.com/danielgtaylor/huma/blob/main/error.go

但是吧,即使自定义 error ,如果 error 不带堆栈,仅靠 error 大概率还是无法确定错误位置,还是得靠 error + log 来解决。
aababc
262 天前
@Ayanokouji #48 怎么说呢,这就是 go 的特点吧,我们现在的做法就是每层都追加自己的信息,相当于手工造了一个堆栈
Ayanokouji
262 天前
@aababc 是的,我目前也是这么解决的 return fmt.Errorf("xxx:%w", err),其实自定义 error 和 fmt.Errorf 的区别,就是看后续是否需要针对 error 类型细化处理(一般是中间件之类的)。
NotLongNil
262 天前
@Kumo31 这个好,我也在在找类似的库,收藏了。这个能完美满足楼主的需求
nextvay
262 天前
1. 每次返回 err 都 errors.wrap 包一下
2. 再想打印日志的地方,记录 堆栈日志 :fmt.Sprintf("%+v", err)
windcode
262 天前
我的做法是:
1. API/方法返回的 error ,包含错误码和层层 warp 的错误信息( ErrCode 和 Message ),但是不包含错误堆栈
2. 服务端在中间件层统一打印错误堆栈

核心思路是对外的接口是给用户看的,透出的应该是可读性较好、经过抽象的信息,而错误堆栈是给开发者看的,排查问题用的,所以放在日志里。
lwldcr
262 天前
歪个楼 2024 年的最后一天,马上就是新年以及假期,你们居然在这一本正经讨论代码问题
OMGD
262 天前
@lwldcr 哈哈
blur1119
262 天前
最近看最佳实践这词看吐了
dingyaguang117
262 天前
pkg/errors 不是标准做法嘛 😂😂
zhu327808
262 天前
SingeeKing
262 天前
自荐一下我自己基于 pkg/errors 改的 ee 库

以下摘自我的博客:

最优的方案实际上是全局使用第三方库。这里推荐使用我自己的 ee 错误处理库( https://pkg.go.dev/github.com/ImSingee/go-ex/ee ),其修改自官方的 pkg/errors 库,但基于实际需求做了一定的优化:

1. (相比标准库)为所有的错误都包装了调用栈信息。
2. 对于已经存在调用栈信息的,不会覆盖(来保证永远可以拿到最深层的调用栈信息)。
3. 支持在 WithStack 时指定 skip 来使用上层栈(用于编写工具函数)。
4. 栈信息的 StackTrace 和 Frame 可访问,以供外部工具(例如日志处理库)结构化利用。
5. 增加 Panic 函数,调用时会自动生成 error 并记录 panic 位置信息。
6. 所有 error 都实现了 TextMarshaler 接口,对序列化友好。
oaix
262 天前

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1101542

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX