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

259 天前
 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 (含调用栈) 强吗。
6138 次点击
所在节点    Go 编程语言
81 条回复
xxlxiaxiaolei
259 天前
@guanzhangzhang 真张馆长?我还有你 QQ 呢
Linxing
259 天前
WithStack
pkoukk
259 天前
一般我不会携带堆栈信息,太多了,去获取当前堆栈的资源消耗也太重了。
其实很简单啊,error 是个 interface ,return error 的时候变成 retrun MyError(code,err)就行了
额外附加一个 code 足矣.
这是个简单的示例,实际上还可以做很多事情在里面

func MyError(code int, err error) error {
if errors.As(err, &myError{}) {
return err
} else {
return &myError{code: code, source: err}
}
}
lifei6671
259 天前
@Ayanokouji #2 golang 的 1.21 可以直接用%w 来包裹 error ,也可以用 errors.join 来合并多个 error 。不需要第三方库了。
pkoukk
259 天前
对你的 APPEND 的回复:
那你直接用 panic ,那玩意里面自带堆栈,去上游 revocer 。
喜欢 try catch 的,用 panic recover 去
Nazz
259 天前
我的做法是用 github.com/pkg/errors 带上错误堆栈, 用 zerolog 设置错误级别, 在响应函数里面写入日志, lumberjack 做日志切分
Ayanokouji
259 天前
@pkoukk 你是二极管吗,要看调用栈就得用 panic 啊,你家写 go 全是 panic 啊。前面写那么多看了吗,还是说你写过 go 吗。有能耐把你的解决方法写出来啊。
Vitumoc
259 天前
没有特别看懂 OP 想要表达什么。

如果只是想要调用栈的话,并不困难啊?

大概就这样:

```go
import "runtime/debug"

func main() {
defer func() {
stack := debug.Stack()
fmt.Sprintf("调用栈信息:\n%s", stack)
}
}
```

实际用的时候再做一些封装
Ayanokouji
259 天前
@Vitumoc 不是非必要用调用栈,一切为了问题排查,就比如日志突兀的出现一句:Error: unexpected '>' at the beginning of value 。这种情况知道是 json 错误,但是哪里发生的 json 错误,参数是什么之类的,定位解决问题的时候,很困难
pkoukk
259 天前
@Ayanokouji #27 我写了,你没看,你才是二极管
pkoukk
259 天前
@Ayanokouji #27 这么简单的解法都想不出来,怀疑你的水平
Ayanokouji
259 天前
@pkoukk 所以你家 go ,全是 panic recover ? java 的 try catch = go 的 panic recover ?
p1gd0g
259 天前
没太看懂。
我们这边错误码都是封装好的,每次使用都是 new 出来的,自带简略堆栈信息和代码行号。在返回上层的时候就不再处理 error 了,也没必要。第三方的错误码转掉,保证返回网关时错误码都是同一套。线上再搭配链路追踪,查问题足够了。
不知道能不能解决你的问题。
依稀记得官方库已经支持 error wrap 了所以不再需要 github.com/pkg/errors ,没细看过不保真。
Kumo31
259 天前
可以看下这个库: https://github.com/samber/oops
qW7bo2FbzbC0
259 天前
@Ayanokouji 在同一种错误需要多次使用的情况下,我也是参考开源项目,对错误进行实例化,然后在项目内返回这个实例化的错误。至于国际化,我不太清楚,我尽量都用英文定义错误和打印日志
Ayanokouji
259 天前
@p1gd0g 官方只支持了 %w ,错误码封装的思路,需要自定义 error 。应该和 huma 的思路类似
https://github.com/danielgtaylor/huma/blob/main/error.go
zoharSoul
259 天前
很多人没写过那种复杂业务, 他理解不了这些场景
tiedan
259 天前
只要遇到就应该打印日志
p1gd0g
259 天前
@Ayanokouji #36
哦哦谢谢补充,做全栈后好久没看 go 的信息了。

其实我想问,你们前端是怎么处理错误码的?如果前后端用同一套错误码,那自定义一套错误码应该是比较自然的吧。
aababc
259 天前
同样的问题,我也问过,怎么说呢,感觉 golang 这边没有啥统一的做法,看了看大佬意思就是 error 是一个 interface ,你自己想咋封装都可以

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

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

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

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

© 2021 V2EX