muduo icon indicating copy to clipboard operation
muduo copied to clipboard

进程刚启动时退出,日志不能flush到文件

Open jainyzau opened this issue 9 years ago • 5 comments

您好!在最近的学习中遇到了这样的问题,就是进程刚启动时,如果因为某些原因退出,则日志不能flush到文件中。当然这只是一个小问题,改起来应该比较容易,我只是在这里提出来。

示例代码如下,应该是没有来得及进入AsyncLogging::threadFunc()中的while循环,running_已经变成false。虽然while循环之后有output.flush(),但是此时还没有写到output中。

#include <muduo/base/AsyncLogging.h>
#include <muduo/base/Logging.h>
#include <memory>

std::shared_ptr<muduo::AsyncLogging> gpAsyncLogging;
void async_log_output(const char* msg, int len)
{
    gpAsyncLogging->append(msg, len);
}

void async_log_flush()
{
    // do nothing
}
int main()
{
    gpAsyncLogging.reset(new muduo::AsyncLogging("teset", 500 * 1000 * 1000));
    gpAsyncLogging->start();
    muduo::Logger::setOutput(async_log_output);
    //muduo::Logger::setFlush(async_log_flush);

    LOG_INFO << "test 1";
    LOG_INFO << "test 2";
    LOG_INFO << "test 3";
}

jainyzau avatar Jun 17 '15 07:06 jainyzau

这个问题陈硕在他的《Linux多线程服务端编程》已经进行了说明,你可以自行查阅

dtm-labs avatar Jun 18 '15 08:06 dtm-labs

@chenshuo @yedf 这个和陈硕在书中说的不一样.
是 AsyncLogging.cc 的问题.

class AsyncLogging : boost::noncopyable
{
 public:

  void start()
  {
    running_ = true;
    thread_.start();
    latch_.wait();
  }

start的 中的 latch_.wait(); 只能保证进入线程函数 void AsyncLogging::threadFunc()
不能保证 进入其中的 while (running_) ....

因为执行到while(running)的时候,可能主线程已经结束, AsyncLogging  对象被析构函数被调用. running_已经被设置为False.  导致缓冲中的信息不能刷新到文件中.



void AsyncLogging::threadFunc()
{
  assert(running_ == true);
  latch_.countDown();
  LogFile output(basename_, rollSize_, false);
  BufferPtr newBuffer1(new Buffer);
  BufferPtr newBuffer2(new Buffer);
  newBuffer1->bzero();
  newBuffer2->bzero();
  BufferVector buffersToWrite;
  buffersToWrite.reserve(16);
  while (running_)
  {
    assert(newBuffer1 && newBuffer1->length() == 0);
    assert(newBuffer2 && newBuffer2->length() == 0);
    assert(buffersToWrite.empty());

只需要把 AsyncLogging::threadFunc() 改为 do {...} while(running_); 就ok了.

maguohe avatar Mar 15 '16 10:03 maguohe

多线程环境下,是无法保证你的这种做法是正确的。你的解决方案仅仅能够减少未flush到文件的情况 考虑如下的场景:

  1. 主线程主线程写出了日志,然后退出,running变成false
  2. 1发生时,日志线程刚好执行到do ... while(running)的while部分,那么日志线程也就退出了,留下一部分日志没有flush到文件 如果你要正确处理这种情况,那么需要用线程同步的机制,让日志线程最后退出。即使你花了大的代价,写了复杂的同步机制做到了,那么程序coredump时,你还是无法保证的。最后的权衡结果还是不做线程同步更好。

dtm-labs avatar Mar 15 '16 11:03 dtm-labs

  • 上面的改动,只是针对一个简单测试程序 main->log->return.(之前说法不够严谨,)
  • 程序异常退出的时候, 有些日志没刷出来,作者是交代了的,从core中找"哨兵".

maguohe avatar Mar 16 '16 01:03 maguohe

这是因为,日志模块后端线程3秒才会flush,你的程序如果马上退出,日志消息还在缓冲区,并没有flush到内核缓冲/磁盘。 另外,Logger析构时,只有FATAL级别日志消息,才会调用flush。

如果想要让程序启动后快速退出,日志能flush到磁盘文件,可以尝试: 1)主线程等待超过3秒退出; 2)退出前,主动调用一次与g_output同步的g_flush。

fortunely avatar Apr 15 '22 11:04 fortunely