logfile类:
class logfile:noncopyable { };
作用:
主要负责日志写入文件的管理
内部提供append,rollFile,flush三个函数
append表示向文件尾部追加数据,
rollFile表示需要更换一个日志文件来写日志
flush表示清空文件读写缓冲区
注意append和flush都提供了有锁/无锁的实现,logfile构造函数需要传入是否是线程安全的threadSafe参数
若是true需要创建一个互斥所mutexlock并且加锁,否则不用加锁.
logfile成员变量:
private: const string m_basename; //日志文件名 const off_t m_rollSize; //已写入的数据大于rollsize就生成一个新日志文件 const int m_flushInterval; //日志写入时间间隔,默认每3s写一次 const int m_checkEveryN; //m_count>默认=1024时需要检查是否回滚或写入 int m_count; //计数器,每次append操作时,都需要让m_count++更新 std::unique_ptr<mutexlock> m_mutex;//锁智能指针 time_t m_startOfPeriod; //开始记录日志的时间 time_t m_lastRoll; //上一次滚动日志文件的时间 time_t m_lastFlush; //上一次日志写入文件的时间 std::unique_ptr<fileutil::AppendFile> m_file; //文件智能指针 static const int m_kRollPerSeconds=60*60*24; //一天的秒数
logfile成员函数:
public: logfile(const string& basename,off_t rollSize,bool threadSafe=true, int flushInterval=3,int checkEveryN=1024); ~logfile(); //写文件操作,有锁/无锁两种实现 void append(const char* logline,int len); //清空文件读写缓冲区,有锁/无锁两种实现 void flush(); //回滚文件,其实就是当前文件不能写日志了,需要更换下一个日志文件来写日志 bool rollFile(); private: //append的实现函数 void append_unlocked(const char* logline,int len); //根据当前时间获得一个新的日志文件名字 static string getLogFileName(const string& basename,time_t* now);
logfile.h
#ifndef LOGFILE_H #define LOGFILE_H #include"base/mutex.h" #include"base/types.h" #include<memory> namespace mymuduo { namespace fileutil{ class AppendFile; } class logfile:noncopyable { public: logfile(const string& basename,off_t rollSize,bool threadSafe=true, int flushInterval=3,int checkEveryN=1024); ~logfile(); //写文件操作,有锁/无锁两种实现 void append(const char* logline,int len); //清空文件读写缓冲区,有锁/无锁两种实现 void flush(); //回滚文件,其实就是当前文件不能写日志了,需要更换下一个日志文件来写日志 bool rollFile(); private: //append的实现函数 void append_unlocked(const char* logline,int len); //根据当前时间获得一个新的日志文件名字 static string getLogFileName(const string& basename,time_t* now); const string m_basename; //日志文件名 const off_t m_rollSize; //已写入的数据大于rollsize就生成一个新日志文件 const int m_flushInterval; //日志写入时间间隔,默认每3s写一次 const int m_checkEveryN; //m_count>默认=1024时需要检查是否回滚或写入 int m_count; //计数器,每次append操作时,都需要让m_count++更新 std::unique_ptr<mutexlock> m_mutex;//锁智能指针 time_t m_startOfPeriod; //开始记录日志的时间 time_t m_lastRoll; //上一次滚动日志文件的时间 time_t m_lastFlush; //上一次日志写入文件的时间 std::unique_ptr<fileutil::AppendFile> m_file; //文件智能指针 static const int m_kRollPerSeconds=60*60*24; //一天的秒数 }; }//namespace mymuduo #endif // LOGFILE_H
logfile.cpp
#include "logfile.h" #include"base/fileutil.h" #include"base/processinfo.h" #include<assert.h> #include<stdio.h> #include<time.h> namespace mymuduo { //构造函数,负责初始化文件名,回滚大小,是否线程安全(判断是否需要创建互斥锁), //写入日志文件的时间间隔等信息 logfile::logfile(const string& basename,off_t rollSize,bool threadSafe, int flushInterval,int checkEveryN) :m_basename(basename),m_rollSize(rollSize),m_flushInterval(flushInterval), m_checkEveryN(checkEveryN),m_count(0), m_mutex(threadSafe?new mutexlock:NULL), m_startOfPeriod(0),m_lastRoll(0),m_lastFlush(0) { //保证basename中没有 / , 也就是这里basename是文件名而不是文件路径 assert(basename.find('/')==string::npos); rollFile(); } logfile::~logfile()=default; //两种方式:有锁/无锁,内部都是append_unlocked()函数实现 void logfile::append(const char* logline,int len) { if(!m_mutex) append_unlocked(logline,len); else { mutexlockguard mlg(*m_mutex); append_unlocked(logline,len); } } //两种方式:有锁/无锁,内部都是AppendFile::flush()实现 void logfile::flush() { if(!m_mutex) m_file->flush(); else { mutexlockguard mlg(*m_mutex); m_file->flush(); } } //回滚日志文件,功能是根据当前时间now设置m_lastRoll,m_lastFlush,m_startOfPeriod //并把m_file重新指向一个新的AppendFile(完整日志文件名字) bool logfile::rollFile() { time_t now = 0; //filename格式 test.txt.20200825-064633.master.22895.log string filename = getLogFileName(m_basename, &now); time_t start = now / m_kRollPerSeconds * m_kRollPerSeconds;//啥意思? //这里考虑一下now<=m_lastRoll,要不然就是getLogFileName返回错误的now //要不然就是时间静止/倒退,肯定不正确,只考虑now>m_lastRoll if (now > m_lastRoll) { m_lastRoll = now; m_lastFlush = now; m_startOfPeriod = start; //m_file重新指向一个新的AppendFile对象,打开的文件为filename //即完整的日志文件名字,如果文件不存在则新创建一个文件 m_file.reset(new fileutil::AppendFile(filename)); return true; } return false; } //利用AppendFile类完成向文件中写操作 void logfile::append_unlocked(const char* logline,int len) { //向文件中写入长度为len的logline字符串 m_file->append(logline,len); //判断已写入的字节数是否超出了回滚大小,若超出了只能再次回滚文件: //根据当前时间创建一个新的日志文件,并且把m_file指向这个新的AppendFile if(m_file->writtenBytes()>m_rollSize) rollFile(); else { //不需要回滚文件时候,对当前文件进行操作即可. m_count++; if(m_count>=m_checkEveryN) { //此时重置m_count,再次回滚文件. m_count=0; time_t now=::time(NULL); time_t thisPeriod=now/m_kRollPerSeconds*m_kRollPerSeconds; if(thisPeriod!=m_startOfPeriod) //比较是否相等,不相等 说明到了第二天0点,就滚动文件 rollFile(); else if(now-m_lastFlush>m_flushInterval)//判断是否超过flush时间间隔 { m_lastFlush=now; m_file->flush(); } } } } //得到完整的日志文件名字,例如basename=test.txt时,filename=如下 //test.txt.20200825-064633.master.22895.log //basename.时间.hostname.pid.log //就是一个完整的日志文件名字 string logfile::getLogFileName(const string& basename,time_t* now) { string filename; filename.reserve(basename.size()+64); //添加basename filename=basename; char timebuf[32]; struct tm tm; *now=::time(NULL); gmtime_r(now,&tm); strftime(timebuf, sizeof timebuf, ".%Y%m%d-%H%M%S.", &tm); filename += timebuf; //添加time filename+=processinfo::hostname();//添加hostname char pidbuf[32]; snprintf(pidbuf, sizeof pidbuf, ".%d", processinfo::pid()); filename += pidbuf;//添加pid filename += ".log";//添加后缀名 return filename; } }