生活随笔
收集整理的這篇文章主要介紹了
日志库EasyLogging++学习系列(10)—— 日志文件滚动
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
在很多應(yīng)用場(chǎng)合,我們是需要實(shí)現(xiàn)日志文件滾動(dòng)的,特別是在一些長(zhǎng)期運(yùn)行的服務(wù)器程序中,如果把所有的日志都記錄在一個(gè)文件之中,勢(shì)必會(huì)造成日志文件越來越大。當(dāng)日志內(nèi)容很多的時(shí)候,萬一哪天突然需要查詢某個(gè)日志信息就會(huì)顯得十分不便。所以,支持日志文件滾動(dòng)是很多日志庫都支持的功能,而文件滾動(dòng)又可以分為按大小滾動(dòng)和按時(shí)間滾動(dòng)。
按大小滾動(dòng)文件
在 Easylogging++ 中,已經(jīng)實(shí)現(xiàn)了按照日志文件大小來滾動(dòng)日志記錄。在前面《日志庫EasyLogging++學(xué)習(xí)系列(3)—— 配置功能》一文中介紹配置文件時(shí),有一個(gè)配置項(xiàng):MAX_LOG_FILE_SIZE,這個(gè)配置項(xiàng)的值(以字節(jié)為單位)表示的就是日志文件的最大大小。一旦日志文件的大小達(dá)到這個(gè)配置項(xiàng)設(shè)置的值,日志文件就會(huì)自動(dòng)清空文件中所有的日志記錄,并重新開始寫入。不過配置項(xiàng)?MAX_LOG_FILE_SIZE?在默認(rèn)情況下是不生效的,需要設(shè)置標(biāo)記:LoggingFlag::StrictLogFileSizeCheck 來激活。另外,如果我們想要保留之前的日志記錄,那么我們可以注冊(cè)一個(gè)回調(diào)函數(shù),這個(gè)回調(diào)函數(shù)將會(huì)允許我們?cè)谇蹇杖罩疚募皩?duì)日志文件進(jìn)行一次處理。下面的代碼演示了按大小滾動(dòng)日志文件,并通過回調(diào)函數(shù)保留了所有的日志記錄:
[cpp] view plaincopy print?
#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;??}??
#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");/// 注冊(cè)回調(diào)函數(shù)el::Helpers::installPreRollOutCallback(rolloutHandler);for (int i = 0; i < 100; ++i){LOG(INFO) << "Test";}/// 注銷回調(diào)函數(shù)el::Helpers::uninstallPreRollOutCallback();return 0;
}
通過配置文件來設(shè)置
配置項(xiàng)?
MAX_LOG_FILE_SIZE?的大小也可以實(shí)現(xiàn)上述演示代碼的效果,另外我們還可以設(shè)置不同級(jí)別的日志文件按照不同的文件大小來滾動(dòng)。如果不小心忘記了設(shè)置標(biāo)記:LoggingFlag::StrictLogFileSizeCheck ,我們還可以通過調(diào)用函數(shù)?el::Helpers::validateFileRolling(el::Logger*, const el::Level&) 以手動(dòng)的方式來檢查日志滾動(dòng),建議各位小伙伴可以自己嘗試一下。
按時(shí)間滾動(dòng)文件
在 Easylogging++ 中是沒有實(shí)現(xiàn)按時(shí)間滾動(dòng)日志文件的,不過既然是開源的日志庫,我們可以參考著按大小滾動(dòng)日志文件的實(shí)現(xiàn)方式,根據(jù)自己的需求去實(shí)現(xiàn)一個(gè)按時(shí)間滾動(dòng)日志文件的功能。下面簡(jiǎn)單地說明一下實(shí)現(xiàn)步驟:
- 在按大小滾動(dòng)日志文件中有配置項(xiàng)?MAX_LOG_FILE_SIZE,所以我們也增加一個(gè)配置項(xiàng)?LOG_FILE_ROLLING_TIME ,新增配置項(xiàng)的值類型為 char* 型,其值只能是以下四個(gè):"MONTH" 、"DAY"、"HOUR"、"MINUTE",其中"MONTH"表示按月份滾動(dòng)日志文件,"DAY"表示按天數(shù)滾動(dòng)日志文件,"HOUR"表示按小時(shí)滾動(dòng)日志文件,"MINUTE"表示按分鐘滾動(dòng)日志文件。
- 在按大小滾動(dòng)日志文件中有標(biāo)記?LoggingFlag::StrictLogFileSizeCheck 來激活滾動(dòng)功能,所以我們?cè)趯?shí)現(xiàn)按時(shí)間滾動(dòng)日志文件的功能中也增加一個(gè)標(biāo)記?LoggingFlag::StrictLogFileTimeCheck 來激活滾動(dòng)功能。
- 在按大小滾動(dòng)日志文件中,允許我們?cè)谇蹇瘴募匦聦懭胫巴ㄟ^回調(diào)函數(shù)對(duì)日志文件進(jìn)行處理,所以我們?cè)?span style="font-family:'Microsoft YaHei'; font-size:14px; line-height:21.76px; background-color:rgb(248,248,248)">按時(shí)間滾動(dòng)日志文件的功能實(shí)現(xiàn)中,也同樣保留該回調(diào)函數(shù)的功能,但是在回調(diào)函數(shù)中增加了一個(gè)參數(shù),用來區(qū)分是按大小滾動(dòng)日志文件還是按時(shí)間滾動(dòng)日志文件。
- 本文的最后提供了實(shí)現(xiàn)按時(shí)間滾動(dòng)日志文件功能的Easylogging++?源碼,實(shí)現(xiàn)的細(xì)節(jié)可在源碼中搜索“modify by Fish”來查看。 因本功能目前只限于本人在使用,如有錯(cuò)誤,歡迎指正。
下面的代碼演示了如何使用新增的按時(shí)間滾動(dòng)日志文件的功能:
[cpp] view plaincopy print?
#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;??}?? #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:/// 按大小滾動(dòng)日志文件break;case el::base::RollingLogFileBasis::RollLog_DateTime:/// 按時(shí)間滾動(dòng)日志文件{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"); /// 按分鐘滾動(dòng)日志文件/// 注冊(cè)回調(diào)函數(shù)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);}/// 注銷回調(diào)函數(shù)el::Helpers::uninstallPreRollOutCallback();return 0;
}
特別提醒:
- 因?yàn)樵谛略霭磿r(shí)間滾動(dòng)日志文件的功能中修改了回調(diào)函數(shù),所以如果使用按大小滾動(dòng)日志文件功能,也需要使用修改后的回調(diào)函數(shù)。
- 因?yàn)橹挥性谟腥罩緦懭氲臅r(shí)候才判斷是否需要更新文件,所以如果無日志記錄,日志文件是無法按時(shí)間滾動(dòng)的。
在實(shí)際應(yīng)用中,如果日志按時(shí)間滾動(dòng),我們的日志文件基本上都會(huì)以時(shí)間來命名,所以為了更加方便地使用,我們可以在實(shí)現(xiàn)了按時(shí)間滾動(dòng)功能的代碼上再增加一個(gè)宏定義ELPP_NAME_LOG_FILE_AFTER_TIME。通過定義這個(gè)宏,我們實(shí)現(xiàn)了這樣一個(gè)功能:當(dāng)按時(shí)間滾動(dòng)日志時(shí),可以自動(dòng)地創(chuàng)建新的日志文件,并且會(huì)以滾動(dòng)時(shí)間命名新建文件。不過這個(gè)功能目前并不是很完善,使用起來有以下幾個(gè)限制條件:
- 不同級(jí)別的日志必須保存在不同的日志文件中,否則無法實(shí)現(xiàn)日志滾動(dòng)。
- 按月份滾動(dòng)的日志文件名中日期格式須配置:%datetime{%Y%M},如FILENAME = "log\\test_%datetime{%Y%M}.log"。
- 按天數(shù)滾動(dòng)的日志文件名中日期格式須配置:%datetime{%Y%M%d},如FILENAME = "log\\test_%datetime{%Y%M%d}.log"。
- 按小時(shí)滾動(dòng)的日志文件名中日期格式須配置:%datetime{%Y%M%d%H},如FILENAME = "log\\test_%datetime{%Y%M%d%H}.log"。
- 按分鐘滾動(dòng)的日志文件名中日期格式須配置:%datetime{%Y%M%d%H%m},如FILENAM="log\\test_%datetime{%Y%M%d%H%m}.log"。
雖然使用這個(gè)功能有些限制條件,但是這些條件基本符合我平時(shí)的使用習(xí)慣,因?yàn)椴煌?jí)別的日志在實(shí)際應(yīng)用中我肯定是會(huì)保存在不同的文件中,而且文件名中的日期格式也和滾動(dòng)的時(shí)間間隔一致,所以我也就沒有去完善這個(gè)功能。下面通過配置文件的方式演示了這個(gè)功能:
[cpp] view plaincopy print?
#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;??}?? #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 內(nèi)容如下:
[plain] view plaincopy print?
*?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"?? * 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"利用上述演示代碼,可以完全自動(dòng)地按照每分鐘的間隔創(chuàng)建如下格式的日志文件:
按時(shí)間滾動(dòng)日志文件之所以寫了這么多,最主要的原因就是為了說明在開源的日志庫中,我們可以完全自主地按照自己的想法來實(shí)現(xiàn)一些符合自己需求的功能。比如上面介紹的宏定義ELPP_NAME_LOG_FILE_AFTER_TIME功能,雖然還不完善,但是只要嚴(yán)格按照限制條件來使用,完全可以達(dá)到我們想要的效果。對(duì)于開源代碼,能夠直接使用還并不是我們最終的目的,能夠在開源的基礎(chǔ)上加以修改完善并應(yīng)用于實(shí)際編程當(dāng)中才是我們學(xué)習(xí)開源代碼的初衷。
猛戳這里,下載源碼!
總結(jié)
以上是生活随笔為你收集整理的日志库EasyLogging++学习系列(10)—— 日志文件滚动的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。