到目前为止,我一直避免使用log.Fatal,但我最近偶然发现了这两个问题:code-coveragetests-using-log-fatal.

100个代码覆盖问题中的一条 comments 说:

...在绝大多数情况下,log.Fatal应该仅在main或init函数中使用(或者可能在某些只能直接从它们调用的函数中使用)."

这让我开始思考,所以我开始查看Go提供的标准库代码.库中的test代码使用log.Fatal的例子有很多,这看起来不错.在测试代码之外有几个示例,例如在net/http中,如下所示:

// net/http/transport.go 
func (t *Transport) putIdleConn(pconn *persistConn) bool {
    ...
    for _, exist := range t.idleConn[key] {
        if exist == pconn {
            log.Fatalf("dup idle pconn %p in freelist", pconn)
        }
    }
    ...
}

如果避免使用log.Fatal是最佳实践,那么为什么要在标准库中使用它,我应该只返回一个错误.调用os.Exit而不为应用程序提供任何清理机会似乎对库的用户不公平.

我可能太天真了,所以我的问题似乎是更好的做法是拨打log.Panic,这是可以回收的,而我理论上长期运行的稳定应用程序可能有机会起死回生.

那么,什么时候应该使用log.FATAL应该使用GO的最佳实践说明是什么呢?

推荐答案

可能只有我一个人,但以下是我使用log.Fatal的方法.根据UNIX约定,遇到错误的进程应该尽早失败,并返回非零退出代码.这将我引向以下使用log.FatalWhen…的指导原则

  1. …my func init()中的任何一个都会发生错误,分别是在处理导入时或在调用main func之前.相反,我只做不会直接影响库或cmd应该做的工作单元的事情.例如,我设置了日志(log)记录,并判断我们是否有一个健全的环境和参数.如果我们有无效的标志,就不需要运行main,对吗?如果我们不能给出适当的反馈,我们应该尽早告诉他们.
  2. …一个错误发生了,我知道它是无法恢复的.假设我们有一个程序,它创建命令行上给定的图像文件的缩略图.如果此文件不存在或由于权限不足而不可读,则没有理由继续,并且无法恢复此错误.所以我们固守惯例,结果失败了.
  3. …在可能不可逆的过程中发生错误.我知道,这是一种软性的定义.让我来说明一下.假设我们有一个cp的实现,并且它开始是非交互的,并且递归地复制一个目录.现在,假设我们在目标目录中遇到一个文件,该文件与要复制到目标目录的文件同名(但内容不同).因为我们不能要求用户决定要做什么,也不能复制这个文件,所以我们有一个问题.因为当我们结束退出代码0时,用户会假设源目录和目标目录是完全相同的副本,所以我们不能简单地跳过有问题的文件.但是,我们不能简单地覆盖它,因为这可能会 destruct 信息.这是一种我们无法根据用户的显式请求进行恢复的情况,因此我将使用log.Fatal来解释这种情况,并在此遵循尽早失败的原则.

Go相关问答推荐

golang gin何时使用收件箱上下文以及何时重定向

杜松子wine -戈尼克背景在 children 围棋例行公事中被取消

如何使用GRPC UnaryClientInterceptor中的`maily`参数?

Golang使用Run()执行的命令没有返回

如何在Golang中覆盖404

如何解析Go-Gin多部分请求中的 struct 切片

Go:为什么我不能比较 net.Addr

日志(log)文件不在 golang 的日志(log)目录中

Cypher 查找(多个)最低 node

为什么 Go 对于长度为 100k 的切片使用的内存比长度为 100k 的数组要少?

无法读取postman 中的表单数据

如何将已知类型转换为指向switch 中类型参数的指针?

如何将一片 map 转换为一片具有不同属性的 struct

如何模仿联合类型

NaN 是 golang 中的可比类型吗?

如何使用特定的 Go 版本运行 govulncheck?

级联调用泛型函数时的泛型类型推断

gorm 获取列名

如何在gorm中处理多个查询

使用不安全的指针从 [] 字符串中获取值