异步日志
该类
AsyncLogging
成员变量
- MutexLock mutex_;
互斥量,线程安全的添加日志。 - Condition cond_;
条件变量,当有日志添加进来的时候通知。当日志缓冲区没准备好的时候等待 - Thread thread_;
为添加日志单独建一个线程。 - string basename_;
日志文件头部文件名 - size_t rollSize_;
更换新日志文件的间隔 - bool running_;
确定日志文件是否在运行。
其实主要是条件变量的循环的判断条件。 - const int flushInterval_;
冲刷间隔 - CountDownLatch latch_;
当线程开始运行以后,才继续下面的程序
应该是怕日志线程没准备好,但却添加日志? - BufferVector buffers_;
已更换下来的日志文件的存储区。
FixedBuffer<muduo::detail::kLargeBuffer> Buffer
boost::ptr_vector<Buffer> BufferVector;
使用的仍然是一种指针的容器。 - BufferPtr nextBuffer_,BufferPtr currentBuffer_;
下一块日志的缓冲区,和日志的缓冲区
构造函数
AsyncLogging(const string& basename,size_t rollSize,int flushInterval = 3)
: flushInterval_(flushInterval),
running_(false),
basename_(basename),
rollSize_(rollSize),
thread_(boost::bind(&AsyncLogging::threadFunc, this), "Logging"),
//直接初始化日志线程
latch_(1),
//日志线程中的函数运行起来以后再返回
mutex_(),
//MutexLock默认初始化
cond_(mutex_),
//条件变量,初始化,需要在mutex_后面
currentBuffer_(new Buffer),
//LogStream中FixedBuffer里面的
nextBuffer_(new Buffer),
//就是一个char []
buffers_()
//boost的指针容器
{
currentBuffer_->bzero();
//置空char []
nextBuffer_->bzero();
buffers_.reserve(16);
//调整日志容器的大小
}
append
添加日志的函数
void AsyncLogging::append(const char* logline, int len)
{
muduo::MutexLockGuard lock(mutex_);
//线程安全的添加日志
if (currentBuffer_->avail() > len)
//如果缓冲区剩余大小足够在写入
{
currentBuffer_->append(logline, len);
}
else
{
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();
//不太清楚作用
}
}
threadFunc()
运行在日志线程中的函数。
主要是:
- 循环向磁盘中写入文件。
- 如果上次写入到这次写入之间没有添加日志,那么就等待固定时间。
- 进入循环之先初始化了两个缓冲区,用来赋值给当前缓冲区指针和下一个缓冲区指针。
这两个缓冲区将一直存在,写入在缓冲区容器的前两个元素,交换以后,也是在待写入缓冲区的前两个元素,然后被pop出来。所以这两个缓冲区一直存在 - 两个缓冲区,一个类的成员变量,一个是将要写入到文件的待写入缓冲区容器。
缓冲区容器交还给待写入缓冲区容器,然后待写入缓冲区容器在清空的时候留两个缓冲区pop出来给两个缓冲区指针,省去初始化的时间?
void AsyncLogging::threadFunc()
//在日志线程中运行的函数
{
assert(running_ == true);
latch_.countDown();
//当线程运行起来以后,Thread的start函数才能继续往下执行
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());
{
muduo::MutexLockGuard lock(mutex_);
if (buffers_.empty()) // unusual usage!
//缓冲容器如果是空的话,也就切换容器这段时间没添加日志
{
cond_.waitForSeconds(flushInterval_); //就是定期写入
}
//等一会,然后不管有没有添加日志,都放入缓冲区
buffers_.push_back(currentBuffer_.release());
//不是空的话,之前写过,直接将当前缓冲的缓冲存到缓冲容器中
currentBuffer_ = boost::ptr_container::move(newBuffer1);
//将两个互换缓冲一个赋值给单钱指针
buffersToWrite.swap(buffers_);
//缓冲容器和写入缓冲容器互换
if (!nexttBuffer_)
//如果下一个指针为空,那么赋值
{
nextBuffer_ = boost::ptr_container::move(newBuffer2);
}
}
assert(!buffersToWrite.empty()); //不是空,继续
if (buffersToWrite.size() > 25)
//如果待写入缓冲容器太多,那么直接扔掉记录
{
char buf[256];
snprintf(buf, sizeof buf, "Dropped log messages at %s, %zd larger buffers\n",
Timestamp::now().toFormattedString().c_str(),
buffersToWrite.size()-2);
fputs(buf, stderr);
output.append(buf, static_cast<int>(strlen(buf)));
buffersToWrite.erase(buffersToWrite.begin()+2, buffersToWrite.end());
//但是为什么保存了前2个??
}
for (size_t i = 0; i < buffersToWrite.size(); ++i)
//待缓冲容器中日志写入到文件
{
output.append(buffersToWrite[i].data(), buffersToWrite[i].length());
}
if (buffersToWrite.size() > 2)
//如果之前没扔记录应该多余2,否则就两个
{
// drop non-bzero-ed buffers, avoid trashing
buffersToWrite.resize(2);
//保留两个元素,其他销毁
}
if (!newBuffer1)
//互换缓冲区1不存在
{
assert(!buffersToWrite.empty());
//这个是省去了创建新缓冲区的时间,直接使用旧的缓冲区,
newBuffer1 = buffersToWrite.pop_back();
newBuffer1->reset();
//重置,使用旧的缓冲区
}
if (!newBuffer2)
{
assert(!buffersToWrite.empty());
newBuffer2 = buffersToWrite.pop_back();
newBuffer2->reset();
}
buffersToWrite.clear();
//清除缓冲区,写入到文件
output.flush();
}
output.flush();
//停止运行以后写入一次
}
另外还有一个start(),stop()函数,启动和停止。
嗯,思想很厉害。
连个一直存在的缓冲区。
两个一直一直在不停交换的缓冲区容器。
两个缓冲区容器不停交换,这样可以缩短互斥量锁住的时间,交换完了就可以解锁互斥量
网友评论