日志量问题 - 减少收集的日志量





5.00/5 (3投票s)
通过引入日志条目块的概念,审视生产系统中日志量增长的问题。
引言
当我们的生产系统出现问题时,我们希望在日志中包含所有必要的信息,以便找到错误的原因。在相当复杂的系统中,这会导致收集大量的数据:哪些处理阶段已完成,调用某些函数时的参数是什么,对外部服务的调用返回了什么结果等等。这里的问题是,即使没有错误,我们也必须收集所有这些信息。这导致我们的日志存储量增加,我们需要为此付费。
日志级别(错误、警告、信息等)在这里作用不大。通常,应用程序具有某个目标日志级别(例如,信息)。这意味着所有级别等于或高于此目标级别的记录都会被记录,而所有其他记录都会被丢弃。但是,在发生错误的那一刻,我们感兴趣的是这些调试级别的条目,而这些条目通常被丢弃。如果问题频繁重复出现,我们可以暂时降低目标级别,收集所有必要的信息,然后将目标级别恢复。在这种情况下,日志存储量增加的速度只会暂时提高。但如果错误很少见,这种方法(即使可能)不是很方便,因为它会导致收集大量数据。
我们能否改善这种情况?我认为我们可以。但在这里,我必须说,在这篇文章中,我不会提供现成的解决方案。这只是一个应该在现有日志记录系统中实现的想法,因为它需要更改其源代码。
好的。让我们开始。
主要思想
获得两全其美的基本想法如下。让我们引入日志条目块的概念。
using(_logBlockFactory.CreateLogBlock())
{
...
SomeOperations();
...
}
此类块内的所有记录都不会立即发送到存储。相反,它们会等待块的Dispose
方法被调用。在此方法内部,我们分析此块的所有日志条目。如果至少有一个错误日志级别或更高级别的条目,我们会将所有条目发送到存储。否则,我们只会删除所有这些记录。
当然,每个日志条目的时间戳是在创建记录时获取的,而不是在调用Dispose
方法时获取的。
可能的改进
这个基本想法可以改进。
首先,我们可能需要将一些日志条目写入存储,而不管是否存在错误。例如,您可能需要此类记录来进行审计。或者您想要保存有关操作性能的信息(尽管最好使用指标来执行此操作)。但是,我们必须具备这种能力。
_logger.Mandatory.WriteInformation("Something");
其次,我们可能需要能够决定是否要将记录上传到存储。为此,我们可以使用一个简单的属性
_logBlockFactory.CurrentBlock.WriteBlock = true;
或者发明一些更复杂的东西,例如一个函数,它将查看块中的所有条目并做出决定。
我们还可以使用日志级别。例如,如果存在错误,我们可以将所有级别的日志记录写入存储。如果没有错误,我们将只保存信息级别及以上的记录。我不知道是否可以(并且有必要)实现这种方法。如今,我们可以为每个存储设置一个单独的目标日志级别。因此,这种新的日志记录方法需要对现有的日志记录概念进行太多更改。
问题
当然,这个想法也有负面影响。
如果您想几乎实时地存储日志条目,这种方法将不适合您。在这里,记录只有在调用块的Dispose
方法时才会保存。
由于我们目前以批处理方式存储日志条目,因此可能会发生它们在时间上以错误的顺序到达存储的情况。例如,第一个块存储 11:31:01、11:31:02、11:31:03 的记录。之后,第二个块完成,它也存储 11:31:01、11:31:02、11:31:03 的记录。这意味着记录会按其时间戳打乱。如果您的存储不支持时间戳排序(控制台、文件等),这可能会成为一个问题。另一方面,我认为所有现代生产日志存储(例如,Azure Application Insights)都支持这种排序。此外,来自不同块的记录通常会有不同的关联 ID。这将简化此类记录的分离。
此外,由于我们不立即写入日志条目,因此在发生严重故障时,我们可能会丢失一些条目(如果根本没有调用该块的Dispose
方法)。
嵌套块的语义也存在一个小问题。如果内部块遇到错误,外部块应该如何处理?如果在外部块中发生错误怎么办?我们应该写入内部块的记录吗?所有这些问题都应该解决。
结论
这就是我解决日志量增长问题的想法。我希望它对您有用,并且有人会尝试实现它。祝你好运!
历史
- 2023年12月29日:初始版本