- FileUtil
- LogFile
FileUtil
这个类负责把日志写入到文件
FileUtil.h
namespace FileUtil
{
// read small file < 64KB
class ReadSmallFile : boost::noncopyable
{
public:
ReadSmallFile(StringArg filename);
~ReadSmallFile();
// return errno
template<typename String>
int readToString(int maxSize,
String* content,
int64_t* fileSize,
int64_t* modifyTime,
int64_t* createTime);
/// Read at maxium kBufferSize into buf_
// return errno
int readToBuffer(int* size);
const char* buffer() const { return buf_; }
static const int kBufferSize = 64*1024;
private:
int fd_;
int err_;
char buf_[kBufferSize];
};
// read the file content, returns errno if error happens.
template<typename String>
int readFile(StringArg filename,
int maxSize,
String* content,
int64_t* fileSize = NULL,
int64_t* modifyTime = NULL,
int64_t* createTime = NULL)
{
ReadSmallFile file(filename);
return file.readToString(maxSize, content, fileSize, modifyTime, createTime);
}
// not thread safe
class AppendFile : boost::noncopyable
{
public:
explicit AppendFile(StringArg filename);
~AppendFile();
void append(const char* logline, const size_t len);
void flush();
size_t writtenBytes() const { return writtenBytes_; }
private:
size_t write(const char* logline, size_t len);
FILE* fp_;//指向文件的指针
char buffer_[64*1024];//缓存
size_t writtenBytes_;//已经使用了多少
};
}
FileUtil.cc
FileUtil::AppendFile::AppendFile(StringArg filename)
: fp_(::fopen(filename.c_str(), "ae")), // ‘e‘ for O_CLOEXEC
writtenBytes_(0)
{
assert(fp_);
::setbuffer(fp_, buffer_, sizeof buffer_);
// posix_fadvise POSIX_FADV_DONTNEED ?
}
FileUtil::AppendFile::~AppendFile()
{
::fclose(fp_);//析构函数关闭文件
}
void FileUtil::AppendFile::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, "AppendFile::append() failed %s\n", strerror_tl(err));
}
break;
}
n += x;
remain = len - n; // remain -= x
}
writtenBytes_ += len;
}
void FileUtil::AppendFile::flush()//输出到文件
{
::fflush(fp_);
}
size_t FileUtil::AppendFile::write(const char* logline, size_t len)
{
// #undef fwrite_unlocked
return ::fwrite_unlocked(logline, 1, len, fp_);
}
FileUtil::ReadSmallFile::ReadSmallFile(StringArg filename)
: fd_(::open(filename.c_str(), O_RDONLY | O_CLOEXEC)),
err_(0)
{
buf_[0] = ‘\0‘;
if (fd_ < 0)
{
err_ = errno;
}
}
FileUtil::ReadSmallFile::~ReadSmallFile()
{
if (fd_ >= 0)
{
::close(fd_); // FIXME: check EINTR
}
}
// return errno
template<typename String>
int FileUtil::ReadSmallFile::readToString(int maxSize,
String* content,
int64_t* fileSize,
int64_t* modifyTime,
int64_t* createTime)
{
//BOOST_STATIC_ASSERT(sizeof(off_t) == 8);
assert(content != NULL);
int err = err_;
if (fd_ >= 0)
{
content->clear();
if (fileSize)
{
struct stat statbuf;
if (::fstat(fd_, &statbuf) == 0)
{
if (S_ISREG(statbuf.st_mode))
{
*fileSize = statbuf.st_size;
content->reserve(static_cast<int>(std::min(implicit_cast<int64_t>(maxSize), *fileSize)));
}
else if (S_ISDIR(statbuf.st_mode))
{
err = EISDIR;
}
if (modifyTime)
{
*modifyTime = statbuf.st_mtime;
}
if (createTime)
{
*createTime = statbuf.st_ctime;
}
}
else
{
err = errno;
}
}
while (content->size() < implicit_cast<size_t>(maxSize))
{
size_t toRead = std::min(implicit_cast<size_t>(maxSize) - content->size(), sizeof(buf_));
ssize_t n = ::read(fd_, buf_, toRead);
if (n > 0)
{
content->append(buf_, n);
}
else
{
if (n < 0)
{
err = errno;
}
break;
}
}
}
return err;
}
int FileUtil::ReadSmallFile::readToBuffer(int* size)
{
int err = err_;
if (fd_ >= 0)
{
ssize_t n = ::pread(fd_, buf_, sizeof(buf_)-1, 0);
if (n >= 0)
{
if (size)
{
*size = static_cast<int>(n);
}
buf_[n] = ‘\0‘;
}
else
{
err = errno;
}
}
return err;
}
template int FileUtil::readFile(StringArg filename,
int maxSize,
string* content,
int64_t*, int64_t*, int64_t*);
template int FileUtil::ReadSmallFile::readToString(
int maxSize,
string* content,
int64_t*, int64_t*, int64_t*);
#ifndef MUDUO_STD_STRING
template int FileUtil::readFile(StringArg filename,
int maxSize,
std::string* content,
int64_t*, int64_t*, int64_t*);
template int FileUtil::ReadSmallFile::readToString(
int maxSize,
std::string* content,
int64_t*, int64_t*, int64_t*);
LogFile
LogFile类负责日志的滚动,例如日大小达到指定大小、到达某一个时间点都会新建一个日志。
日志的名字为:日志名+日期+时间+主机名+线程ID+.log
LogFile.h
namespace FileUtil
{
class AppendFile;//前置声明
}
class LogFile : boost::noncopyable
{
public:
LogFile(const string& basename,
size_t rollSize,
bool threadSafe = true,//是否是线程安全。所以下面才有了unlocked版本的函数
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);
static string getLogFileName(const string& basename, time_t* now);//获取文件名字
const string basename_;//文件名字
const size_t rollSize_;//超过这个大小,则回滚文件
const int flushInterval_;//写入日志间隔
const int checkEveryN_;//没写入这么多条日志,检查一次是否要回滚
int count_;//写入日志次数,配合checkEveryN_
boost::scoped_ptr<MutexLock> mutex_;
time_t startOfPeriod_;//开始写入日志事件
time_t lastRoll_;//上一次滚动日志事件
time_t lastFlush_;//上一次写入日志时间
boost::scoped_ptr<FileUtil::AppendFile> file_;//文件相关的类,辅助写入到文件
const static int kRollPerSeconds_ = 60*60*24;//每隔多久回滚一次日志
};
LogFile.cc
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();//先回滚一次,在回滚中才会初始化file_。当file_.reset时,前一次的会在析构函数关闭
}
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_;
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_;
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;
filename += ".log";
return filename;
}
写一个测试程序:
#include <muduo/base/LogFile.h>
#include <muduo/base/Logging.h>
#include <string>
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[])
{
muduo::Logger::setOutput(outputFunc);
muduo::Logger::setFlush(flushFunc);
const std::string logName="LogFile";
g_logFile.reset(new muduo::LogFile(logName.c_str(), 150 * 1024));//100k
muduo::string logContent = "This is a test. This is a test. This is a test. This is a test. This is a test. This is a test.";
for (int i = 0; i < 10000; ++i)
{
LOG_INFO << logContent << i;
usleep(1000);
}
}
输出为:
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-12-12 16:21:15