日志规范

日志规范

我发现要包含在日志消息中的最重要的信息,应当是在代码中同一路径的不同执行之间有所不同的属性。这主要归结为通过不同 IO 通道提供的数据,无论是用户输入还是从数据库读取数据。固定好这些变量后,将日志事件映射到代码并了解系统行为通常相对容易。

NOT TOO MUCH

即使现代的日志记录系统可以处理大量日志消息,也最好限制记录的内容。一些过度记录的最坏情况也没有什么价值。例如,如果跳过条件很简单,则“跳过的 margin_data_available 类型的消息”只是杂音,应将其删除。同样,如果代码中的点 A 总是紧跟在点 B 之后,那么就足以在点 B 记录日志(可能包括来自点 A 的信息),而不是两个日志语句。

NOT TOO LITTLE

尽管有时记录过多有时会引起问题,但记录不足的情况更为常见。这尤其适用于所有类型的故障(预期的和意外的)。通常在调试问题时,您想知道任务失败的方式和原因。如果没有所有故障模式的日志记录,那么有时您可能会猜测。

ADD DYNAMIC INFORMATION

通常,该日志消息没有得到应有的帮助。例如,可以通过添加服务器 IP 地址和端口来改进“已连接到发票服务器”。另一个例子:在工作中,我们通过从文件中读取日历数据来初始化内存数据库。创建数据库时的日志消息包括文件名和文件包含的数据行数。解决由于数据文件被截断而引起的问题时,此日志消息是关键。通常,尝试添加尽可能多的动态信息

GREP-ABLE MESSAGES

即使日志消息中自动包含文件名和行号,也要使日志消息中的文本唯一且易于 grep 处理,这是一个好主意。这使得在代码中查找 log 语句变得容易和快速。如果日志消息是基于字符串构建的,请确保仍易于使用 grep 进行查找。很多地方的“失败:%s%s”使这一点变得很困难。如果很难在文本中描述不同的情况,您甚至可以使用“失败(1):%s%s”等来区分情况。

如果日志记录是在从许多地方调用的函数中完成的,请考虑为该函数添加一个额外的参数以描述该情况。这样,在日志消息中就可以清楚地知道是哪种情况。

RETURNING A MESSAGE LIST

当有什么逻辑可以做时,我喜欢将该逻辑放入可以自己进行单元测试的函数中。如果逻辑很复杂,我还想生成一些日志消息,解释所采取的决定。有很多方法可以做到这一点,但是我喜欢的一种方法是返回一个元组。元组的第一个元素是结果,第二个元素是要输出的日志消息列表(可能为空)。调用函数后,如果消息列表不为空,则将条目串联起来并作为日志消息输出。

这样,很容易测试结果是否正确以及日志消息是否符合我的期望。它是一个列表而不是一个字符串的原因是,在如何做出决定方面通常可以有多个重要方面。例如,配置强制使用保证金类型,并且协议的开始日期是将来的日期。两者都可能与该协议不应在此期间开具发票的决定有关。同样,即使列表中有多个项目,它仍然只是一个 log 语句,而不是多个,从而减少了日志记录。

NO SCREAMING

有时我在代码中看到这样的日志消息:“#### Could not fetch agreement data for ABC-DEF-CSA”. 不可避免地会有类似的消息开头,例如“ ####-”等。最终,所有这些都演变成一场竞赛,其日志消息可以大声喊叫。日志消息中不应包含任何特殊字符以引起您的注意。让任何人在查看日志时都可以决定他们要查看的内容。唯一可以接受的区别是使用不同的日志级别,例如 WARNING 和 INFO。

迭代

第一次尝试就很难获得正确的日志消息。我对自己所看到的感到满意之前,常常需要尝试几次。查看输出,并根据需要调整消息。例如,我在未开具发票时记录了协议 UUID。但是事实证明,记录协议名称和参与方名称而不是 UUID 更为有用,因此我进行了更改。

此外,如果您要解决问题,并且日志中没有您需要的所有信息,请记住将其添加。

杂项

有时,日志记录看似多余,但仍然有用。例如,在工作中,我们每分钟都有一次心跳 API 调用。有一个度量标准,如果未收到警报,则发送警报。但是,我还添加了接收日志的功能。由于只有分钟,因此不会产生太多输出。它有助于了解系统中正在发生的事情。此外,一旦指标收集出现问题,最好能够检查日志并查看心跳是否仍在。