AsyncLogging.h

异步日志

该类

AsyncLogging

成员变量

  1. MutexLock mutex_;
    互斥量,线程安全的添加日志。
  2. Condition cond_;
    条件变量,当有日志添加进来的时候通知。当日志缓冲区没准备好的时候等待
  3. Thread thread_;
    为添加日志单独建一个线程。
  4. string basename_;
    日志文件头部文件名
  5. size_t rollSize_;
    更换新日志文件的间隔
  6. bool running_;
    确定日志文件是否在运行。
    其实主要是条件变量的循环的判断条件。
  7. const int flushInterval_;
    冲刷间隔
  8. CountDownLatch latch_;
    当线程开始运行以后,才继续下面的程序
    应该是怕日志线程没准备好,但却添加日志?
  9. BufferVector buffers_;
    已更换下来的日志文件的存储区。
    FixedBuffer Buffer
    boost::ptr_vector BufferVector;
    使用的仍然是一种指针的容器。
  10. 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()

运行在日志线程中的函数。
主要是:

  1. 循环向磁盘中写入文件。
  2. 如果上次写入到这次写入之间没有添加日志,那么就等待固定时间。
  3. 进入循环之先初始化了两个缓冲区,用来赋值给当前缓冲区指针和下一个缓冲区指针。
    这两个缓冲区将一直存在,写入在缓冲区容器的前两个元素,交换以后,也是在待写入缓冲区的前两个元素,然后被pop出来。所以这两个缓冲区一直存在
  4. 两个缓冲区,一个类的成员变量,一个是将要写入到文件的待写入缓冲区容器。
    缓冲区容器交还给待写入缓冲区容器,然后待写入缓冲区容器在清空的时候留两个缓冲区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(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()函数,启动和停止。

嗯,思想很厉害。
连个一直存在的缓冲区。
两个一直一直在不停交换的缓冲区容器。

两个缓冲区容器不停交换,这样可以缩短互斥量锁住的时间,交换完了就可以解锁互斥量

你可能感兴趣的:(AsyncLogging.h)