65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2023年12月29日

CPOL

4分钟阅读

viewsIcon

4224

通过引入日志条目块的概念,审视生产系统中日志量增长的问题。

引言

当我们的生产系统出现问题时,我们希望在日志中包含所有必要的信息,以便找到错误的原因。在相当复杂的系统中,这会导致收集大量的数据:哪些处理阶段已完成,调用某些函数时的参数是什么,对外部服务的调用返回了什么结果等等。这里的问题是,即使没有错误,我们也必须收集所有这些信息。这导致我们的日志存储量增加,我们需要为此付费。

日志级别(错误、警告、信息等)在这里作用不大。通常,应用程序具有某个目标日志级别(例如,信息)。这意味着所有级别等于或高于此目标级别的记录都会被记录,而所有其他记录都会被丢弃。但是,在发生错误的那一刻,我们感兴趣的是这些调试级别的条目,而这些条目通常被丢弃。如果问题频繁重复出现,我们可以暂时降低目标级别,收集所有必要的信息,然后将目标级别恢复。在这种情况下,日志存储量增加的速度只会暂时提高。但如果错误很少见,这种方法(即使可能)不是很方便,因为它会导致收集大量数据。

我们能否改善这种情况?我认为我们可以。但在这里,我必须说,在这篇文章中,我不会提供现成的解决方案。这只是一个应该在现有日志记录系统中实现的想法,因为它需要更改其源代码。

好的。让我们开始。

主要思想

获得两全其美的基本想法如下。让我们引入日志条目块的概念。

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日:初始版本
© . All rights reserved.