muduo的日志库分析三之LogFile类

LogFile类图

LogFile类主要负责日志的滚动,日志滚动有两种条件,一种是日志文件大小达到预设值,另一种是时间到达超过当天。由几个成员变量来控制日志滚动。

// 基本的文件名,日志文件名由基本文件名和时间日期等元素构造  
  const string basename_;  
  // 日志文件的极限容量,当日志文件的数据量达到这个限制之后就重新生成一个日志文件  
  const size_t rollSize_;  
  // 刷新间隔  
  const int flushInterval_;  
  // 每调用checkEveryN_次日志写,就滚动一次日志  
  const int checkEveryN_;  
  // 写入的次数  
  int count_;  
  // 锁  
  std::unique_ptr<MutexLock> mutex_;
  // 开始的周期,在同一个周期内的日志数据会写入同一个日志文件中(如果数据不是太大的话)  
  time_t startOfPeriod_;  
  // 最后一次滚动日志的时间  
  time_t lastRoll_;  
  // 上一次冲刷的时间  
  time_t lastFlush_;  
  // 日志文件  
  std::unique_ptr<FileUtil::AppendFile> file_; 
  // roll的周期:即60*60*24= 一天  
  const static int kRollPerSeconds_ = 60*60*24;  
muduo库独特之处在于使用了一个threadsafe标志,来决定是否分配mutex_,如果为假,将不使用mutex_,非线程安全但是提高了效率。

日志的滚动的实现主要由rollFile()函数。

/* 
 * 滚动日志 
 * 相当于重新生成日志文件,再向里面写数据 
 */  
bool LogFile::rollFile()  
{  
  time_t now = 0;  
  string filename = getLogFileName(basename_, &now);  //获取生成一个文件名称  
  
  //注意,这里先除KRollPerSeconds然后乘KPollPerSeconds表示对齐值KRollPerSeconds的整数倍,也就是事件调整到当天零点(/除法会引发取整)  
  time_t start = now / kRollPerSeconds_ * kRollPerSeconds_;     
  
  if (now > lastRoll_)   //如果大于lastRoll,产生一个新的日志文件,并更新lastRoll  
  {  
    lastRoll_ = now;  
    lastFlush_ = now;  
    startOfPeriod_ = start;  
    file_.reset(new FileUtil::AppendFile(filename));  
    return true;  
  }  
  return false;  
}  
  
/* 
 * 构造一个日志文件名 
 * 日志名由基本名字+时间戳+主机名+进程id+加上“.log”后缀 
 */  
string LogFile::getLogFileName(const string& basename, time_t* now)  
{  
    string filename;  
    //reserve()将字符串的容量设置为至少basename.size() + 64,因为后面要添加时间、主机名、进程id等内容,预先设置容量大小,为了避免反复重新分配缓冲区内存而导致效率降低,或者在使用某些STL操作(例如std::copy)之前保证缓冲区够大
    filename.reserve(basename.size() + 64);  
  
    // 基本文件名  
    filename = basename;  
  
    // 获取当前年月日  
    char timebuf[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  
    char pidbuf[32];  
    snprintf(pidbuf, sizeof pidbuf, ".%d", ProcessInfo::pid());  
    filename += pidbuf;  
  
    filename += ".log";  
  
    return filename;  
}  

在rollFile()函数里先获取日志文件名,日志文件名的格式如下:

        logfile_test.20130411-115604.popo.7743.log

     // 进程名字.文件的创建时间.主机名.进程id.log

日志文件名的获取由getLogFileName()函数实现:

    (1)给string filename预先设置容量

      (2) 用gmtime_r()获取当前时间。

#include<time.h>

struct tm *gmtime(const time_t *timep);

struct tm *gmtime_r(const time_t *timep, struct tm *result);

这两个函数意思一样,将timep这个秒数转换成以UTC时区为标准的年月日时分秒时间。gmtime_r是线程安全的,推荐使用这个。gmtime返回的是一个structtm*,这个指针指向一个静态的内存,这块区域是会经常被改动的。你刚调用gmtime(),执行了其他几条命令,然后想使用刚才gmtime()得到struct tm,会发现内容不对了,所以很危险,使用gmtime_r后就没有问题,gmtime_r会将结果保存到你传入的内存中。

gmtime_r()函数功能与此相同,但是它可以将数据存储到用户提供的结构体中。

strftime()函数说明:https://baike.baidu.com/item/strftime/9569073?fr=aladdin

    (3)取主机名和进程id
string ProcessInfo::hostname()
{
  // HOST_NAME_MAX 64
  // _POSIX_HOST_NAME_MAX 255
  char buf[256];
  if (::gethostname(buf, sizeof buf) == 0)
  {
    buf[sizeof(buf)-1] = '\0';
    return buf;
  }
  else
  {
    return "unknownhost";
  }
}

pid_t ProcessInfo::pid()
{
  return ::getpid();
}
下面的这行代码是获取到的是这天的凌晨零点,因为我们使用的是北京时间,东8区,实际上是这天的8点。

time_t start = now / kRollPerSeconds_ * kRollPerSeconds_;
append()函数添加日志内容
void LogFile::append(const char* logline, int len)
{
  if (mutex_)
  {
    MutexLockGuard lock(*mutex_);
    append_unlocked(logline, len);
  }
  else
  {
    append_unlocked(logline, len);
  }
}

void LogFile::append_unlocked(const char* logline, int len)
{
    // 添加到日志文件中(其实只是存放在缓冲区中
  file_->append(logline, len);

  if (file_->writtenBytes() > rollSize_)
  {
    rollFile();
  }
  else
  {
    ++count_;
    if (count_ >= checkEveryN_)
    {
      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();
      }
    }
  }
}





  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

X-Programer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值