在很多应用场合,我们是需要实现日志文件滚动的,特别是在一些长期运行的服务器程序中,如果把所有的日志都记录在一个文件之中,势必会造成日志文件越来越大。当日志内容很多的时候,万一哪天突然需要查询某个日志信息就会显得十分不便。所以,支持日志文件滚动是很多日志库都支持的功能,而文件滚动又可以分为按大小滚动和按时间滚动。
按大小滚动文件
在 Easylogging++ 中,已经实现了按照日志文件大小来滚动日志记录。在前面《日志库EasyLogging++学习系列(3)—— 配置功能》一文中介绍配置文件时,有一个配置项:MAX_LOG_FILE_SIZE,这个配置项的值(以字节为单位)表示的就是日志文件的最大大小。一旦日志文件的大小达到这个配置项设置的值,日志文件就会自动清空文件中所有的日志记录,并重新开始写入。不过配置项 MAX_LOG_FILE_SIZE 在默认情况下是不生效的,需要设置标记:LoggingFlag::StrictLogFileSizeCheck 来激活。另外,如果我们想要保留之前的日志记录,那么我们可以注册一个回调函数,这个回调函数将会允许我们在清空日志文件之前对日志文件进行一次处理。下面的代码演示了按大小滚动日志文件,并通过回调函数保留了所有的日志记录:
#include "easylogging++.h" INITIALIZE_EASYLOGGINGPP static 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++ 中是没有实现按时间滚动日志文件的,不过既然是开源的日志库,我们可以参考着按大小滚动日志文件的实现方式,根据自己的需求去实现一个按时间滚动日志文件的功能。下面简单地说明一下实现步骤:
#include "easylogging++.h" INITIALIZE_EASYLOGGINGPP void 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。通过定义这个宏,我们实现了这样一个功能:当按时间滚动日志时,可以自动地创建新的日志文件,并且会以滚动时间命名新建文件。不过这个功能目前并不是很完善,使用起来有以下几个限制条件:
#define ELPP_NAME_LOG_FILE_AFTER_TIME #define ELPP_NO_DEFAULT_LOG_FILE #include "easylogging++.h" INITIALIZE_EASYLOGGINGPP int 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 内容如下:
* GLOBAL: FORMAT = "[%level | %datetime] | %msg" ENABLED = true TO_FILE = true TO_STANDARD_OUTPUT = true LOG_FLUSH_THRESHOLD = 0 MILLISECONDS_WIDTH = 3 PERFORMANCE_TRACKING = false MAX_LOG_FILE_SIZE = 2097152 ## Throw log files away after 2097152 2MB / 209715200 200MB / 4398046511104 1GB LOG_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"利用上述演示代码,可以完全自动地按照每分钟的间隔创建如下格式的日志文件: