go语言优雅地处理error工具及技巧详解
作者:洛天枫 发布时间:2023-07-16 18:56:56
引言
我看到很多 golang
社区的开发者,特别是因为它的简单性而被吸引的开发者,对 golang
中的事情应该如何处理做出了一些快速的判断。
其中一件事就是错误处理。由于目前大多数语言的开发者都来自于 OOP
背景,他们习惯于处理异常,或者说"抛出"异常的概念来停止当前的应用程序的流程,而且他们大多认为这也是 golang
的方式,我们必须在出错的情况下停止我们的应用程序的流程。他们错了!
不要滥用你的工具
我见过很多,我以前也是这样做的。每当有意外情况发生时,就用 os.exit(1)
,然后继续前进。好吧,这不是使用go的正确方法!
我明白为什么这被广泛使用,因为大多数早期的 golang
应用程序只是终端工具,而且许多这些工具曾经使用 .sh
可执行文件来构建,我们曾经只是退出1;以表示刚刚发生了一些意外的事情,我们想退出。
我们把这种习惯带到了我们的 golang 简单的终端应用中,然后又带到了复杂的应用中,这只是另一种 Cargo Cult Programming。 我高度鼓励你在不得不这样做的情况下,要非常小心,因为它是:
随着你的应用程序的增长,非常难以维护。
最重要的是,不可能对这样的代码进行单元测试,这显然表明它的不洁性。
以这种方式退出将阻止你的任何延迟操作的执行,你的程序将立即终止,这可能导致资源泄漏。请考虑一下这个例子:
func main() {
dbConnection := db.connect("...")
defer dbConnection.Close() // this operation won't be executed!
entity := Entity{}
err := dbConnection.Save(entity)
if err != nil {
os.Exit(1)
}
}
考虑传递你的错误
错误只是 golang
中的另一种类型,你必须用它们来控制程序的执行流程。
为了做到这一点,我们必须在整个程序中传播这些错误,直到适当的处理点。
考虑一个管理订单的HTTP API,我们想禁止客户在特定条件下下订单,例如:
package order
// package errors
var (
UnableToShipToCountry = errors.New("unable to ship order to the requested country")
)
type Order struct {
// ... order fields
}
type OrderRepo struct {
DB db
// ...
}
func newOrderFromRequest(o OrderRequest) (Order, error) {
if o.ShippingAddress.Country != "DE" {
return UnableToShipToCountry
} // ... the creation logic
return Order{...}, nil
}
func (r *OrderRepo)PlaceOrder(o OrderRequest) error {
order, err := newOrderFromRequest(o)
if err != nil {
// don't handle the error here, its handling may differ
return err
} // ... db transaction may also return an error
return r.db.Save(order)
}
在我们的 http package
中:
package http
http.HandleFunc("/order", func (w http.ResponseWriter, r *http.Request) {
orderRequest := createOrderRequest(r)
err := orderRepo.PlaceOrder(orderRequest)
if errors.Is(err, order.UnableToShipToCountry) {
w.WriteHeader(http.StatusBadRequest)
return
}
if err != nil {
// this error in case of DB transaction failure
w.WriteHeader(http.StatusInternalServerError)
return
} // ...
w.WriteHeader(http.StatusOK)
})
定制你的错误
我们可以创建我们自己的自定义错误值,并在我们的程序中使用它,同时考虑添加一些有用的信息,如错误跟踪这可能会给我们的日志增加一个有益的价值,特别是在调试期间。
type AppErr struct {
msg string
code int
trace string
}
func (e AppErr) Error() string {
return fmt.Sprintf("Msg: %s, code: %d, trace:\n %s", e.msg, e.code, e.trace)
}
func NewAppErr(msg string, code int) AppErr {
stackSlice := make([]byte, 512)
s := runtime.Stack(stackSlice, false)
return AppErr{msg, code, fmt.Sprintf("\n%s", stackSlice[0:s])}
}
而我们在一个包内有这样一个用例:
package admin
func A() error {
return b()
}
func b() error {
return NewAppErr("error from b function!", 3)
}
main.go:
func main() {
err := admin.A()
fmt.Println(err)
}
记录的错误信息将是:
Msg: error from b function!, code: 3, trace:
goroutine 1 [running]:
./cmd/app/error.NewAppErr({0x1f42b0, 0x17}, 0x7)
./cmd/app/error/error.go:16 +0x35
./cmd/app/admin.b(...)
./cmd/app/admin/**admin.go:12**
./cmd/app/admin.A(...)
./cmd/app/admin/**admin.go:8**
main.main()
./cmd/app/**main.go:10** +0x8d
你也可以考虑在生产环境中关闭你的跟踪打印,或者通过检查其他配置值。
来源:https://juejin.cn/post/7166807817499148302
猜你喜欢
- 网页设计遇到最大的麻烦之一莫过于网页对不同浏览器的兼容性问题了,因为IE 6.0 / IE 7.0 / firefox 2 / Opera
- 1.彻底弄懂CSS盒子模式一(DIV布局快速入门) 2.彻底弄懂CSS盒子模式二(导航栏实例) 4.彻底弄懂CSS盒子模式四(绝对定位和相对
- 某人使用如下过滤代码,发现经常被黑:n=request.form("username") p=request.form(
- 1、slice结构体首先我们来看一段代码package mainimport ( "fmt"
- 本文实例讲述了php生成curl命令行的方法。分享给大家供大家参考,具体如下:示例:curl "http://localhost/
- 译序:本文提到了一种很不错的实现跨浏览器圆角的解决方案,但是说的不够全面,前端观察最近将整理更多更全面的资源给大家,敬请期待。前一段时间,我
- javascript版 俄罗斯方块(Russian box)小游戏,喜欢的朋友可以玩玩。对源代码感兴趣的朋友也可以研究一下。玩法介绍:可以输
- 作为一名数据库管理员,在进行代码迁移之前,我总是尽力给提交于开发环境的代码一个完整的面貌。但是,不得不承认,我不能保证不发生任何可能破坏开发
- TNS简要介绍与应用 Oracle中TNS的完整定义:transparence Network Substrate透明网络底层,监听服务是它
- 最近,在项目开发过程中,碰到了数据库死锁问题,在解决问题的过程中,笔者对MySQL InnoDB引擎锁机制的理解逐步加深。案例如下:在使用S
- 连接数据库:mysql -u用户名 -p密码导入数据 source d:\create.sql用下面的语句就可以导出mysql中的数据了:m
- 万众瞩目的 Firefox 3 从昨天开始可以下载了,用“铺天盖地”四个字来形容她的宣传一点都不为过。大多数的广告还是她的fans自发进行的
- Brendan Eich于1995年在Netscape发明了JavaScript语言,这个语言在过去的一些年里曾是一个最被误解的语言,随着A
- 斐波那契记忆优化法:<script type="text/javascript">var fibonacci
- CSS的出现使网页制作者在对网页元素的控制方便许多,当然,有利必有弊,CSS只能对颜色、大小、距离等静
- 实现在线人数统计最常用的方法就是golobal.asa结合session做,但这种方法有两个不利: 1、每个session要占用12k的服务
- 1. lr_scheduler相关lr_scheduler = WarmupLinearSchedule(optimizer, warmup
- MySQL内部复制功能是建立在两个或两个以上服务器之间,通过设定它们之间的主-从关系来实现的。其中一个作为主服务器,其它的作为从服务器。本节
- javascript上下滑动广告效果 参数说明:客服果果(
- 在使用ASP来进行后端的数据合法性校验的时候,有些人为满足不同环境下面的数据校验,编写了很多的函数来实现,比如,我们想要校验用户输入的URL