go语言优雅地处理error工具及技巧详解

 更新时间:2022年11月24日 09:27:37   作者:洛天枫  
这篇文章主要为大家介绍了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

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

以上就是go语言优雅地处理error工具及技巧详解的详细内容,更多关于go语言处理error工具的资料请关注脚本之家其它相关文章!

相关文章

  • Go语言异常处理案例解析

    Go语言异常处理案例解析

    这篇文章主要介绍了Go语言异常处理案例解析,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • Go GORM版本2.0新特性介绍

    Go GORM版本2.0新特性介绍

    这篇文章主要为大家介绍了Go GORM版本2.0新特性的使用示例介绍,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • 利用Go语言实现在终端绘制小兔子

    利用Go语言实现在终端绘制小兔子

    这篇文章主要为大家详细介绍了如何利用Go语言实现在终端绘制小兔子来给大家拜个早年,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下
    2023-01-01
  • go test 命令示例详解

    go test 命令示例详解

    go test是Go用来执行测试函数(test function)、基准函数(benchmark function)和示例函数(example function)的命令,这篇文章主要介绍了go test 命令,需要的朋友可以参考下
    2023-11-11
  • GO语言实现文件上传代码分享

    GO语言实现文件上传代码分享

    本文给大家分享的是一则使用golang实现文件上传的代码,主要是使用os.Create创建文件,io.Copy来保存文件,思路非常清晰,这里推荐给大家,有需要的小伙伴参考下吧。
    2015-03-03
  • 详解Golang time包中的结构体time.Time

    详解Golang time包中的结构体time.Time

    在日常开发过程中,会频繁遇到对时间进行操作的场景,使用 Golang 中的 time 包可以很方便地实现对时间的相关操作,本文先讲解一下 time 包中的结构体 time.Time,需要的朋友可以参考下
    2023-07-07
  • Go语言中strings和strconv包示例代码详解

    Go语言中strings和strconv包示例代码详解

    这篇文章主要介绍了Go语言中strings和strconv包示例代码详解 ,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-11-11
  • 一文搞懂Go语言操作Redis的方法

    一文搞懂Go语言操作Redis的方法

    Redis是一个开源的内存数据库,在项目开发中redis的使用也比较频繁,本文介绍了Go语言中go-redis库的基本使用。感兴趣的小伙伴们可以参考借鉴一下
    2022-09-09
  • 详解Go语言中配置文件使用与日志配置

    详解Go语言中配置文件使用与日志配置

    这篇文章主要为大家详细讲解一下Go语言中调整项目目录结构、增加配置文件使用和增加日志配置的方法,文中示例代码讲解详细,需要的可以参考一下
    2022-06-06
  • Go语言使用读写OPC详解

    Go语言使用读写OPC详解

    这篇文章主要介绍了Go语言使用读写OPC详解,图文讲解的很清晰,有感兴趣的同学可以学习下
    2021-03-03

最新评论