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

标签:go,error,工具
0
投稿

猜你喜欢

  • 关于通过Java连接mysql对反斜杠”\\“转义的测试详解

    2024-01-27 06:52:59
  • python实现输入任意一个大写字母生成金字塔的示例

    2022-02-04 10:27:19
  • ACCESS如何打印窗体中当前显示的记录

    2008-11-20 16:31:00
  • Bootstrap Paginator分页插件使用方法详解

    2023-09-05 23:08:42
  • Python实现简易的图书管理系统

    2021-09-12 06:06:21
  • Vue3 ref构建响应式变量失效问题及解决

    2023-07-02 16:33:49
  • Python编程实现微信企业号文本消息推送功能示例

    2022-09-30 19:45:19
  • selenium 多窗口切换的实现(windows)

    2021-08-17 03:58:54
  • 百分百成功的全网最简约sklearn环境配置教程

    2022-03-27 13:43:44
  • 基于pytorch padding=SAME的解决方式

    2023-03-03 05:05:28
  • Python列表切片操作实例总结

    2023-01-30 16:06:57
  • my.ini(my.cnf)与mysql优化指南

    2009-12-15 16:20:00
  • Firefox下正则诡异问题

    2009-08-03 14:03:00
  • python实现支持目录FTP上传下载文件的方法

    2023-04-13 02:40:33
  • python简单实现刷新智联简历

    2023-09-08 06:49:44
  • Python PyInstaller安装和使用教程详解

    2021-05-12 01:28:40
  • MYSQL数据库教程:唯一编号

    2009-02-27 15:27:00
  • Java通过MyBatis框架对MySQL数据进行增删查改的基本方法

    2024-01-17 10:27:12
  • python解决12306登录验证码的实现

    2023-05-29 10:28:35
  • Python中pywifi模块的基本用法讲解

    2023-09-23 00:21:00
  • asp之家 网络编程 m.aspxhome.com