muduo网络库源码学习————日志滚动
来源:互联网 发布:图象扭曲算法 编辑:程序博客网 时间:2024/05/21 12:07
muduo库里面的实现日志滚动有两种条件,一种是日志文件大小达到预设值,另一种是时间到达超过当天。滚动日志类的文件是LogFile.cc ,LogFile.h
代码如下:
LogFile.cc
#include <muduo/base/LogFile.h>#include <muduo/base/Logging.h> // strerror_tl#include <muduo/base/ProcessInfo.h>#include <assert.h>#include <stdio.h>#include <time.h>using namespace muduo;//LogFile里面嵌套的File类// not thread safeclass LogFile::File : boost::noncopyable{ public: //传进文件名称,执行打开文件,文件指针保存到fp_ explicit File(const string& filename): fp_(::fopen(filename.data(), "ae")), writtenBytes_(0) { assert(fp_);//断言文件已经打开 ::setbuffer(fp_, buffer_, sizeof buffer_);//设定文件指针的缓冲区 // posix_fadvise POSIX_FADV_DONTNEED ? } ~File() {//析构函数关闭文件指针 ::fclose(fp_); } //把logline这一行信息添加到文件当中 void append(const char* logline, const size_t len) { size_t n = write(logline, len);//这个write是一个内部的成员函数 //计算剩余的字节数,len是要写入的,n是已经写入的 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, "LogFile::File::append() failed %s\n", strerror_tl(err)); } break; } n += x;//更新已经写的个数 remain = len - n; // remain -= x,更新剩余字节数 } writtenBytes_ += len;//更新已经写的字节数 } void flush() {//清空缓冲区 ::fflush(fp_); } //返回已经写的字节数 size_t writtenBytes() const { return writtenBytes_; } private: size_t write(const char* logline, size_t len) {#undef fwrite_unlocked//使用fwrite_unlocked方式写入效率会高一些 return ::fwrite_unlocked(logline, 1, len, fp_); } FILE* fp_;//fp_为文件指针 char buffer_[64*1024];//文件指针的缓冲区,64k size_t writtenBytes_;//已经写入的字节数};LogFile::LogFile(const string& basename, size_t rollSize, bool threadSafe, int flushInterval) : basename_(basename),//日志文件的basename rollSize_(rollSize),//日志文件写到rollSize_这么大的容量时就换一个新的文件 flushInterval_(flushInterval),//日志写入的间隔时间,默认是3秒钟 count_(0),//计数器初始化为0 mutex_(threadSafe ? new MutexLock : NULL),//如果是线程安全的则构造一个互斥锁 //mutex_是个智能指针能够自动销毁 startOfPeriod_(0),//开始记录日志时间(将会调整至0点时间) lastRoll_(0),//上一次滚动日志的时间 lastFlush_(0)//上一次日志写入文件的时间{ assert(basename.find('/') == string::npos);//断言basename不能找到'/' 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_)//如果已经写入的字节数大于rollSize_ { rollFile();//滚动日志 } else {//否则查看计数值count是否超过kCheckTimeRoll_ if (count_ > kCheckTimeRoll_) { 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(); } } else { ++count_;//计数值增加 } }}//滚动日志void LogFile::rollFile(){ time_t now = 0; string filename = getLogFileName(basename_, &now);//获取文件名,并且返回时间 //这里除以kRollPerSeconds_又乘以kRollPerSeconds_表示对齐至kRollPerSeconds_的整数倍 //即把时间调整到当天的零点 time_t start = now / kRollPerSeconds_ * kRollPerSeconds_; if (now > lastRoll_) {//滚动日志 lastRoll_ = now; lastFlush_ = now; startOfPeriod_ = start; file_.reset(new File(filename));//产生一个新的日志文件 }}//获取文件名string LogFile::getLogFileName(const string& basename, time_t* now){ string filename; //string类对象filename预留这么多的空间,basename后面还要加内容,所以再加64 filename.reserve(basename.size() + 64); filename = basename; char timebuf[32]; char pidbuf[32]; struct tm tm; *now = time(NULL);//获取当前时间 //gmtime_r是线程安全的,gmtime不是线程安全的 gmtime_r(now, &tm); // FIXME: localtime_r ?GMT时间也就是UTC时间,保存在tm中 //把时间格式化放在缓冲区timebuf中 strftime(timebuf, sizeof timebuf, ".%Y%m%d-%H%M%S.", &tm); //时间加入到filename中 filename += timebuf; filename += ProcessInfo::hostname();//主机名称加入到filename中 //获取进程号pid snprintf(pidbuf, sizeof pidbuf, ".%d", ProcessInfo::pid()); //pid加入到filename中 filename += pidbuf; //后缀名加入到filename中 filename += ".log"; //返回filename return filename;}
LogFile.h
//日志滚动#ifndef MUDUO_BASE_LOGFILE_H#define MUDUO_BASE_LOGFILE_H#include <muduo/base/Mutex.h>#include <muduo/base/Types.h>#include <boost/noncopyable.hpp>#include <boost/scoped_ptr.hpp>namespace muduo{class LogFile : boost::noncopyable{ public: //线程安全默认是true,日志写入的间隔时间是3秒钟 LogFile(const string& basename,size_t rollSize,bool threadSafe = true,int flushInterval = 3); ~LogFile(); //将长度为len的一行添加到日志当中 void append(const char* logline, int len); //清空缓冲区 void flush(); private: //以不加锁的方式添加 void append_unlocked(const char* logline, int len); //获取日志文件的名称 static string getLogFileName(const string& basename, time_t* now); //滚动日志 void rollFile(); const string basename_;//日志文件的basename const size_t rollSize_;//日志文件写到rollSize_这么大的容量时就换一个新的文件 const int flushInterval_;//日志写入的间隔时间 int count_;//计数器,当达到kCheckTimeRoll_会去检测一下是否需要写入新文件 boost::scoped_ptr<MutexLock> mutex_;//互斥量的智能指针 time_t startOfPeriod_;//开始记录日志时间(将会调整至0点时间) time_t lastRoll_;//上一次滚动日志的时间 time_t lastFlush_;//上一次日志写入文件的时间 class File;//File嵌套类 boost::scoped_ptr<File> file_;//File嵌套类的一个智能指针 const static int kCheckTimeRoll_ = 1024; const static int kRollPerSeconds_ = 60*60*24;//一天的秒数};}#endif // MUDUO_BASE_LOGFILE_H
测试代码则是输出一系列的日志,进行日志滚动,代码如下:
LogFile_test.cc
//日志滚动测试程序#include <muduo/base/LogFile.h>#include <muduo/base/Logging.h>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[]){ char name[256]; strncpy(name, argv[0], 256); //滚动日志的话肯定就是输出到文件了 g_logFile.reset(new muduo::LogFile(::basename(name), 200*1000)); muduo::Logger::setOutput(outputFunc); muduo::Logger::setFlush(flushFunc); muduo::string line = "1234567890 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ "; for (int i = 0; i < 10000; ++i) {//输出日志 LOG_INFO << line << i; usleep(1000); }}
运行结果如下:
另一个文件测试日志的各种性能
Logging_test.cc
#include <muduo/base/Logging.h>#include <muduo/base/LogFile.h>#include <muduo/base/ThreadPool.h>#include <stdio.h>int g_total;FILE* g_file;//文件指针boost::scoped_ptr<muduo::LogFile> g_logFile;//智能指针void dummyOutput(const char* msg, int len){ g_total += len;//更新g_total if (g_file)//最开始g_file,g_logFile都是空的,不执行 { fwrite(msg, 1, len, g_file); } else if (g_logFile) { g_logFile->append(msg, len); }}void bench(const char* type){ muduo::Logger::setOutput(dummyOutput);//更改默认输出 muduo::Timestamp start(muduo::Timestamp::now());//开始时间 g_total = 0; int n = 1000*1000;//100w次 const bool kLongLog = false; muduo::string empty = " "; muduo::string longStr(3000, 'X');//3000个X longStr += " "; for (int i = 0; i < n; ++i) { LOG_INFO << "Hello 0123456789" << " abcdefghijklmnopqrstuvwxyz" << (kLongLog ? longStr : empty)//kLongLog真,输出longStr,假输出empty << i; } muduo::Timestamp end(muduo::Timestamp::now());//截止时间 double seconds = timeDifference(end, start);//计算时间差 //打印到标准输出 printf("%12s: %f seconds, %d bytes, %10.2f msg/s, %.2f MiB/s\n", type, seconds, g_total, n / seconds, g_total / seconds / (1024 * 1024));}void logInThread(){//线程池当中的线程,更新日志 LOG_INFO << "logInThread"; usleep(1000);}int main(){//获取父进程pid getppid(); // for ltrace and strace //建立线程池 muduo::ThreadPool pool("pool"); //线程池启动5个线程 pool.start(5); //线程池添加5个任务 pool.run(logInThread); pool.run(logInThread); pool.run(logInThread); pool.run(logInThread); pool.run(logInThread); //主线程输出日志 LOG_TRACE << "trace"; LOG_DEBUG << "debug"; LOG_INFO << "Hello"; LOG_WARN << "World"; LOG_ERROR << "Error"; LOG_INFO << sizeof(muduo::Logger); LOG_INFO << sizeof(muduo::LogStream); LOG_INFO << sizeof(muduo::Fmt); LOG_INFO << sizeof(muduo::LogStream::Buffer); //睡眠1秒钟 sleep(1); //性能测试程序 bench("nop"); char buffer[64*1024]; //空文件,测试数据写入到/dev/null中的性能 g_file = fopen("/dev/null", "w"); setbuffer(g_file, buffer, sizeof buffer); bench("/dev/null"); fclose(g_file); //测试数据写入到/tmp/log中的性能 g_file = fopen("/tmp/log", "w"); setbuffer(g_file, buffer, sizeof buffer); bench("/tmp/log"); fclose(g_file); g_file = NULL; //不是线程安全的 g_logFile.reset(new muduo::LogFile("test_log_st", 500*1000*1000, false)); bench("test_log_st"); //线程安全的 g_logFile.reset(new muduo::LogFile("test_log_mt", 500*1000*1000, true)); bench("test_log_mt"); g_logFile.reset();}
运行结果如下:
1 0
- muduo网络库源码学习————日志滚动
- muduo网络库源码学习————日志类封装
- muduo网络库源码学习————互斥锁
- muduo网络库源码学习————Timestamp.cc
- muduo网络库源码学习————原子性操作Atomic.h
- muduo网络库源码学习————Exception类
- muduo网络库源码学习————线程类
- muduo网络库源码学习————条件变量
- muduo网络库源码学习————无界队列和有界队列
- muduo网络库源码学习————线程池实现
- muduo网络库源码学习————线程安全
- muduo网络库源码学习————线程特定数据
- muduo网络库源码学习————线程本地单例类封装
- muduo库阅读(16)——异步日志类
- muduo库阅读(17)——日志文件类
- muduo库阅读(18)——日志记录器
- 《Linux多线程服务端编程》—muduo网络库(1)
- muduo 日志库学习(一)
- 45天的坚持是否值得
- java基础 求素数
- iOS_UI_3_UITextField
- uva 729(枚举排列)
- 总结一些it学习的网站
- muduo网络库源码学习————日志滚动
- 操作系统中的进程与线程
- PHP生成验证码时“图像因其本身有错无法显示”的解决方法
- XAMPP+TestLink安装步骤和测试流程
- Ubuntu14.04增加右键菜单=>“run_on_python”
- Hibernate的查询方式知多少 二
- 4. Median of Two Sorted Arrays
- 线性表、栈和队列
- 程序员总结:帮助你早些明白一些道理