自己是 Java 后台开发,尝试用 Go 写了个小项目,有几个工程实践上的问题想咨询下各位都是怎么处理的?
if err != nil
么?抛开工程上的这些问题,Go 还是蛮好的,找回了刚上学时候写 C 的快感,真就是单纯地在一行一行写代码,感觉每一行都能映射到汇编上,掌控性很强。感觉更适合强规范的基础设施开发和工具开发。
接下来想去看看 Rust ,不知道会不会遇到类似的问题。毕竟语言再好,最终还是要用来干活儿的。对于业务开发,CRUD boy 来说,写代码和查问题这种场景搞不定,语言再好也是麻烦。
1
lix7 OP 看到这么一篇文章,貌似日志规范这个事情在 2022 年还没有什么解决方案
[聊聊 Go 应用输出日志的工程实践 | Tony Bai]( https://tonybai.com/2022/03/05/go-logging-practice/) |
2
MoYi123 2022-07-01 16:37:58 +08:00
1. 比如 xorm 有个 setlogger 的功能, 需要把你的 logger 按他的 interface 封装一下, 就可以接入统一的日志了, grpc 和 echo 同理. 一般都会有这个功能, 但是不排除某些第三方库不支持.
2. 一般都是 context 传 3. 多数情况就是 if err != nil,或者用 recover 也行吧. |
4
BeautifulSoap 2022-07-01 16:50:07 +08:00
1. 没有。不同包往往都有自己的日志输出逻辑,完全看包的作者
2. requestId 就是 context 层层往下传。context 的作用之一就是这个这么做没关系的,算是 Go 的标准做法。并且你要知道,Go 协程、Go 协程,这里不是 Go 多线程。不同的网络请求很可能会跑在同一个线程里的 3. err 一层层往上返回也是 Go 的标准做法,if err != nil 有人觉得麻烦有人觉得无所谓(我属于后者)。不过因为现在官方 go 的 errors 包不含调用栈信息,所以实际上用得最多的还是 github.com/pkg/errors 这个包 |
5
IIInsomnia 2022-07-01 17:04:14 +08:00
可以参考一下这个: https://github.com/shenghui0779/tplgo
|
6
yc8332 2022-07-01 17:22:06 +08:00
你说的 java 的是生态决定的吧,只是那些包都支持了那个日志组件。
|
7
LoNeFong 2022-07-01 17:38:54 +08:00
1.基本正经的库都不会乱打日志,一般都是 error 级别的才有,捕获 wrap 往上抛出即可,业务代码中使用的是 zap ,提出全局的 logger 配置到公共库
2.我研究过好多 web 框架 requestId 都是通过 ctx 传递的,这种做法也还是合理的,毕竟一层层传递 ctx 不光是为了一个 requestId 3.v 站有很严重的争议问题,怎么说怎么有理,我选择优先处理错误: https://go.dev/blog/errors-are-values |
8
haolongsun 2022-07-01 17:49:54 +08:00
rust 做的非常好,标准库只定义了 log trait ,然后让第三方库去自己实现,这样不管什么框架用的啥,都要回归到标准 log ,这样统一全局。
|
9
janxin 2022-07-01 18:08:09 +08:00
第一个问题是生态软件没有一统江湖的选择,然而事实上也不能这么依赖第三方的依赖,毕竟这个选择权归作者所有,作者不用就抓瞎。Go 的第三方库基本这个问题就是看是否有预定抽象接口,替换一下即可。不过这也是作者行为,作者不允许你改,你能做的只有重定向输出。
第二个问题建议使用 context 下传,后面很多东西比较方便处理。如果实在不想,也有 gls 这些第三方方案可以用。但是这些方案不保证兼容性。周边生态基本是 context 兼容的,如果使用 gls 很难和这些方案结合,比如分布式追踪。选择 gls 另外一个问题和 goroutine 有关,新 goroutine 的创建时就需要额外处理很多东西了。这个在并发时会经常遇到。 第三个问题无解,就是辣么烦。不过想偷懒的话,Go1.18 之后有第三方库快速处理一下,比如 https://pkg.go.dev/github.com/samber/lo#readme-try |
10
lix7 OP @IIInsomnia 看了下,感觉还不错,很规整,star 了
|
11
lix7 OP @yc8332 但是基本上 Java 生态里所有的三方组件都会支持这个日志组件,已经是生态里日志规范的事实标准了。Go 发展也有快十年了,出现这样一个社区标准也不意外
|
13
lix7 OP @haolongsun 这个也太 nice 了!简直是理想状态了,我去了解了解
|
14
KevinBlandy 2022-07-01 18:37:09 +08:00
我想补充一个问题:
Go 里面如何优雅的处理事务呢?最好能实现类似于 spring 那种事务管理,自动回滚,提交。并且事务方法之间的调用保证在同一个事务之中。 |
15
haolongsun 2022-07-01 19:47:06 +08:00
|
16
haolongsun 2022-07-01 19:48:21 +08:00
|
17
guoer 2022-07-01 19:58:29 +08:00
无解
其实 1,2,3 中的痛点都是 Go 的推荐做法 XD |
18
blless 2022-07-02 15:00:26 +08:00
@KevinBlandy 我们以前是 web 框架用 gin ,事务封装成一个 gin 中间件,进入业务处理前先获取一个 db 事务对象,业务处理完成后提交就行。如果是无状态的业务同时有多个进程,中间可能要加个 redis 分布式锁,事务提交前要先检查锁是否过期,过期了就不能提交。
|
19
blless 2022-07-02 15:10:40 +08:00
1 、各个组件的日志看组件有没有提供 Logger 接口,有的话一般是把全局的 Logger 单独实现一个组件的 Logger 然后传进去,但是其实我们以前公司是不允许组件输出太多 Log 的。不然很容易就导致日志量暴表。
2 、据我所知大部分框架 RequestId 之类的还真是靠 context 往下传的,context 其实在 go 里面真的很有用,因为协程的生命周期都需要用 context 来控制。基本上你可以认为 context 就是用来跟协程进行绑定的东西,你不用 context 往下传,协程处理的生命周期就会断开,导致一些未知的 bug. 3 、Java 也不是什么地方都可以随便 Try catch 的,正常业务异常都需要 throw 出去,不然可能会丢失原始的错误信息,导致出现 bug 的时候无法排查。少数比如网络重试之类的异常可以直接 catch 掉 重试,go 里面 你要想省事就直接 进入协程处理业务进去的时候写一个 recover ,然后业务里面出错直接 panic 。我们以前就这么干,web 业务应用无所谓的。但是基础组件,中间件,我们不允许 panic |