生活随笔
收集整理的這篇文章主要介紹了
日志库EasyLogging++学习系列(10)—— 日志文件滚动
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
在很多應用場合,我們是需要實現日志文件滾動的,特別是在一些長期運行的服務器程序中,如果把所有的日志都記錄在一個文件之中,勢必會造成日志文件越來越大。當日志內容很多的時候,萬一哪天突然需要查詢某個日志信息就會顯得十分不便。所以,支持日志文件滾動是很多日志庫都支持的功能,而文件滾動又可以分為按大小滾動和按時間滾動。
按大小滾動文件
在 Easylogging++ 中,已經實現了按照日志文件大小來滾動日志記錄。在前面《日志庫EasyLogging++學習系列(3)—— 配置功能》一文中介紹配置文件時,有一個配置項:MAX_LOG_FILE_SIZE,這個配置項的值(以字節為單位)表示的就是日志文件的最大大小。一旦日志文件的大小達到這個配置項設置的值,日志文件就會自動清空文件中所有的日志記錄,并重新開始寫入。不過配置項?MAX_LOG_FILE_SIZE?在默認情況下是不生效的,需要設置標記:LoggingFlag::StrictLogFileSizeCheck 來激活。另外,如果我們想要保留之前的日志記錄,那么我們可以注冊一個回調函數,這個回調函數將會允許我們在清空日志文件之前對日志文件進行一次處理。下面的代碼演示了按大小滾動日志文件,并通過回調函數保留了所有的日志記錄:
[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");/// 注冊回調函數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?
#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:/// 按大小滾動日志文件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?
#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 內容如下:
[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"利用上述演示代碼,可以完全自動地按照每分鐘的間隔創建如下格式的日志文件:
按時間滾動日志文件之所以寫了這么多,最主要的原因就是為了說明在開源的日志庫中,我們可以完全自主地按照自己的想法來實現一些符合自己需求的功能。比如上面介紹的宏定義ELPP_NAME_LOG_FILE_AFTER_TIME功能,雖然還不完善,但是只要嚴格按照限制條件來使用,完全可以達到我們想要的效果。對于開源代碼,能夠直接使用還并不是我們最終的目的,能夠在開源的基礎上加以修改完善并應用于實際編程當中才是我們學習開源代碼的初衷。
猛戳這里,下載源碼!
總結
以上是生活随笔為你收集整理的日志库EasyLogging++学习系列(10)—— 日志文件滚动的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。