Go 包是否应该使用 log.Fatal 以及何时使用?

来自 100 个代码覆盖率问题的评论之一说:


...在绝大多数情况下log.Fatal应该只在 main 或 init 函数中使用(或者可能是一些只能直接从它们调用的东西)”


这让我思考,所以我开始查看 Go 提供的标准库代码。库中的测试代码使用了很多示例,这些示例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哪些可以恢复,并且我理论上的长期运行稳定的应用程序可能有机会从灰烬中崛起。


那么对于 Go 什么时候应该使用 log.Fatal 的最佳实践是什么?


繁星coding
浏览 172回答 2
2回答

守着一只汪

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

蓝山帝景

我认为它非常好,非常有见地,我倾向于同意你的崩溃。很难一概而论,尽管作为 Go 的新手,我一直在考虑更多。我认为在理论层面上,如果我们寻求计算中的最佳实践,无论操作系统、包框架或库如何,记录器的责任就是简单地记录。在任何级别,记录员的职责:将信息格式化和打印到我选择的频道。分类、过滤、显示不同的日志级别 [debug、info、warn、error]跨异步和并行作业处理和聚合日志条目如果程序运行正常,日志包或任何包没有也不应该有使程序崩溃的权限。任何中间件或库都应该遵循 throw/catch 模式,并有机会让调用者捕获所有抛出的异常。这也是在应用程序中遵循的一个很好的模式,当您构建为应用程序的各个部分以及可能的其他应用程序提供支持的基础和包时,它们永远不应该直接使应用程序崩溃。相反,他们应该抛出一个致命的异常,让程序来处理。我认为这也解决了 Marcus 的一些观点,因为它可以在未被发现时立即提醒呼叫者,这是致命的崩溃。在大多数情况下,我可以直接在 Go 中利用 log.Fatal 来实现直接面向用户的 cli 工具,我认为这是它真正想要的简单性。我认为作为处理跨包致命错误的长期方法没有什么意义。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go