muduo源码学习(12)-日志类封装2

来源:互联网 发布:linux怎么重启服务器 编辑:程序博客网 时间:2024/06/08 02:28

之前大体上分析到muduo的日志的一些类,主要就是Logger类,该类的析构函数如下

//析构函数,输出日志Logger::~Logger(){//调用impl_的finish()  impl_.finish();    const LogStream::Buffer& buf(stream().buffer());//调用输出g_output为函数指针  g_output(buf.data(), buf.length());  if (impl_.level_ == FATAL)  {  //刷新输出的缓冲区    g_flush();      abort();  }}

会调用函数指针g_output将日志输出到指定设备,默认的有

//默认的输出,输出到标准输出上void defaultOutput(const char* msg, int len){  size_t n = fwrite(msg, 1, len, stdout);  //FIXME check n  (void)n;}//默认的刷新,刷新stdoutvoid defaultFlush(){  fflush(stdout);}//默认的//OutputFunc//FlushFunc函数指针Logger::OutputFunc g_output = defaultOutput;Logger::FlushFunc g_flush = defaultFlush;}

可见默认日志是输出到标志输出设备上的。然而我们需要将日志保存到文件中,虽然可以重定向,但是还有解决日志滚动问题。也就是要限定每一个日志文件的大小,还有每天的日志文件的数量。muduo中封装了LogFile类来支持有关操作。在base/LogFile.h中

class LogFile : boost::noncopyable{ public:  LogFile(const string& basename,          size_t rollSize,          //ĬÈÏΪḬ̈߳²È«µÄ          bool threadSafe = true,          int flushInterval = 3);  ~LogFile();//写日志到文件  void append(const char* logline, int len);  //刷新  void flush(); private: //不加锁的将日志写入文件  void append_unlocked(const char* logline, int len);//获取日志文件名称  static string getLogFileName(const string& basename, time_t* now);  //滚动日志  void rollFile();//  const string basename_;//日志文件大小的上限,  const size_t rollSize_;//刷新的间隔时间  const int flushInterval_;//¼ÆÊýÆ÷  int count_;  boost::scoped_ptr<MutexLock> mutex_;  //开始记录日志的时间(调整到零点)  time_t startOfPeriod_;//上一次滚动的时间  time_t lastRoll_;//上一次刷新的时间  time_t lastFlush_;  //File类前置声明  class File;    //  boost::scoped_ptr<File> file_;  const static int kCheckTimeRoll_ = 1024;  //一天的秒数  const static int kRollPerSeconds_ = 60*60*24;};

其中声明了一个File类,在base/LogFile.cc中

//class LogFile::File : boost::noncopyable{ public:  explicit File(const string& filename)  //    : fp_(::fopen(filename.data(), "ae")),      writtenBytes_(0)  {    assert(fp_);//设置缓冲区    ::setbuffer(fp_, buffer_, sizeof buffer_);    // posix_fadvise POSIX_FADV_DONTNEED ?  }//  ~File()  {    ::fclose(fp_);  }//写文件  void append(const char* logline, const size_t len)  {    size_t n = write(logline, len);    size_t remain = len - n;//    while (remain > 0)    {      size_t x = write(logline + n, remain);      if (x == 0)      {        int err = ferror(fp_);        if (err)        {          fprintf(stderr, "LogFile::File::append() failed %s\n", strerror_tl(err));        }        break;      }      n += x;      remain = len - n; // remain -= x    }    writtenBytes_ += len;  }//刷新  void flush()  {    ::fflush(fp_);  }//  size_t writtenBytes() const { return writtenBytes_; } private://append()  size_t write(const char* logline, size_t len)  {#undef fwrite_unlocked//    return ::fwrite_unlocked(logline, 1, len, fp_);  }//  FILE* fp_;  //缓冲区  char buffer_[64*1024];//已经写入的字节数  size_t writtenBytes_;};
主要就是对文件的写操作,在LogFile类中,比较重要的首先是获取日志文件名称

//string LogFile::getLogFileName(const string& basename, time_t* now){  string filename;  //  filename.reserve(basename.size() + 64);  filename = basename;  char timebuf[32];  char pidbuf[32];  struct tm tm;  //  *now = time(NULL);  //  gmtime_r(now, &tm); // FIXME: localtime_r ?//¸时间  strftime(timebuf, sizeof timebuf, ".%Y%m%d-%H%M%S.", &tm);  filename += timebuf;  
//主机名称  filename += ProcessInfo::hostname();//格式化进程id  snprintf(pidbuf, sizeof pidbuf, ".%d", ProcessInfo::pid());  filename += pidbuf;  filename += ".log";  return filename;}
日志文件名称格式为  basename.时间.主机.进程号.log

写日志的操作

//void LogFile::append_unlocked(const char* logline, int len){//写入日志  file_->append(logline, len);//判断文件大小是否超过上限  if (file_->writtenBytes() > rollSize_)  {  //滚动日志    rollFile();  }  else  {      if (count_ > kCheckTimeRoll_)    {      count_ = 0;      time_t now = ::time(NULL);
//会将当前时间对齐到零点        time_t thisPeriod_ = now / kRollPerSeconds_ * kRollPerSeconds_;  
//      if (thisPeriod_ != startOfPeriod_)      {        rollFile();      }  //检查是否刷新      else if (now - lastFlush_ > flushInterval_)      {        lastFlush_ = now;        file_->flush();      }    }    else    {    //      ++count_;    }  }}

首先将日志写入文件 ,然后判断大小,是否要滚动日志,之后根据count_大小决定是否检查,判断当前时间与之前相比是否经过了一天,如果是,则滚动日志。其中

 time_t thisPeriod_ = now / kRollPerSeconds_ * kRollPerSeconds_;
KRollPerSeconds是一天的秒数,now/KRollPerSeconds会把小数忽略,相当于同步到一天的零点,比如某天的4:30,会把4:30都舍弃,之后乘以kRollPerSeconds,得到秒数,如果在同一天,得到的结果是相同的,不会进行日志滚动。
日志滚动函数
void LogFile::rollFile(){  time_t now = 0;  //得到新的日志文件名称  string filename = getLogFileName(basename_, &now);//同步到零点  time_t start = now / kRollPerSeconds_ * kRollPerSeconds_;//  if (now > lastRoll_)  {    lastRoll_ = now;    lastFlush_ = now;    startOfPeriod_ = start;//创建新的Filefile_.reset(new File(filename));  }}


主要逻辑就是得到新的文件名称,创建新的文件,更新有关时间。


在使用写入到文件的日志的时候,需要设置Logger类中的函数指针,实例如下

boost::scoped_ptr<muduo::LogFile> g_logFile;void outputFunc(const char* msg, int len){  g_logFile->append(msg, len);}void flushFunc(){  g_logFile->flush();}int main(int argc, char* argv[]){  char name[256];  strncpy(name, argv[0], 256);  g_logFile.reset(new muduo::LogFile(::basename(name), 200*1000));  muduo::Logger::setOutput(outputFunc); //设置函数指针  muduo::Logger::setFlush(flushFunc);  muduo::string line = "1234567890 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ ";  for (int i = 0; i < 10000; ++i)  {    LOG_INFO << line << i;    usleep(1000);  }






原创粉丝点击