在很多应用场合,我们是需要实现日志文件滚动的,特别是在一些长期运行的服务器程序中,如果把所有的日志都记录在一个文件之中,势必会造成日志文件越来越大。当日志内容很多的时候,万一哪天突然需要查询某个日志信息就会显得十分不便。所以,支持日志文件滚动是很多日志库都支持的功能,而文件滚动又可以分为按大小滚动和按时间滚动。

按大小滚动文件

在 Easylogging++ 中,已经实现了按照日志文件大小来滚动日志记录。在前面《日志库EasyLogging++学习系列(3)—— 配置功能》一文中介绍配置文件时,有一个配置项:MAX_LOG_FILE_SIZE,这个配置项的值(以字节为单位)表示的就是日志文件的最大大小。一旦日志文件的大小达到这个配置项设置的值,日志文件就会自动清空文件中所有的日志记录,并重新开始写入。不过配置项 MAX_LOG_FILE_SIZE 在默认情况下是不生效的,需要设置标记:LoggingFlag::StrictLogFileSizeCheck 来激活。另外,如果我们想要保留之前的日志记录,那么我们可以注册一个回调函数,这个回调函数将会允许我们在清空日志文件之前对日志文件进行一次处理。下面的代码演示了按大小滚动日志文件,并通过回调函数保留了所有的日志记录:

[cpp] view plaincopy print?
  1. #include "easylogging++.h"
  2. INITIALIZE_EASYLOGGINGPP
  3. static unsigned int idx;
  4. void rolloutHandler(const char* filename, std::size_t size)
  5. {
  6. /// 备份日志
  7. system("mkdir bin");
  8. std::stringstream ss;
  9. ss << "move " << filename << " bin\\log_backup_" << ++idx;
  10. system(ss.str().c_str());
  11. }
  12. int main(int, char**)
  13. {
  14. idx = 0;
  15. el::Loggers::addFlag(el::LoggingFlag::StrictLogFileSizeCheck);
  16. el::Loggers::reconfigureAllLoggers(el::ConfigurationType::MaxLogFileSize, "100");
  17. /// 注册回调函数
  18. el::Helpers::installPreRollOutCallback(rolloutHandler);
  19. for (int i = 0; i < 100; ++i)
  20. {
  21. LOG(INFO) << "Test";
  22. }
  23. /// 注销回调函数
  24. el::Helpers::uninstallPreRollOutCallback();
  25. return 0;
  26. }
#include "easylogging++.h"INITIALIZE_EASYLOGGINGPPstatic unsigned int idx;void rolloutHandler(const char* filename, std::size_t size)
{/// 备份日志system("mkdir bin");std::stringstream ss;ss << "move " << filename << " bin\\log_backup_" << ++idx;system(ss.str().c_str());
}int main(int, char**)
{idx = 0;el::Loggers::addFlag(el::LoggingFlag::StrictLogFileSizeCheck);el::Loggers::reconfigureAllLoggers(el::ConfigurationType::MaxLogFileSize, "100");/// 注册回调函数el::Helpers::installPreRollOutCallback(rolloutHandler);for (int i = 0; i < 100; ++i){LOG(INFO) << "Test";}/// 注销回调函数el::Helpers::uninstallPreRollOutCallback();return 0;
}

通过配置文件来设置 配置项  MAX_LOG_FILE_SIZE 的大小也可以实现上述演示代码的效果,另外我们还可以设置不同级别的日志文件按照不同的文件大小来滚动。如果不小心忘记了设置标记:LoggingFlag::StrictLogFileSizeCheck ,我们还可以通过调用函数 el::Helpers::validateFileRolling(el::Logger*, const el::Level&) 以手动的方式来检查日志滚动,建议各位小伙伴可以自己尝试一下。

按时间滚动文件

在 Easylogging++ 中是没有实现按时间滚动日志文件的,不过既然是开源的日志库,我们可以参考着按大小滚动日志文件的实现方式,根据自己的需求去实现一个按时间滚动日志文件的功能。下面简单地说明一下实现步骤:

  • 在按大小滚动日志文件中有配置项 MAX_LOG_FILE_SIZE,所以我们也增加一个配置项 LOG_FILE_ROLLING_TIME ,新增配置项的值类型为 char* 型,其值只能是以下四个:"MONTH" 、"DAY"、"HOUR"、"MINUTE",其中"MONTH"表示按月份滚动日志文件,"DAY"表示按天数滚动日志文件,"HOUR"表示按小时滚动日志文件,"MINUTE"表示按分钟滚动日志文件。
  • 在按大小滚动日志文件中有标记 LoggingFlag::StrictLogFileSizeCheck 来激活滚动功能,所以我们在实现按时间滚动日志文件的功能中也增加一个标记 LoggingFlag::StrictLogFileTimeCheck 来激活滚动功能。
  • 在按大小滚动日志文件中,允许我们在清空文件重新写入之前通过回调函数对日志文件进行处理,所以我们在按时间滚动日志文件的功能实现中,也同样保留该回调函数的功能,但是在回调函数中增加了一个参数,用来区分是按大小滚动日志文件还是按时间滚动日志文件。
  • 本文的最后提供了实现按时间滚动日志文件功能的Easylogging++ 源码,实现的细节可在源码中搜索“modify by Fish”来查看。 因本功能目前只限于本人在使用,如有错误,欢迎指正。
下面的代码演示了如何使用新增的按时间滚动日志文件的功能:
[cpp] view plaincopy print?
  1. #include "easylogging++.h"
  2. INITIALIZE_EASYLOGGINGPP
  3. void rolloutHandler(const char* filename, std::size_t size, el::base::RollingLogFileBasis rollingbasis)
  4. {
  5. switch (rollingbasis)
  6. {
  7. case el::base::RollingLogFileBasis::RollLog_FileSize:
  8. /// 按大小滚动日志文件
  9. break;
  10. case el::base::RollingLogFileBasis::RollLog_DateTime:
  11. /// 按时间滚动日志文件
  12. {
  13. time_t cuurenttime = time(NULL);
  14. cuurenttime -= 60;
  15. struct::tm oneMinuteAgo;
  16. localtime_s(&oneMinuteAgo, &cuurenttime);
  17. std::string filenameTemp = filename;
  18. int pos = filenameTemp.rfind('.');
  19. filenameTemp = filenameTemp.substr(0, pos);
  20. char backupFile[MAX_PATH] = { 0 };
  21. sprintf_s(backupFile, MAX_PATH, "%s_%04d%02d%02d%02d%02d.log", filenameTemp.c_str(), oneMinuteAgo.tm_year + 1900
  22. , oneMinuteAgo.tm_mon + 1, oneMinuteAgo.tm_mday, oneMinuteAgo.tm_hour, oneMinuteAgo.tm_min);
  23. /// 自定义日志备份
  24. std::stringstream ss;
  25. ss << "move " << filename << " " << backupFile;
  26. system(ss.str().c_str());
  27. }
  28. break;
  29. default:
  30. break;
  31. }
  32. }
  33. int main(int, char**)
  34. {
  35. el::Loggers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog);
  36. el::Loggers::addFlag(el::LoggingFlag::StrictLogFileTimeCheck);
  37. el::Loggers::reconfigureAllLoggers(el::ConfigurationType::LogFileRollingTime, "minute");    /// 按分钟滚动日志文件
  38. /// 注册回调函数
  39. el::Helpers::installPreRollOutCallback(rolloutHandler);
  40. for (int i = 0; i < 100000; ++i)
  41. {
  42. LOG(DEBUG) << "DEBUG";
  43. LOG(INFO) << "INFO";
  44. DLOG(INFO) << "DEBUG";
  45. LOG(WARNING) << "WARNING";
  46. LOG(ERROR) << "ERROR";
  47. LOG(FATAL) << "FATAL";
  48. LOG(TRACE) << "TRACE";
  49. VLOG(0) << "VERBOSE";
  50. Sleep(1000);
  51. }
  52. /// 注销回调函数
  53. el::Helpers::uninstallPreRollOutCallback();
  54. return 0;
  55. }
#include "easylogging++.h"INITIALIZE_EASYLOGGINGPPvoid rolloutHandler(const char* filename, std::size_t size, el::base::RollingLogFileBasis rollingbasis)
{switch (rollingbasis){case el::base::RollingLogFileBasis::RollLog_FileSize:/// 按大小滚动日志文件break;case el::base::RollingLogFileBasis::RollLog_DateTime:/// 按时间滚动日志文件{time_t cuurenttime = time(NULL);cuurenttime -= 60;struct::tm oneMinuteAgo;localtime_s(&oneMinuteAgo, &cuurenttime);std::string filenameTemp = filename;int pos = filenameTemp.rfind('.');filenameTemp = filenameTemp.substr(0, pos);char backupFile[MAX_PATH] = { 0 };sprintf_s(backupFile, MAX_PATH, "%s_%04d%02d%02d%02d%02d.log", filenameTemp.c_str(), oneMinuteAgo.tm_year + 1900, oneMinuteAgo.tm_mon + 1, oneMinuteAgo.tm_mday, oneMinuteAgo.tm_hour, oneMinuteAgo.tm_min);/// 自定义日志备份std::stringstream ss;ss << "move " << filename << " " << backupFile;system(ss.str().c_str());}break;default:break;}
}int main(int, char**)
{el::Loggers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog);el::Loggers::addFlag(el::LoggingFlag::StrictLogFileTimeCheck);el::Loggers::reconfigureAllLoggers(el::ConfigurationType::LogFileRollingTime, "minute");  /// 按分钟滚动日志文件/// 注册回调函数el::Helpers::installPreRollOutCallback(rolloutHandler);for (int i = 0; i < 100000; ++i){LOG(DEBUG) << "DEBUG";LOG(INFO) << "INFO";DLOG(INFO) << "DEBUG";LOG(WARNING) << "WARNING";LOG(ERROR) << "ERROR";LOG(FATAL) << "FATAL";LOG(TRACE) << "TRACE";VLOG(0) << "VERBOSE";Sleep(1000);}/// 注销回调函数el::Helpers::uninstallPreRollOutCallback();return 0;
}

特别提醒:

  • 因为在新增按时间滚动日志文件的功能中修改了回调函数,所以如果使用按大小滚动日志文件功能,也需要使用修改后的回调函数。
  • 因为只有在有日志写入的时候才判断是否需要更新文件,所以如果无日志记录,日志文件是无法按时间滚动的。

在实际应用中,如果日志按时间滚动,我们的日志文件基本上都会以时间来命名,所以为了更加方便地使用,我们可以在实现了按时间滚动功能的代码上再增加一个宏定义ELPP_NAME_LOG_FILE_AFTER_TIME。通过定义这个宏,我们实现了这样一个功能:当按时间滚动日志时,可以自动地创建新的日志文件,并且会以滚动时间命名新建文件。不过这个功能目前并不是很完善,使用起来有以下几个限制条件:

  • 不同级别的日志必须保存在不同的日志文件中,否则无法实现日志滚动。
  • 按月份滚动的日志文件名中日期格式须配置:%datetime{%Y%M},如FILENAME = "log\\test_%datetime{%Y%M}.log"。
  • 按天数滚动的日志文件名中日期格式须配置:%datetime{%Y%M%d},如FILENAME = "log\\test_%datetime{%Y%M%d}.log"。
  • 按小时滚动的日志文件名中日期格式须配置:%datetime{%Y%M%d%H},如FILENAME = "log\\test_%datetime{%Y%M%d%H}.log"。
  • 按分钟滚动的日志文件名中日期格式须配置:%datetime{%Y%M%d%H%m},如FILENAM="log\\test_%datetime{%Y%M%d%H%m}.log"。
虽然使用这个功能有些限制条件,但是这些条件基本符合我平时的使用习惯,因为不同级别的日志在实际应用中我肯定是会保存在不同的文件中,而且文件名中的日期格式也和滚动的时间间隔一致,所以我也就没有去完善这个功能。下面通过配置文件的方式演示了这个功能:
[cpp] view plaincopy print?
  1. #define ELPP_NAME_LOG_FILE_AFTER_TIME
  2. #define ELPP_NO_DEFAULT_LOG_FILE
  3. #include "easylogging++.h"
  4. INITIALIZE_EASYLOGGINGPP
  5. int main(int, char**)
  6. {
  7. el::Loggers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog);
  8. el::Loggers::addFlag(el::LoggingFlag::StrictLogFileTimeCheck);
  9. el::Configurations conf("log.conf");
  10. el::Loggers::reconfigureAllLoggers(conf);
  11. for (int i = 0; i < 100000; ++i)
  12. {
  13. LOG(DEBUG) << "DEBUG";
  14. LOG(INFO) << "INFO";
  15. LOG(WARNING) << "WARNING";
  16. LOG(ERROR) << "ERROR";
  17. LOG(FATAL) << "FATAL";
  18. LOG(TRACE) << "TRACE";
  19. VLOG(0) << "VERBOSE";
  20. Sleep(1000);
  21. }
  22. return 0;
  23. }
#define ELPP_NAME_LOG_FILE_AFTER_TIME
#define ELPP_NO_DEFAULT_LOG_FILE
#include "easylogging++.h"INITIALIZE_EASYLOGGINGPPint main(int, char**)
{el::Loggers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog);el::Loggers::addFlag(el::LoggingFlag::StrictLogFileTimeCheck);el::Configurations conf("log.conf");el::Loggers::reconfigureAllLoggers(conf);for (int i = 0; i < 100000; ++i){LOG(DEBUG) << "DEBUG";LOG(INFO) << "INFO";LOG(WARNING) << "WARNING";LOG(ERROR) << "ERROR";LOG(FATAL) << "FATAL";LOG(TRACE) << "TRACE";VLOG(0) << "VERBOSE";Sleep(1000);}return 0;
}

其中的配置文件 log.conf 内容如下:

[plain] view plaincopy print?
  1. * GLOBAL:
  2. FORMAT                  =   "[%level | %datetime] | %msg"
  3. ENABLED                 =   true
  4. TO_FILE                 =   true
  5. TO_STANDARD_OUTPUT      =   true
  6. LOG_FLUSH_THRESHOLD     =   0
  7. MILLISECONDS_WIDTH      =   3
  8. PERFORMANCE_TRACKING    =   false
  9. MAX_LOG_FILE_SIZE       =   2097152 ## Throw log files away after 2097152 2MB / 209715200 200MB / 4398046511104 1GB
  10. LOG_FILE_ROLLING_TIME   =   minute
  11. * INFO:
  12. FILENAME                =   "log\\test_%datetime{%Y%M%d%H%m}_info.log"
  13. * DEBUG:
  14. FILENAME                =   "log\\test_%datetime{%Y%M%d%H%m}_debug.log"
  15. * WARNING:
  16. FILENAME                =   "log\\test_%datetime{%Y%M%d%H%m}_warning.log"
  17. * TRACE:
  18. FILENAME                =   "log\\test_%datetime{%Y%M%d%H%m}_trace.log"
  19. * VERBOSE:
  20. FILENAME                =   "log\\test_%datetime{%Y%M%d%H%m}_verbose.log"
  21. * ERROR:
  22. FILENAME                =   "log\\test_%datetime{%Y%M%d%H%m}_error.log"
  23. * FATAL:
  24. FILENAME                =   "log\\test_%datetime{%Y%M%d%H%m}_fatal.log"
* GLOBAL:FORMAT                  =   "[%level | %datetime] | %msg"ENABLED                 =   trueTO_FILE                 =   trueTO_STANDARD_OUTPUT      =   trueLOG_FLUSH_THRESHOLD     =   0MILLISECONDS_WIDTH      =   3PERFORMANCE_TRACKING    =   falseMAX_LOG_FILE_SIZE       =   2097152 ## Throw log files away after 2097152 2MB / 209715200 200MB / 4398046511104 1GBLOG_FILE_ROLLING_TIME   =   minute
* INFO:FILENAME                =   "log\\test_%datetime{%Y%M%d%H%m}_info.log"
* DEBUG:FILENAME                =   "log\\test_%datetime{%Y%M%d%H%m}_debug.log"
* WARNING:FILENAME                =   "log\\test_%datetime{%Y%M%d%H%m}_warning.log"
* TRACE:FILENAME                =   "log\\test_%datetime{%Y%M%d%H%m}_trace.log"
* VERBOSE:FILENAME                =   "log\\test_%datetime{%Y%M%d%H%m}_verbose.log"
* ERROR:FILENAME                =   "log\\test_%datetime{%Y%M%d%H%m}_error.log"
* FATAL:FILENAME                =   "log\\test_%datetime{%Y%M%d%H%m}_fatal.log"

利用上述演示代码,可以完全自动地按照每分钟的间隔创建如下格式的日志文件:

按时间滚动日志文件之所以写了这么多,最主要的原因就是为了说明在开源的日志库中,我们可以完全自主地按照自己的想法来实现一些符合自己需求的功能。比如上面介绍的宏定义ELPP_NAME_LOG_FILE_AFTER_TIME功能,虽然还不完善,但是只要严格按照限制条件来使用,完全可以达到我们想要的效果。对于开源代码,能够直接使用还并不是我们最终的目的,能够在开源的基础上加以修改完善并应用于实际编程当中才是我们学习开源代码的初衷。
猛戳这里,下载源码!

日志库EasyLogging++学习系列(10)—— 日志文件滚动相关推荐

  1. 日志库EasyLogging++学习系列(7)—— 记录方式详解

    在前面所列文章的演示代码中,其实已经展示了一部分记录日志的方式.为了使用方便,在 Easylogging++ 中,通过使用宏的集合来完成日志记录. 普通日志记录 对于普通的日志记录,我们可以选择以下两 ...

  2. 日志库EasyLogging++学习系列(5)—— 辅助配置功能

    正如前面<日志库EasyLogging++学习系列(3)-- 配置功能>文中最后提到的,在某些应用场景下,我们还需要通过其他的一些配置手段来辅助我们完成某些特殊功能,这些辅助配置手段包括设 ...

  3. 日志库EasyLogging++学习系列(3)—— 配置功能

    在前面的文章 <日志库Easylogging++学习系列(1) -- 简要介绍 >中,我们已经初步见识到了 Easylogging++ 日志库强大的配置功能.那么配置文件中各个字段的意义是 ...

  4. 日志库EasyLogging++学习系列(6)—— 日志记录器

    所有的日志都是由日志记录器完成的,日志记录器使用唯一的 ID(大小写敏感)来标识.在 Easylogging++ 中默认了三个现有的日志记录器: 默认日志记录器,其 ID 为:default 性能日志 ...

  5. 日志库EasyLogging++学习系列(2)—— 日志级别

    在很多的C++日志库中,日志信息会根据严重性来划分级别,使用者可以设置严重性级别门阀值来控制日志的输出,即严重性级别在该门阀值以上的日志信息才进行记录.以此不同,在Easylogging++日志库中, ...

  6. 日志库EasyLogging++学习系列(11)—— 共享日志库

    在前面的学习系列文章中,我们都是在单独的一个应用程序中使用 Easylogging++ 日志库.其实 Easylogging++ 日志库是可以共享给动态库.静态库以及应用程序共同使用的.在编写一些大型 ...

  7. 日志库EasyLogging++学习系列(1)—— 简要介绍

    对于有开发经验的程序员来说,记录程序执行日志是一件必不可少的事情.通过查看和分析日志信息,不仅可以有效地帮助我们调试程序,而且当程序正式发布运行之后,更是可以帮助我们快速.准确地定位问题.在现在这个开 ...

  8. 日志库EasyLogging++学习系列(4)—— 格式说明符

    在上一篇文章中,主要记录了如何使用 Easylogging++ 的配置功能,虽然已经用了很大的篇幅尽可能详细地加以记录,不过相信有些细心的小伙伴可能已经发现遗漏了些什么,请看下面两句摘自 my_log ...

  9. 日志库EasyLogging++学习系列(9)—— 性能跟踪功能

    性能跟踪是 Easylogging++ 其中一个非常显著的功能,而且使用起来也十分简单.如果在Windows平台下使用性能跟踪的话,其原理是基于 Windows API函数 GetSystemTime ...

最新文章

  1. java phantomjs 2.1.1_Java之网络爬虫WebCollector2.1.2+selenium2.44+phantomjs2.1.1
  2. dva + antd + mockjs 实现用户管理
  3. 基于redis AE异步网络架构
  4. c语言如何输入汉字_C语言入门的第一个小程序
  5. 空间谱专题13:联合解算DOA(ML/AP)
  6. [css] 你是如何压缩字体的?
  7. 某教授对“中国式科研”的酒后真言
  8. 微软超融合私有云测试11-SCVMM2016部署之添加Hyper-V集群
  9. fastrtext︱R语言使用facebook的fasttext快速文本分类算法
  10. 【Maven】1.使用myecplise配置自己的Maven配置,不使用默认的maven
  11. 一个不错的架构图:基于SpringCloud的微服务项目
  12. css 背景图 左右空白,缩小窗口时CSS背景图出现右侧空白BUG的解决方法
  13. javaswing个人记账系统 java swing mysql实现的个人记账系统源码(1012)
  14. 微信支付接口详细步骤
  15. easyui select 默认选中指定值
  16. Hadoop 中 FileSplit (文件分割器)的简单使用
  17. 哈尔滨学计算机编程学校,哈尔滨中小学生编程学校排名
  18. 什么叫横向比较和纵向比较
  19. Error instantiating servlet class 的解决办法
  20. yum命令的基本用法

热门文章

  1. 连接到多台mysql_Oracle通过dblink连接到多台MySQL
  2. everything文件搜索_Everything,闪电搜索,百万文件100%秒搜,真是文件搜索神器!...
  3. 华为鸿蒙os公测需要多久,华为系统公测到正式版要多久
  4. 2019年工程造价表_2019年工程造价咨询统计公报
  5. Windows平台编译Webkit
  6. numa对MySQL多实例性能影响
  7. struts2.0简单页面 (不带拦截器和带拦截器案例)
  8. Web 第二阶段Java Script (续)
  9. [CareerCup] 4.5 Validate Binary Search Tree 验证二叉搜索树
  10. (转)NAT与NAT穿透 原理