到目前为止我已经避免使用log.Fatal
,但我最近偶然发现了这些问题;代码覆盖率 https://stackoverflow.com/questions/33873305/how-to-get-100-code-coverage-in-golang and 使用日志致命测试 https://stackoverflow.com/questions/30688554/how-to-test-go-function-containing-log-fatal.
100 个代码覆盖率问题的评论之一说:
...在绝大多数情况下log.Fatal
应该只在 main 或 init 函数中使用(或者可能有些东西只能直接从它们调用)”
这引起了我的思考,所以我开始查看 Go 提供的标准库代码。有很多例子表明test库中的代码利用log.Fatal
这看起来不错。测试代码之外还有一些示例,例如net/http https://golang.org/src/net/http/transport.go?h=log.Fatal#L408,如下图所示:
// 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
这是可以恢复的,我理论上长期运行稳定的应用程序可能有机会浴火重生。
那么 Go 的最佳实践是关于什么时候应该使用 log.Fatal 的呢?
可能只有我这么想,但这就是我的使用方式log.Fatal
。根据 UNIX 约定,遇到错误的进程应尽早失败并返回非零退出代码。这引导我遵循以下使用指南log.Fatal
when…
- …我的任何一个都发生错误
func init()
,因为这些分别发生在处理导入时或调用主函数之前。相反,我只做不直接影响库或 cmd 应该做的工作单元的事情。例如,我设置日志记录并检查我们是否有健全的环境和参数。如果我们有无效标志,则无需运行 main,对吧?如果我们无法提供适当的反馈,我们应该尽早告知。
- …发生了我知道无法恢复的错误。假设我们有一个程序,可以创建命令行上给出的图像文件的缩略图。如果此文件不存在或由于权限不足而无法读取,则没有理由继续,并且此错误无法恢复。所以我们遵守惯例并失败了。
- …过程中发生的错误可能是不可逆的。我知道,这是一个软定义。让我举例说明一下。假设我们有一个实现
cp
,并且开始以非交互方式递归复制目录。现在,假设我们在目标目录中遇到一个文件,该文件与要复制到那里的文件具有相同的名称(但内容不同)。由于我们无法要求用户决定要做什么,并且我们无法复制该文件,因此我们遇到了问题。因为当我们以退出代码零完成时,用户会假设源目录和目标目录是精确的副本,所以我们不能简单地跳过有问题的文件。但是,我们不能简单地覆盖它,因为这可能会破坏信息。这是我们无法从用户的每个明确请求中恢复的情况,所以我会使用log.Fatal
说明情况,特此遵循尽早失败的原则。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)