muduo
muduo copied to clipboard
进程刚启动时退出,日志不能flush到文件
您好!在最近的学习中遇到了这样的问题,就是进程刚启动时,如果因为某些原因退出,则日志不能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";
}
这个问题陈硕在他的《Linux多线程服务端编程》已经进行了说明,你可以自行查阅
@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了.
多线程环境下,是无法保证你的这种做法是正确的。你的解决方案仅仅能够减少未flush到文件的情况 考虑如下的场景:
- 主线程主线程写出了日志,然后退出,running变成false
- 1发生时,日志线程刚好执行到do ... while(running)的while部分,那么日志线程也就退出了,留下一部分日志没有flush到文件 如果你要正确处理这种情况,那么需要用线程同步的机制,让日志线程最后退出。即使你花了大的代价,写了复杂的同步机制做到了,那么程序coredump时,你还是无法保证的。最后的权衡结果还是不做线程同步更好。
- 上面的改动,只是针对一个简单测试程序 main->log->return.(之前说法不够严谨,)
- 程序异常退出的时候, 有些日志没刷出来,作者是交代了的,从core中找"哨兵".
这是因为,日志模块后端线程3秒才会flush,你的程序如果马上退出,日志消息还在缓冲区,并没有flush到内核缓冲/磁盘。 另外,Logger析构时,只有FATAL级别日志消息,才会调用flush。
如果想要让程序启动后快速退出,日志能flush到磁盘文件,可以尝试: 1)主线程等待超过3秒退出; 2)退出前,主动调用一次与g_output同步的g_flush。