上一篇文章讲到muduo日志库的基础部分,现在来讲muduo日志库的异步日志工作流程。除了异步,muduo日志库还具有自动把数据从FILE结构体缓冲区flush到硬盘功能和定期roll(回滚)日志文件的功能。
异步:
异步日志由LogFile{.h, .cc}、AsyncLogging{.h, .cc}中定义的类来配合工作的。主要是有两个类 LogFile 和AsyncLogging。在使用异步日志前,要更改Logger的默认输出函数,使得Logger的默认输出函数向AsyncLogging输入日志内容(具体的修改可参考上一篇文章)。
LogFile类和AsyncLogging类各有自己的buffer(在下文中,分别记为file_buffer和async_buffer)。
当用户使用LOG_*写入日志内容时,将会把日志内容写入到async_buffer中,当async_buffer写满了,就会把async_buffer中的内容写入到file_buffer中。在LogFile类中,会自动将file_buffer的内容flush到硬盘。
这是数据流的大致流向,读者先有一个大致的方向。具体是怎么异步的,下面会慢慢细说。
AsyncLogging类有一个线程成员变量,AsyncLogging类的代码由两个线程执行。一个是使用LOG_*进行写日志的线程(更确切地说是使用了LOG_*的所有线程),另外一个就是AsyncLogging类的线程成员变量了,分别把这两个线程称为前台和后台线程,他们也分别是同步问题中的生产者和消费者。一般情况下,前台线程和后台线程各有两个buffer。前面说的async_buffer是前台和后台buffer的统称。
先贴上前台线程代码。代码中用到了currentBuffer_和nextBuffer_两个前台buffer。还有一个buffers_智能指针数组(其类型为boost::ptr_vector<Buffer>),其存放已经满了的前台buffer。
//所有LOG_*最终都会调用append函数。
voidAsyncLogging::append(const char* logline, int len)
{
//可能有多个线程同时调用LOG_*写入日志,所以需要同步。
muduo::MutexLockGuard lock(mutex_);
if ( currentBuffer_->avail() > len) //buffer还够大,可以装下新一条日志内容。
{
currentBuffer_->append(logline, len);
}
else
{
//currentBuffer_已经满了,要将之存放到buffers_中。
buffers_.push_back(currentBuffer_.release());
if (nextBuffer_)
{
currentBuffer_ =boost::ptr_container::move(nextBuffer_);
}
else