本文件使用的是C++17版本
#ifndef MUDUO_BASE_LOGFILE_H
#define MUDUO_BASE_LOGFILE_H
#include <muduo/base/Mutex.h>
#include <muduo/base/Types.h>
#include <memory>
namespace muduo
{
namespace FileUtil
{
class AppendFile;
}
class LogFile : noncopyable//文件日誌類
{
public:
LogFile(const string& basename,
size_t rollSize,
bool threadSafe = true,
int flushInterval = 3,
int checkEveryN = 1024);
~LogFile();
void append(const char* logline, int len);
void flush();
bool rollFile();//生成新日誌文件
private:
void append_unlocked(const char* logline, int len);
//如果打算開闢新文件,根據basename和當前時間,得到意圖創建的文件名字
static string getLogFileName(const string& basename, time_t* now);
const string basename_;
const size_t rollSize_;//日誌文件的極限容量,超過後,將導致開闢新文件
const int flushInterval_;
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_;
const static int kRollPerSeconds_ = 60*60*24;//刷新時間
};
}
#endif // MUDUO_BASE_LOGFILE_H
#include <muduo/base/LogFile.h>
#include <muduo/base/FileUtil.h>
#include <muduo/base/ProcessInfo.h>
#include <assert.h>
#include <stdio.h>
#include <time.h>
using namespace muduo;
LogFile::LogFile(const string& basename,
size_t rollSize,
bool threadSafe,
int flushInterval,
int checkEveryN)
: basename_(basename),
rollSize_(rollSize),
flushInterval_(flushInterval),
checkEveryN_(checkEveryN),
count_(0),
mutex_(threadSafe ? new MutexLock : NULL),//是否要求線程安全
startOfPeriod_(0),
lastRoll_(0),
lastFlush_(0)
{
assert(basename.find('/') == string::npos);//文件名裏沒有古怪的符號
rollFile();
}
LogFile::~LogFile()
{
}
void LogFile::append(const char* logline, int len)
{
if (mutex_)//沒有必要專寫一個加鎖版本
{
MutexLockGuard lock(*mutex_);
append_unlocked(logline, len);
}
else
{
append_unlocked(logline, len);
}
}
void LogFile::flush()//立刻寫到文件
{
if (mutex_)
{
MutexLockGuard lock(*mutex_);
file_->flush();
}
else
{
file_->flush();
}
}
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_;//time_t就是個long int,這裏把天數的餘數清除掉
if (thisPeriod_ != startOfPeriod_)//不是同一天
{
rollFile();//滾動日誌文件
}
else if (now - lastFlush_ > flushInterval_)//達到刷新間隔
{
lastFlush_ = now;
file_->flush();//寫文件
}
}
}
}
bool LogFile::rollFile()//嘗試滾動文件,對時間再檢查一次
{
time_t now = 0;
string filename = getLogFileName(basename_, &now);//得到文件名和當前時間
time_t start = now / kRollPerSeconds_ * kRollPerSeconds_;//保存1970到現在經過的天數
if (now > lastRoll_)//這不應該有誤
{
lastRoll_ = now;//滾動時間
lastFlush_ = now;//刷新時間
startOfPeriod_ = start;//創建時間(天)
file_.reset(new FileUtil::AppendFile(filename));//重新設置一個可追加的文件
return true;
}
return false;//不應該發生
}
string LogFile::getLogFileName(const string& basename, time_t* now)
{
string filename;
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();//再加上主機名
char pidbuf[32];
snprintf(pidbuf, sizeof pidbuf, ".%d", ProcessInfo::pid());
filename += pidbuf;//再加上線程id
filename += ".log";//尾綴
return filename;
}