让我们谈谈记录

这篇文章的灵感来自Nate Finch发起的Go论坛主题。 这篇文章的重点是Go,但是如果您忽略了这一点,我认为此处介绍的想法将广泛适用。

为什么没有爱?


Go中的日志包没有日志级别;您可以自己手动添加DEBUG,INFO,WARN和ERROR前缀。 此外,Go中的记录器类型无法单独为选定的软件包启用或禁用这些级别。 为了进行比较,让我们看一下第三方开发人员的一些替代产品。

图片

Google glog具有以下级别:

  • 资讯
  • 警告
  • 失误
  • 致命的(终止程序)

让我们看一下为Juju开发的另一个库loggo ,其中有可用的级别:

  • 痕迹
  • 除错
  • 资讯
  • 警告
  • 失误
  • 关键的

Loggo还可以单独设置所需软件包的日志详细级别。

这是两个示例,显然是在其他库的影响下创建的,用于以其他语言记录。

实际上,它们的起源可以追溯到syslog(3),甚至可能更早。 我认为他们是错的。

我想采取有争议的立场。 我认为所有日志库都是不好的,因为它们提供了太多的功能。 令人震惊的功能集使程序员在需要思考如何与以后与读者交流以及与谁来阅读这些杂志的时候立即震惊。

我认为成功的日志包所需的功能要少得多,当然所需的级别也要少得多。

让我们谈一谈警告(警告)


让我们从最简单的开始。 没有人需要警告日志级别(警告)。

没有人阅读警告,因为根据定义,没有发生任何不良情况。 也许将来可能会出问题,但这听起来像是其他人,不是我的问题。

另外,如果使用某种多级别日志记录,为什么需要设置警告级别? 您将级别设置为INFO或ERROR。 设置警告级别意味着您可能正在报告警告级别的错误。

排除警告级别-这是参考消息或错误。

让我们谈谈致命错误的程度


致命级别实际记录该消息,然后调用os.Exit(1)。 原则上,这意味着:

  • 其他例程(goroutine)中的延迟表达式不执行;
  • 缓冲区不刷新;
  • 临时文件和目录不会被删除。

本质上,log.Fatal不太冗长,但在语义上等同于恐慌。

通常认为,库不应该使用panic1,但是如果调用log.Fatal2具有相同的效果,则也应该禁用它。

可以通过在记录器中注册关闭处理程序来解决此清除问题的假设提供了所使用的记录器与发生清除操作的每个位置之间的紧密关系。 这也违反了利益分离。

不要记录致命级别的消息,而希望将错误返回给呼叫者。 如果错误到达main.main,那么这是在关闭程序之前执行任何清理的正确位置。

让我们谈谈错误(ERROR级别)


错误处理和日志记录密切相关,因此,乍一看,应该很容易地证明错误级别日志记录(ERROR)是正确的。 我不同意。

在Go中,如果调用函数或方法返回错误值,那么您实际上有两个选择:

  • 处理错误。
  • 向调用者返回错误。 您可以将错误包装在礼品包装纸中(包装纸),但这对本次讨论并不重要。

如果决定通过将错误写入日志来处理错误,那么根据定义,它不再是错误-您已经处理了它。 错误本身的注册行为是对错误的处理;因此,不再建议将其作为错误写入日志。

让我用这段代码说服您:

err := somethingHard() if err != nil { log.Error("oops, something was too hard", err) return err // what is this, Java ? } 

绝对不要在错误级别注册任何东西,因为您必须处理错误或将其传递给调用方。

您需要清楚地了解,我并不是说您不应该在日志中写明状态已发生更改:

 if err := planA(); err != nil { log.Infof("could't open the foo file, continuing with plan b: %v", err) planB() } 

但实际上,log.Info和log.Error具有相同的目标。

我不会说“不要注册错误”! 相反,我问一个问题,最小的记录API是什么? 当涉及到错误时,我相信在ERROR级别记录的绝大多数事情都是简单完成的,因为它们与错误有关。 实际上,它们只是提供信息,因此我们可以从API中删除错误级别的日志记录(ERROR)。

还剩下什么?


我们排除了警告,认为在错误级别(ERROR)不应记录任何内容,并表明只有顶级应用程序才应具有某种log.Fatal行为。 还剩下什么?

我认为您应该只登录两件事:

  • 开发人员在开发或调试程序时关心的事情;
  • 用户在使用您的程序时关心的事情。

显然,它们分别是调试级别(DEBUG)和信息级别(INFO)。

log.Info应该只将此行写入日志输出。 禁用它应该是不可能的,因为用户应该只告诉他什么对他有用。 如果发生无法处理的错误,则该错误应出现在程序退出的main.main中。 给最终日志消息之前插入FATAL前缀或使用fmt.Fprintf直接写入os.Stderr带来的不便之处,不足以用log.Fatal方法扩展软件包。

log.Debug,这是完全不同的事情。 开发人员或支持工程师需要它来控制程序。 在开发过程中,调试表达式应该很多,而不必求助于跟踪级别或debug2(您知道自己是谁)。 对于程序包中所需的程序包,甚至可能在较窄的范围内,日志记录程序包必须支持粒度控制以启用或禁用调试表达式。

结论


如果是Twitter的民意调查,我会请您选择

  • 记录很重要
  • 记录很难

但是事实是日志记录是两者兼而有之。 解决这个问题的方法必须是销毁和无情地减少不必要的干扰。

你觉得呢 是足够奢侈的工作,还是仅仅是奢侈的?

注意事项


一些库可能使用panic / recover作为内部控制流机制,但是主要的主张是它们不应允许这些控制流操作泄漏到程序包边界之外。

具有讽刺意味的是,尽管它没有DEBUG输出级别,但标准的Go日志记录包却具有致命和恐慌功能。 在此程序包中,导致程序突然终止的功能数量超过了没有终止功能的数量。

关于作者


本文的作者Dave Cheney是Go的许多流行软件包的作者,例如github.com/pkg/errorsgithub.com/davecheney/httpstat 。 您可以自己评估作者的权威和经验。

来自翻译


许多开发人员都在关注日志记录,一些开发人员讨论了在标准Go库中添加Logger接口以简化库中的日志记录并以此方式刺激开发人员。 这些家伙甚至起草了他们的提案,并把文件进行了讨论。

加上演示文稿反思我们需要一个新的记录器,它应该是什么? 来自克里斯·海因斯(Chris Hines)。

Dave的go-log想法有几种实现方式,在ERROR级别问题上还有一些话题外,还有一个更复杂的logr程序包。

Source: https://habr.com/ru/post/zh-CN440200/


All Articles