go 语言优雅地处理 error

简介: go 语言优雅地处理 error

我看到很多 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

你也可以考虑在生产环境中关闭你的跟踪打印,或者通过检查其他配置值。

相关文章
|
9天前
|
存储 JSON 监控
Viper,一个Go语言配置管理神器!
Viper 是一个功能强大的 Go 语言配置管理库,支持从多种来源读取配置,包括文件、环境变量、远程配置中心等。本文详细介绍了 Viper 的核心特性和使用方法,包括从本地 YAML 文件和 Consul 远程配置中心读取配置的示例。Viper 的多来源配置、动态配置和轻松集成特性使其成为管理复杂应用配置的理想选择。
30 2
|
8天前
|
Go 索引
go语言中的循环语句
【11月更文挑战第4天】
19 2
|
8天前
|
Go C++
go语言中的条件语句
【11月更文挑战第4天】
21 2
|
13天前
|
Ubuntu 编译器 Linux
go语言中SQLite3驱动安装
【11月更文挑战第2天】
37 7
|
13天前
|
关系型数据库 Go 网络安全
go语言中PostgreSQL驱动安装
【11月更文挑战第2天】
48 5
|
12天前
|
安全 Go
用 Zap 轻松搞定 Go 语言中的结构化日志
在现代应用程序开发中,日志记录至关重要。Go 语言中有许多日志库,而 Zap 因其高性能和灵活性脱颖而出。本文详细介绍如何在 Go 项目中使用 Zap 进行结构化日志记录,并展示如何定制日志输出,满足生产环境需求。通过基础示例、SugaredLogger 的便捷使用以及自定义日志配置,帮助你在实际开发中高效管理日志。
32 1
|
12天前
|
程序员 Go
go语言中的控制结构
【11月更文挑战第3天】
88 58
|
3天前
|
Go API 数据库
Go 语言中常用的 ORM 框架,如 GORM、XORM 和 BeeORM,分析了它们的特点、优势及不足,并从功能特性、性能表现、易用性和社区活跃度等方面进行了比较,旨在帮助开发者根据项目需求选择合适的 ORM 框架。
本文介绍了 Go 语言中常用的 ORM 框架,如 GORM、XORM 和 BeeORM,分析了它们的特点、优势及不足,并从功能特性、性能表现、易用性和社区活跃度等方面进行了比较,旨在帮助开发者根据项目需求选择合适的 ORM 框架。
16 4
|
3天前
|
缓存 监控 前端开发
在 Go 语言中实现 WebSocket 实时通信的应用,包括 WebSocket 的简介、Go 语言的优势、基本实现步骤、应用案例、注意事项及性能优化策略,旨在帮助开发者构建高效稳定的实时通信系统
本文深入探讨了在 Go 语言中实现 WebSocket 实时通信的应用,包括 WebSocket 的简介、Go 语言的优势、基本实现步骤、应用案例、注意事项及性能优化策略,旨在帮助开发者构建高效稳定的实时通信系统。
22 1
|
6天前
|
Go
go语言中的continue 语句
go语言中的continue 语句
16 3