在前面的文章 《日志庫Easylogging++學習系列(1) —— 簡要介紹?》中,我們已經初步見識到了 Easylogging++ 日志庫強大的配置功能。那么配置文件中各個字段的意義是什么呢?我們應該如何編寫自己的配置文件呢?又或者說,除了配置文件之外,我們還有沒有別的方法可以完成日志的配置功能呢?希望各位有疑惑的小伙伴在看了本文的內容之后,都能夠找到自己滿意的答案!
要完成 Easylogging++ 日志的配置功能,可以通過三種方法去實現,而且每一種方法都非常簡單。第一種方法就是使用配置文件,這種方法的好處就是只要修改配置文件即可實現日志格式的重新配置,而不需要修改源程序代碼,缺點就是發布程序時必須打包配置文件一起發布,否則程序無法正常運行。在一些小項目中,特別是在只有一個EXE運行文件的程序中,如果我們不希望程序帶著一個多余的日志配置文件,那么我們可以使用另外一種方法,就是使用?el::Configurations? 類提供的成員函數,這種方法和第一種方法的優缺點剛好相反。最后一種方法就是使用?Easylogging++ 的內聯配置功能,但是并不推薦使用這種方法,因為它會顯得配置十分凌亂。
方法一:使用配置文件
在程序運行時,可以通過使用?el::Configurations? 類 加載配置文件來完成?Easylogging++ 的配置功能,配置文件必須遵循下面的語法:
[plain] ?view plaincopy
*?LEVEL:?? ??CONFIGURATION?NAME??=?"VALUE"?##?Comment?? ??CONFIGURATION?NAME??=?"VALUE"??
語法簡要說明如下:
*LEVEL,指的是日志級別,以星號符“*”開始,并以英文冒號“:”結束。 CONFIGURATION NAME ,指的是配置項的名稱,全部名稱詳見下面的表格。 "VALUE",指的是配置項的值,各個配置項對應的值類型詳見下面的表格。 ## Comment,指的是注釋,其中兩個連續井號"##"表示注釋,Comment是注釋的內容。注釋是可有可無的,但是千萬不要在注釋中使用雙引號,否則可能會出現意想不到的錯誤。
編寫配置文件時,強烈建議先寫 Global 級別的配置,這樣的好處是可以使其他任何未在配置文件中明確指出的級別都將會自動地繼承并使用 Global 級別的配置。比如,如果你想把所有級別的日志都保存在同一個文件里,你只需要 在 Global 級別中設置 Filename 就可以了,其他級別的日志將會默認的使用 Global 級別中的 Filename 。下面的表格列舉了GitHub上給出的Easylogging++在配置文件中支持的配置項:
Configuration NameTypeDescription Enabled bool Determines whether or not corresponding level for logger is enabled. You may disable all logs by usingel::Level::Global To_File bool Whether or not to write corresponding log to log file To_Standard_Output bool Whether or not to write logs to standard output e.g, terminal or command prompt Format char* Determines format/pattern of logging for corresponding level and logger. Filename char* Determines log file (full path) to write logs to for corresponding level and logger Milliseconds_Width uint Specifies milliseconds width. Width can be within range (1-6) Performance_Tracking bool Determines whether or not performance tracking is enabled. This does not depend on logger or level. Performance tracking always uses 'performance' logger unless specified Max_Log_File_Size size_t If log file size of corresponding level is >= specified size, log file will be truncated. Log_Flush_Threshold size_t Specifies number of log entries to hold until we flush pending log data
下面再回顧一下《日志庫Easylogging++學習系列(1) —— 簡要介紹?》這篇文章中給出的名稱為?my_log.conf 的 配置文件:
[plain] ?view plaincopy
*?GLOBAL:?? ????ENABLED?????????????????=???true?? ????TO_FILE?????????????????=???true?? ????TO_STANDARD_OUTPUT??????=???true?? ????FORMAT??????????????????=???"[%level?|?%datetime]?|?%msg"?? ????FILENAME????????????????=???"log\\log_%datetime{%Y%M%d}.log"?? ????MILLISECONDS_WIDTH??????=???3?? ????PERFORMANCE_TRACKING????=???false?? ????MAX_LOG_FILE_SIZE???????=???1048576?? ????LOG_FLUSH_THRESHOLD?????=???0?? ?????? *?TRACE:?? ????FILENAME????????????????=???"log\\trace_log_%datetime{%Y%M%d}.log"?? ?????? *?DEBUG:?? ????FILENAME????????????????=???"log\\debug_log_%datetime{%Y%M%d}.log"?? ?????? *?FATAL:?? ????ENABLED?????????????????=???false?? ?????? *?ERROR:?? ????FILENAME????????????????=???"log\\error_log_%datetime{%Y%M%d}.log"?? ?????? *?WARNING:?? ????FILENAME????????????????=???"log\\warning_log_%datetime{%Y%M%d}.log"?? ?????? *?INFO:?? ????FILENAME????????????????=???"log\\info_log_%datetime{%Y%M%d}.log"?? ?????? *?VERBOSE:?? ????ENABLED?????????????????=???false??
在上面的 文件中,我們以 Global 級別開始,并在 Global 級別中把 Easylogging++ 支持的所有的配置項都設置好了,這樣?Global 級別的配置就會被后續的日志級別所繼承,一直到后續的日志級別再次明確地設置配置項才會覆蓋原有的來自于?Global 級別的配置。比如這里的TRACE、DEBUG、ERROR、WARNING、INFO等級別都顯式地定義了 Filename ,所以這幾個級別的日志記錄會各自保存在相應的文件里面。而FATAL、VERBOSE這兩個級別則顯式地定義了 ENABLE ,并將其值設置為false,所以這兩個級別的日志記錄將會被禁用。下面的代碼再次演示如何使用該配置文件(注意注釋的內容):
[cpp] ?view plaincopy
#include?"easylogging++.h" ?? ?? INITIALIZE_EASYLOGGINGPP?? ?? int ?main( int ?argc,? char **?argv)?? {?? ????? ? ? ?? ????el::Loggers::addFlag(el::LoggingFlag::StrictLogFileSizeCheck);?? ?? ????el::Configurations?conf("my_log.conf" );?? ?? ?????? ????el::Loggers::reconfigureLogger("default" ,?conf);?? ?? ?????? ????el::Loggers::reconfigureAllLoggers(conf);?? ?? ????LOG(INFO)?<<?"*****?info?log??*****" ;?? ?? ????system("pause" );?? ????return ?0;?? }??
方法二:使用?el::Configurations? 類的成員函數
在?Easylogging++ 日志庫中,封裝了配置類? el::Configurations,該類提供了完成日志配置功能的全部接口,建議參考該類的源碼,可以查看和了解更多更詳細和更全面的功能接口。下面的代碼演示了幾個常用接口的用法:
[cpp] ?view plaincopy
#include?"easylogging++.h" ?? ?? INITIALIZE_EASYLOGGINGPP?? ?? int ?main( int ?argc,? const ? char **?argv)??? {?? ?????? ????el::Configurations?defaultConf;?? ????defaultConf.setToDefault();?? ????LOG(INFO)?<<?"Using?el::Configurations?class" ;?? ?????? ?????? ????defaultConf.set(el::Level::Info,?? ????????el::ConfigurationType::Format,?"%datetime?%level?%msg" );?? ?????? ????el::Loggers::reconfigureLogger("default" ,?defaultConf);?? ????LOG(INFO)?<<?"Using?el::Configurations?class" ;?? ?? ?????? ????defaultConf.setGlobally(?? ????????el::ConfigurationType::Format,?"%datetime?%msg" );?? ?????? ????el::Loggers::reconfigureLogger("default" ,?defaultConf);?? ????LOG(INFO)?<<?"Using?el::Configurations?class" ;?? ?? ????system("pause" );?? ????return ?0;?? }??
方法三:使用內聯配置功能
所謂的內聯配置功能,就是說你可以通過使用?std::string 字符串來完成日志的配置功能,但是要注意在?std::string 字符串中加上換行符保證字符串的格式遵循配置文件中的格式。比如,如果要使用內聯配置功能實現和方法二中一樣的輸出格式,那么實現代碼必須像下面這樣:
[cpp] view plaincopy print?
#include?"easylogging++.h" ???? INITIALIZE_EASYLOGGINGPP?? ?? int ?main(int ?argc,?const ?char **?argv)???{?? ?????? ????el::Configurations?defaultConf;?? ????defaultConf.setToDefault();?? ????LOG(INFO)?<<?"Using?inline?configuration" ;?? ?????? ?????? ????defaultConf.parseFromText("*INFO:\n?FORMAT?=?%datetime?%level?%msg" );?? ?????? ????el::Loggers::reconfigureLogger("default" ,?defaultConf);?? ????LOG(INFO)?<<?"Using?inline?configuration" ;?? ?? ?????? ????defaultConf.parseFromText("*INFO:\n?FORMAT?=?%datetime?%msg" );?? ?????? ????el::Loggers::reconfigureLogger("default" ,?defaultConf);?? ????LOG(INFO)?<<?"Using?inline?configuration" ;?? ?? ????system("pause" );?? ????return ?0;?? }?? #include "easylogging++.h"INITIALIZE_EASYLOGGINGPPint main(int argc, const char** argv)
{/// 使用默認配置el::Configurations defaultConf;defaultConf.setToDefault();LOG(INFO) << "Using inline configuration";/// 重新設置INFO級別的配置項FORMAT的值defaultConf.parseFromText("*INFO:\n FORMAT = %datetime %level %msg");/// 重新設置配置el::Loggers::reconfigureLogger("default", defaultConf);LOG(INFO) << "Using inline configuration";// 重新設置GLOBAL級別的配置項FORMAT的值defaultConf.parseFromText("*INFO:\n FORMAT = %datetime %msg");/// 重新設置配置el::Loggers::reconfigureLogger("default", defaultConf);LOG(INFO) << "Using inline configuration";system("pause");return 0;
}仔細看上面的代碼,注意每一次調用函數parseFromText()的參數中都包含了一個換行符 “\n”。假如沒有這個換行符,就不能正確的解析出配置項的內容,從而會導致配置無效。由于這個原因,當需要設置的配置項很多的時候,如果還使用內聯配置功能完成日志的配置,就會使得代碼變得凌亂復雜,而且很容易造成錯誤,所以盡量不要使用這種方法或者避免使用這種方法。
默認配置功能
如果你希望為現有的或者將來新建的日志記錄器設置一個默認的配置,你可以使用下面的這個函數:
[cpp] view plaincopy print?
el::Loggers::setDefaultConfigurations(el::Configurations&?configurations,?bool ?configureExistingLoggers?=?false )?? el::Loggers::setDefaultConfigurations(el::Configurations& configurations, bool configureExistingLoggers = false)當你在編寫大型程序或者調用同樣使用 Easylogging++ 的第三方庫時,默認配置功能顯得十分有用。
這個功能使得后續 任何新建的日志記錄器,都將會使用默認配置;如果你希望現有的日志記錄器也使用默認配置,只需把函數的第二個參數設置為 true 即可。下面的代碼演示了默認配置功能:
[cpp] view plaincopy print?
#include?"easylogging++.h" ???? INITIALIZE_EASYLOGGINGPP?? ?? int ?main(void )??{?? ????el::Configurations?defaultConf;?? ????defaultConf.setGlobally(el::ConfigurationType::Format,?"[%logger]?%level:?%msg" );?? ?? ?????? ????el::Loggers::setDefaultConfigurations(defaultConf);?? ????LOG(INFO)?<<?"Set?default?configuration?but?existing?loggers?not?updated?yet" ;?? ?? ?????? ????el::Loggers::getLogger("testDefaultConf1" );?? ????CLOG(INFO,?"testDefaultConf1" )?<<?"Logging?using?new?logger?1" ;?? ?? ?????? ????el::Loggers::setDefaultConfigurations(defaultConf,?true );?? ????LOG(INFO)?<<?"Existing?loggers?updated?as?well" ;?? ?? ?????? ????el::Loggers::getLogger("testDefaultConf2" );?? ????CLOG(INFO,?"testDefaultConf2" )?<<?"Logging?using?new?logger?2" ;?? ?? ????system("pause" );?? ????return ?0;?? }?? #include "easylogging++.h"INITIALIZE_EASYLOGGINGPPint main(void)
{el::Configurations defaultConf;defaultConf.setGlobally(el::ConfigurationType::Format, "[%logger] %level: %msg");/// 只為新建的日志記錄器設置默認配置el::Loggers::setDefaultConfigurations(defaultConf);LOG(INFO) << "Set default configuration but existing loggers not updated yet";/// 新建日志記錄器 testDefaultConf1el::Loggers::getLogger("testDefaultConf1");CLOG(INFO, "testDefaultConf1") << "Logging using new logger 1";// 為現有的日志記錄器設置默認配置el::Loggers::setDefaultConfigurations(defaultConf, true);LOG(INFO) << "Existing loggers updated as well";/// 新建日志記錄器 testDefaultConf2el::Loggers::getLogger("testDefaultConf2");CLOG(INFO, "testDefaultConf2") << "Logging using new logger 2";system("pause");return 0;
}
全局配置功能
全局配置并不是指 Global 級別,而是指利用全局配置文件為全部或部分,甚至是為新建的日志記錄器注冊配置。全局配置文件必須遵循下面的語法:
[plain] view plaincopy print?
--?LOGGER?ID?##?Case?sensitive?? ??##?Everything?else?is?same?as?configuration?file?? ?? --?ANOTHER?LOGGER?ID?? ??##?Configuration?for?this?logger?? -- LOGGER ID ## Case sensitive## Everything else is same as configuration file-- ANOTHER LOGGER ID## Configuration for this logger
語法簡要說明如下:
LOGGER ID ,指的是記錄器 ID,大小寫敏感,并以兩個破折號開始。 其余部分的語法規則和本文中方法一的配置文件的語法規則完全一樣。 一旦你編寫好了全局配置文件,你僅需使用一個函數就可以完成你的日志記錄器的配置,甚至是注冊一個全新的日志記錄器。需要注意的是,你不能使用空白的配置來注冊新的日志記錄器,也就是說在?LOGGER ID 下面,你至少得定義一個配置項。下面的代碼演示了全局配置功能: [cpp] view plaincopy print?
#include?"easylogging++.h" ???? INITIALIZE_EASYLOGGINGPP?? ?? int ?main(void )???{?? ????LOG(INFO)?<<?"Info?log?before?using?global?configuration" ;?? ?? ?????? ????el::Loggers::configureFromGlobal("global.conf" );?? ?? ????LOG(INFO)?<<?"Info?log?AFTER?using?global?configuration" ;?? ????LOG(ERROR)?<<?"Error?log?AFTER?using?global?configuration" ;?? ?? ?????? ????CLOG(TRACE,?"testGlobalConf" )?<<?"TRACE?Logging?using?new?logger" ;?? ????CLOG(DEBUG,?"testGlobalConf" )?<<?"DEBUG?Logging?using?new?logger" ;?? ????CLOG(WARNING,?"testGlobalConf" )?<<?"WARNING?Logging?using?new?logger" ;?? ????CLOG(ERROR,?"testGlobalConf" )?<<?"ERROR?Logging?using?new?logger" ;?? ????CLOG(INFO,?"testGlobalConf" )?<<?"INFO?Logging?using?new?logger" ;?? ?? ????system("pause" );?? ????return ?0;?? }?? #include "easylogging++.h"INITIALIZE_EASYLOGGINGPPint main(void)
{LOG(INFO) << "Info log before using global configuration";/// 只需一個函數即可實現全局配置功能el::Loggers::configureFromGlobal("global.conf");LOG(INFO) << "Info log AFTER using global configuration";LOG(ERROR) << "Error log AFTER using global configuration";/// 用全局配置文件中新建的日志記錄器 testGlobalConf 來記錄日志CLOG(TRACE, "testGlobalConf") << "TRACE Logging using new logger";CLOG(DEBUG, "testGlobalConf") << "DEBUG Logging using new logger";CLOG(WARNING, "testGlobalConf") << "WARNING Logging using new logger";CLOG(ERROR, "testGlobalConf") << "ERROR Logging using new logger";CLOG(INFO, "testGlobalConf") << "INFO Logging using new logger";system("pause");return 0;
} 其中全局配置文件 global.conf 的內容如下:
[plain] view plaincopy print?
--?default??? ???*INFO:?? ??????FORMAT???=?"%level?%msg"?? ??????FILENAME?=?"/tmp/logs/wow.log"?? ???*ERROR:?? ??????FORMAT???=?"%levshort?%fbase:%line?%msg"?? ?? ???????? --?testGlobalConf?? ???*GLOBAL:?? ??????FORMAT??????????????????=???"[%level?|?%datetime]?|?%msg"?? ??????ENABLED?????????????????=???true?? ??????TO_FILE?????????????????=???true?? ??????TO_STANDARD_OUTPUT??????=???true?? ??????MILLISECONDS_WIDTH??????=???3?? -- default *INFO:FORMAT = "%level %msg"FILENAME = "/tmp/logs/wow.log"*ERROR:FORMAT = "%levshort %fbase:%line %msg"-- testGlobalConf*GLOBAL:FORMAT = "[%level | %datetime] | %msg"ENABLED = trueTO_FILE = trueTO_STANDARD_OUTPUT = trueMILLISECONDS_WIDTH = 3
讀取配置 在某些情況下,如果我們想要獲取某一個日志記錄器的當前配置, 可以通過使用 el::Loggers 類的成員函數?typedConfigurations()來實現 ,建議參考該類的源碼,可以查看和了解更多更詳細和更全面的功能接口, 下面的代碼片段演示了該功能: [cpp] view plaincopy print?
el::Logger*?defaultLogger?=?el::Loggers::getLogger("default" );?? ?? bool ?enabled?=?defaultLogger->typedConfigurations()->enabled(el::Level::Info);???? std::string?format?=??defaultLogger->typedConfigurations()->logFormat(el::Level::Info).format();?? el::Logger* defaultLogger = el::Loggers::getLogger("default");
/// 記錄器default是否被禁用
bool enabled = defaultLogger->typedConfigurations()->enabled(el::Level::Info);
/// 記錄器default的INFO級別的日志輸出格式
std::string format = defaultLogger->typedConfigurations()->logFormat(el::Level::Info).format();
學習配置日志是掌握 Easylogging++ 使用的關鍵,通過靈活地運用配置功能,才能更好地將?Easylogging++ 的強悍之處應用到我們的程序開發當中。在本文列舉了幾個比較通用的配置方法,然而,在某些應用場景下,我們還需要通過其他的一些配置手段來輔助我們完成某些特殊功能,這些輔助配置手段包括設置命令行參數、設置日志標記、配置宏定義等,限于篇幅,后面的文章再對這些內容的學習過程逐一進行記錄。
總結
以上是生活随笔 為你收集整理的日志库EasyLogging++学习系列(3)—— 配置功能 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。