“日志”有两个意思:
诊断日志
交易日志
muduo没有采用标准库的iostream,而是自己写的logStream Class,这主要是出于性能原因
LogStream做到了类型安全和类型可扩展,效率也较高。
整数转换字符串是最忌写的额,用的是Matthew Wilson(http://m.blog.csdn.net/article/details?id=46524267)的算法 这个算法比stdio和iostream都要快
muduo日志库采用双缓冲技术,基本思路准备两块buffer:A和B。前端负责往buffer A填数据,后端负责将buffer的数据写入到文件,当bufferA写满的之后,交换A和B 。让后端将buffer的数据写入到文件中,而前端往buffer B中填入新的日志消息
//所有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
{
currentBuffer_.reset(new Buffer); //Rarely happens。先不考虑这种情况。
}
currentBuffer_->append(logline,len);
cond_.notify(); //通知后台线程,已经有一个满了的buffer了。
}
}
currentBuffer //当前缓冲区
nextBuffer_ //预备缓冲区
buffers_ //待吸入文件的已填满的缓冲
上面是发送方的代码,如果当前currentBuffer_剩余空间足够大,则会直接把日志消息追加到当前换缓冲区中,否则说明当前缓冲区已满,就把它移入buffers_,并将另一块预备缓冲区移为当前缓冲区。当前端写入速度太快,一下子把这两块缓冲区都用完了,那么只好分配一块新的buffer作为当前缓冲区。
voidAsyncLogging::threadFunc()
{
LogFile output(basename_, rollSize_, false);//定义一个直接进行IO的类。
BufferPtr newBuffer1(new Buffer); //这两个是后台线程的buffer
BufferPtr newBuffer2(new Buffer);
BufferVector buffersToWrite; //用来和前台线程的buffers_进行swap.
while (running_)
{
{
muduo::MutexLockGuard lock(mutex_);
if (buffers_.empty()) // unusual usage!
{
cond_.waitForSeconds(flushInterval_);//睡眠的时间是日志库flush时间。
}
//无论cond是因何而醒来,都要将currentBuffer_放到buffers_中。
//如果是因为时间到而醒,那么currentBuffer_还没满,此时也要将之写入LogFile中。
//如果已经有一个前台buffer满了,那么在前台线程中就已经把一个前台buffer放到buffers_中
//了。此时,还是需要把currentBuffer_放到buffers_中(注意,前后放置是不同的buffer,
//因为在前台线程中,currentBuffer_已经被换成nextBuffer_指向的buffer了)
buffers_.push_back(currentBuffer_.release());
currentBuffer_ =boost::ptr_container::move(newBuffer1); /*---归还一个buffer---*/
buffersToWrite.swap(buffers_); //交换
if (!nextBuffer_)
{ //the nextBuffer_ is still free. notuse for swap with currentBuffer_
nextBuffer_ = boost::ptr_container::move(newBuffer2); /*-----假如需要,归还第二个----*/
}
}
//将已经满了的buffer内容写入到LogFile中。由LogFile进行IO操作。
for (size_t i = 0; i <buffersToWrite.size(); ++i)
{
// FIXME: use unbuffered stdio FILE ? oruse ::writev ?
output.append(buffersToWrite[i].data(),buffersToWrite[i].length());
}
if (!newBuffer1)
{
newBuffer1= buffersToWrite.pop_back();
newBuffer1->reset();
}
//前台buffer是由newBuffer1 2 归还的。现在把buffersToWrite的buffer归还给后台buffer
if (!newBuffer2)
{
newBuffer2 = buffersToWrite.pop_back();
newBuffer2->reset();
}
buffersToWrite.clear();
output.flush(); //flush to drive. less than3 mins a time
}
output.flush();
}
以上代码是接收方(后端)实现
准备好两块空闲的buffer(newBuffer1,newBuffer2)以备临界区交换 。
先将当前缓冲(currentBuffer)移入到buffers_,并立刻将空闲的newBuffer1移为当前的缓冲,临界区最后干的是将buffer2移入buffer1保证时刻都有一个空闲的预备buffer