本文件使用的是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;
}