何时写入异步日志

When to write asynchronous logs

本文关键字:日志 异步 何时写      更新时间:2023-10-16

我正在尝试编写一个异步日志类,但无法决定何时将日志写入文件。

现在,我正在考虑将所有日志存储在一个向量中,然后根据它们的时间戳对它们进行排序。

问题是我应该在什么时候将日志写入文件?如果我在写入磁盘之前等待N个日志,我可能会错过一个日志条目,或者如果我们已经在向量中达到N个日志的话,可能会有一个日志被无序写入日志。

我想也许用某种计时器,但我不确定这是否会更好。

如果有任何想法,我将不胜感激,谢谢。

我同意关于在没有缓冲(或显式刷新)的情况下无序写入它们的评论。然而,如果你想按顺序编写它们,我会有一个单独的线程来管理编写。您可以定义一个特定的延迟量(大概以秒为单位),您可以合理地保证该延迟量将涵盖任何延迟到达。

因此,在一个非常基本的意义上,你会有一个线程函数,类似于:

time_t writeDelay = 10;  // 10 seconds
for(;;)
{
    // Sleep for a second
    //[OS-specific code here]
    time_t now;
    time(&now);
    WriteLogEntriesUpTo(now - writeDelay);
}

这里的问题是,如果您做得不对,将日志条目排序到向量中可能会给记录器带来大量开销。至少要使用deque,这样就可以很容易地从前面弹出条目。如果我要这样做,我可能会为我的日志字符串和队列中的简单记录设置一个大缓冲区(实际上是一个内存池)。有时间戳和指针的东西会让排序变得容易

struct LogEntry {
    time_t timestamp;
    char *message;
};

我将实现某种有序列表(同样是内存池),而不是将它们放在deque中。要把这一切搞得这么快是相当麻烦的。你想得越多,就越觉得写得不规范。

如果您正在处理异步日志记录,我建议使用线程安全队列将所有带有时间戳的消息泵送到日志记录线程工作者。然后,日志工作程序可以在一个块中的所有消息出现在队列中时进行处理。

这种线程安全的队列抽象允许您设置缓冲区大小或使其不受限制(如果队列/缓冲区已填充,您可以通过条件变量向线程发送消息)、同时从多个线程进行日志记录,如果您将主线程封装在try-catch循环中,等待记录器完成后再退出,则即使在错误发生后也可以处理日志(好吧,这对segfault没有帮助,但对大多数错误仍然有帮助)。此外,您可以向记录器发送消息,并在主程序线程中继续前进,而无需等待任何IO操作(这可能会在执行时间上取得巨大胜利)。实施起来也不难。队列具有非常有限的API,因此您与数据结构的交互可以使用一些范围锁定来包装列表。

您为这个实现付出了很小的代价,即锁定价格。但是,如果你需要添加任何其他多线程,你不必重建你的记录器,通过将IO卸载到另一个线程,你可能会比单线程记录器更快。

如果您收到的邮件出现问题,您有两种选择。在队列的日志工作者一侧执行日志排序(为工作者保留一个本地缓冲区,以保留较旧的消息)。或者将日志队列的实现更改为优先级队列,使用时间戳对消息进行排序,并在插入时支付更高的价格。后者可能更容易实现,但要付出更高的同步代价。

我已经在C++中使用Boost线程使用了这种策略,它运行得很好。我可以崩溃,但仍然可以在崩溃前获得日志,当我将工作分支到各个线程以轻松并行工作时,我只需要在任何地方使用相同的日志抽象来轻松跟踪事件顺序。