高效日志记录:提升软件系统的可维护性

在软件开发过程中,日志记录常常被视作一个辅助工具,但它实际上对于系统的可维护性和可调试性至关重要。良好的日志记录可以成为解决问题的魔法棒,而糟糕的日志记录则可能让人抓狂。在多年的职业生涯中,积累了一些日志记录的经验法则。虽然不敢说这些法则能将日志变成终极的魔法棒,但遵循它们确实能显著减少噩梦般的问题。本文不讨论哪个日志库更好,而是探讨如何编写日志。

假设条件: 系统至少包含两个模块(例如,服务器和客户端)。 至少有100个用户同时使用系统。 用户至少有两种不同的方式使用系统(应用程序)。

日志记录规则: 日志是可交付的,应该像对待UI、API和其他软件系统组件一样对待和测试。 这意味着日志不是随意丢弃任何东西的垃圾箱。日志是见证者,将向它提出以下问题: 发生了什么? 什么时候发生的? 为什么会发生? 客户的声明是否正确? 因此,日志应该像设计所有软件组件一样设计。

随着系统的开发,日志也会发生变化。 这条规则表明:当开始开发软件系统时,通常不知道它将来会如何变化,以及从产品部门或用户那里会得到哪些需求,这些需求可能是在软件首次发布后的一年或两年内提出的。软件可能会发生变化,因此日志也应该相应地变化。

日志必须有统一的时间线。 正如文章的假设部分所述,软件系统至少有两个组件(例如,客户端和服务器)。客户端模块在最终用户的机器上运行,它有自己的时区,可能与服务器的不同。建议对所有日志组件使用UTC。在这里,可能会问:如果想知道事件在客户端本地时间的时间怎么办。那么建议使用另一个字段来存储事件的客户端本地时间戳。

日志文件的大小应该适中,以便首选文本编辑器可以轻松加载和搜索。 每个组件的日志文件不应该太大。它应该可以轻松地被首选编辑器加载。如果组件是冗长的并且写了很多日志,建议使用文件滚动。大多数开源日志库允许定义文件滚动参数,这取决于文件大小。如果日志超出大小,库会创建一个新文件并添加一个索引。

所有软件系统组件的日志应该汇总到一个方便查询和调查的存储库中。 如果软件系统由几个组件组成,每个组件都在特定位置写入其日志文件。日志文件被滚动 - 将所有文件组合起来并调查跨组件问题将非常复杂。日志存储库将解决这个问题。顺便说一句,如果使用存储库,可以忽略规则#4。

日志应该是单调的。 这条规则说:如果在第X行,日志表明系统在时间T0处于状态A。在某个时刻T1,软件系统移动到状态B,这意味着应该有一条明确的日志行,表明系统状态在时间T1更改为B。换句话说:每个系统状态更改都应该明确地写在日志文件中。不应该猜测系统处于什么状态 - 必须从日志中明确知道。

如果系统做出了决定,它应该被写入日志。 这个规则可以通过一个例子来说明。例如,正在记录用户认证过程。必须写明用户是否被认证。两个决定都应该写在日志中。阅读日志后,明确理解认证过程做出了什么决定。只写负面决定(这种方法也存在)可能会部分影响规则#6:日志可能变得不单调。

日志消息应该与源代码相关联。 阅读日志时,应该能够轻松地找到负责特定日志消息的源代码部分。一些框架允许添加文件名和行号。

有些消息比其他消息更重要。 有些行比其他行更重要。例如,希望尽快知道系统中发生了异常。通常,为每条日志消息使用严重性级别。最常见的严重性级别是FATAL - 用于致命异常,ERROR - 用于错误,INFO和DEBUG。有时,还有更多的严重性级别,如WARNING,TRACE等。

日志应该是连续的。 让想象一下,用户抱怨他/她看到了一个错误消息。开发或DevOps团队访问日志存储库查询错误消息并找到了适当的日志消息。这很好,但几乎没有用。为什么?因为知道用户是对的,但如果能够知道这个特定情况下错误的根本原因,那将会更好。为了能够调查,显然需要看到一些日志消息,这些消息是在这个用户看到错误之前写的。为了解决这个问题,提出了连续方法。连续方法说:当用户开始某个流程时,将为其分配一个唯一的ID。将用这个唯一ID签名这个流程的每条日志消息。当流程结束时,ID将被取消。所以回到例子,一旦找到了与用户看到的错误消息相关的日志消息,通过唯一的流程ID,将能够找到错误显示之前写的所有日志消息。如果日志是按照这些规则编写的,将能够轻松理解是什么导致系统为这个特定用户显示错误消息。

写日志是为了在某个时候阅读它们。 日志是为了阅读而写的 - 所以它应该是清晰的!最好没有错别字。根据规则#1:应该像对待可交付的(例如UI)一样对待日志。不会发布不清楚或有错别字的UI,所以也不应该发布不清楚或有错别字的日志。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485