轉載自:
http://blog.csdn.net/Fish_55_66/article/details/49471873
在前面所列文章的演示代碼中,其實已經展示了一部分記錄日志的方式。為了使用方便,在 Easylogging++ 中,通過使用宏的集合來完成日志記錄。
普通日志記錄
對於普通的日志記錄,我們可以選擇以下兩種比較常用的方式:
LOG(LEVEL)
CLOG(LEVEL, logger ID)
兩個宏定義的中 LEVEL 請參看《日志庫EasyLogging++學習系列(2)—— 日志級別》,而宏CLOG(CUSTOM LOG)中的 logger ID 可以參看《日志庫EasyLogging++學習系列(6)—— 日志記錄器》。下面的代碼簡單演示了如何使用這兩個宏:
- #include "easylogging++.h"
- INITIALIZE_EASYLOGGINGPP
- int main(int argc, char** argv)
- {
- /// 可以直接使用,不記錄任何日志信息
- LOG(INFO);
- CLOG(INFO, "default");
- LOG(INFO) << "Here is very simple example.";
- CLOG(INFO, "default") << "Here is very simple example.";
- system("pause");
- return 0;
- }
通過上面的演示代碼可以發現,其實 LOG(LEVEL) 就是 CLOG(LEVEL, "default") 的縮寫。不過,如果我們在包含頭文件 #include "easylogging++.h" 的代碼前面使用另外一個指定的 ID 來定義宏 ELPP_DEFAULT_LOGGER ,那么這個指定的 ID 就會自動地替換掉 "default" 。需要注意的是,這個指定 ID 所標識的日志記錄器必須保證使用前已經被注冊,否則,將無法正常使用宏 LOG(LEVEL) 來記錄日志信息。下面的代碼演示這個功能:
- #define ELPP_DEFAULT_LOGGER "testlog"
- #include "easylogging++.h"
- INITIALIZE_EASYLOGGINGPP
- int main(int argc, char** argv)
- {
- /// 使用前,注冊ID為testlog的日志記錄器
- el::Logger* newLogger = el::Loggers::getLogger("testlog");
- LOG(INFO) << "Here is very simple example.";
- system("pause");
- return 0;
- }
條件日志記錄
條件日志就是只有當滿足某一個條件的時候才進行日志記錄,否則將忽略記錄。下面是記錄條件日志的兩個宏定義:
LOG_IF(condition, LEVEL)
CLOG_IF(condition, LEVEL, logger ID)
上面兩個宏定義中 condition 條件為真時,日志信息才被記錄,在某些應用場景下這會顯得十分便利,下面的代碼演示了條件日志宏定義的用法:
- #include "easylogging++.h"
- INITIALIZE_EASYLOGGINGPP
- int main(int argc, char** argv)
- {
- /// 下面這三個日志,只有第一個會輸出
- LOG_IF(1 == 1, INFO) << "1 is equal to 1";
- LOG_IF(1 > 2, INFO) << "1 is greater than 2";
- LOG_IF(1 == 2, DEBUG) << "1 is equal to 2";
- system("pause");
- return 0;
- }
偶然日志記錄
偶然日志可以分為以下三種常見的情況,具體使用方法請參考示例代碼:
- 每 N 次記錄一次日志,對應的宏定義是:LOG_EVERY_N(n, LEVEL) 或者 CLOG_EVERY_N(n, LEVEL, logger ID)
- 當計數達到 N 次之后,才開始記錄日志,對應的宏定義是:LOG_AFTER_N(n, LEVEL)
- 當記錄次數達到 N 次之后,就不再記錄日志信息,對應的宏定義是:LOG_N_TIMES(n, LEVEL)
- #include "easylogging++.h"
- INITIALIZE_EASYLOGGINGPP
- int main(int argc, char** argv)
- {
- /// 每 N 次記錄一次日志
- for (int i = 1; i <= 200; ++i)
- {
- LOG_EVERY_N(20, INFO) << "LOG_EVERY_N i = " << i;
- LOG_EVERY_N(100, INFO) << "LOG_EVERY_N Current position is " << ELPP_COUNTER_POS;
- }
- /// 當計數達到 N 次之后,才開始記錄日志
- for (int i = 1; i <= 10; ++i)
- {
- LOG_AFTER_N(6, INFO) << "LOG_AFTER_N i = " << i;
- }
- /// 當記錄次數達到 N 次之后,就不再記錄
- for (int i = 1; i < 10; ++i)
- {
- LOG_N_TIMES(6, INFO) << "LOG_N_TIMES i = " << i;
- }
- system("pause");
- return 0;
- }
STL容器日志記錄
在前面的文章《日志庫EasyLogging++學習系列(5)—— 輔助配置功能》中,已經給過示例演示了如何記錄 STL 容器日志的方式。再次提示,使用 STL容器日志記錄需要定義宏 ELPP_STL_LOGGING,每個容器默認最大容量是 100。下面是 Easylogging++ 支持的 STL 容器類型:
* | * | * | * |
---|---|---|---|
std::vector | std::list | std::deque | std::queue |
std::stack | std::priority_queue | std::set | std::multiset |
std::pair | std::bitset | std::map | std::multimap |
下面的是 C++11 標准才支持的 STL 容器,在 Easylogging++ V9.80版本中定義相應的宏就可以使用,別忘記同樣還需要定義宏 ELPP_STL_LOGGING:
Template | Macro Needed |
---|---|
std::array | ELPP_LOG_STD_ARRAY |
std::unordered_map | ELPP_LOG_UNORDERED_MAP |
std::unordered_multimap | ELPP_LOG_UNORDERED_MAP |
std::unordered_set | ELPP_LOG_UNORDERED_SET |
std::unordered_multiset | ELPP_LOG_UNORDERED_SET |
系統日志記錄
系統日志需要系統具有頭文件 syslog.h 的支持,這個頭文件在 Linux 系統下才有,也就是說Windows 系統是不支持 Easylogging++ 的系統日志功能的。因為我並沒有在 Linux下用過 Easylogging++,在這里只簡單介紹一下。如果想要使用系統日志功能,必須先定義宏ELPP_SYSLOG 的,系統日志默認使用 ID 為 “syslog” 的日志記錄器,可以用以下的宏記錄日志:
- SYSLOG(LEVEL)
- SYSLOG_IF(Condition, LEVEL)
- SYSLOG_EVERY_N(n, LEVEL)
- CSYSLOG(LEVEL, loggerId)
- CSYSLOG_IF(Condition, LEVEL, loggerId)
- CSYSLOG_EVERY_N(n, LEVEL, loggerId)
- INFO (LOG_INFO)
- DEBUG (LOG_DEBUG)
- WARNING (LOG_WARNING)
- ERROR (LOG_ERR)
- FATAL (LOG_EMERG)
CHECK Name | Notes + Example |
---|---|
CHECK(condition) |
Checks for condition e.g, CHECK(isLoggedIn()) << "Not logged in"; |
CHECK_EQ(a, b) |
Equality check e.g, CHECK_EQ(getId(), getLoggedOnId()) << "Invalid user logged in"; |
CHECK_NE(a, b) |
Inequality check e.g, CHECK_NE(isUserBlocked(userId), false) << "User is blocked"; |
CHECK_LT(a, b) |
Less than e.g, CHECK_LT(1, 2) << "How 1 is not less than 2"; |
CHECK_GT(a, b) |
Greater than e.g, CHECK_GT(2, 1) << "How 2 is not greater than 1?"; |
CHECK_LE(a, b) |
Less than or equal e.g, CHECK_LE(1, 1) << "1 is not equal or less than 1"; |
CHECK_GE(a, b) |
Greater than or equal e.g, CHECK_GE(1, 1) << "1 is not equal or greater than 1"; |
CHECK_NOTNULL(pointer) |
Ensures pointer is not null - if OK returns pointer e.g, explicit MyClass(Obj* obj) : m_obj(CHECK_NOT_NULL(obj)) {} |
CHECK_STREQ(str1, str2) |
C-string equality (case-sensitive) e.g, CHECK_STREQ(argv[1], "0") << "First arg cannot be 0"; |
CHECK_STRNE(str1, str2) |
C-string inequality (case-sensitive) e.g, CHECK_STRNE(username1, username2) << "Usernames cannot be same"; |
CHECK_STRCASEEQ(str1, str2) |
C-string inequality (case-insensitive) e.g, CHECK_CASESTREQ(argv[1], "Z") << "First arg cannot be 'z' or 'Z'"; |
CHECK_STRCASENE(str1, str2) |
C-string inequality (case-insensitive) e.g,CHECK_STRCASENE(username1, username2) << "Same username not allowed"; |
CHECK_BOUNDS(val, min, max) |
Checks that val falls under the min and max range e.g,CHECK_BOUNDS(i, 0, list.size() - 1) << "Index out of bounds"; |
- #include "easylogging++.h"
- INITIALIZE_EASYLOGGINGPP
- int main(void)
- {
- el::Loggers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog);
- /// CHECK(condition)
- CHECK(1 < 2) << "CHECK(condition) example 1"; // condition為真時不記錄
- CHECK(1 > 2) << "CHECK(condition) example 2"; // condition為假時才記錄
- /// CHECK_EQ(a, b)
- CHECK_EQ(1, 1) << "CHECK_EQ(a, b) example 1"; // 滿足a = b時不記錄
- CHECK_EQ(1, 0) << "CHECK_EQ(a, b) example 3"; // 不滿足a = b時記錄
- /// CHECK_NE(a, b)
- CHECK_NE(1, 2) << "CHECK_NE(a, b) example 1"; // 滿足a != b 時不記錄
- CHECK_NE(1, 1) << "CHECK_NE(a, b) example 2"; // 不滿足a != b 時記錄
- /// CHECK_LT(a, b)
- CHECK_LT(1, 2) << "CHECK_LT(a, b) example 1"; // 滿足a < b時不記錄
- CHECK_LT(1, 1) << "CHECK_LT(a, b) example 2"; // 不滿足a < b時記錄
- /// CHECK_GT(a, b)
- CHECK_GT(2, 1) << "CHECK_GT(a, b) example 1"; // 滿足a > b時不記錄
- CHECK_GT(1, 1) << "CHECK_GT(a, b) example 2"; // 不滿足a > b時記錄
- /// CHECK_LE(a, b)
- CHECK_LE(1, 1) << "CHECK_LE(a, b) example 1"; // 滿足a <= b時不記錄
- CHECK_LE(1, 0) << "CHECK_LE(a, b) example 2"; // 不滿足a <= b時記錄
- /// CHECK_GE(a, b)
- CHECK_GE(1, 1) << "CHECK_GE(a, b) example 1"; // 滿足a >= b時不記錄
- CHECK_GE(1, 2) << "CHECK_GE(a, b) example 2"; // 不滿足a >= b時記錄
- /// CHECK_STREQ(str1, str2) C風格字符串區分大小寫
- CHECK_STREQ("abc", "abc") << "CHECK_STREQ(str1, str2) example 1"; // 滿足str1 = str2時不記錄
- CHECK_STREQ("abc", "ABC") << "CHECK_STREQ(str1, str2) example 2"; // 不滿足str1 = str2時記錄
- /// CHECK_STRNE(str1, str2) C風格字符串區分大小寫
- CHECK_STRNE("abc", "ABC") << "CHECK_STRNE(str1, str2) example 1"; // 滿足str1 != str2時不記錄
- CHECK_STRNE("abc", "abc") << "CHECK_STRNE(str1, str2) example 2"; // 不滿足str1 != str2時記錄
- /// CHECK_STRCASEEQ(str1, str2) C風格字符串不區分大小寫
- CHECK_STRCASEEQ("abc", "ABC") << "CHECK_STRCASEEQ(str1, str2) example 1"; // 滿足str1 = str2時不記錄
- CHECK_STRCASEEQ("abc", "abd") << "CHECK_STRCASEEQ(str1, str2) example 2"; // 不滿足str1 = str2時記錄
- /// CHECK_STRCASENE(str1, str2) C風格字符串不區分大小寫
- CHECK_STRCASENE("abc", "abd") << "CHECK_STRCASENE(str1, str2) example 1"; // 滿足str1 != str2時不記錄
- CHECK_STRCASENE("abc", "ABC") << "CHECK_STRCASENE(str1, str2) example 2"; // 不滿足str1 != str2時記錄
- /// CHECK_NOTNULL(pointer) 判斷指針pointer是否為空,不為空時返回pointer
- int* f = nullptr;
- if (CHECK_NOTNULL(f))
- {
- }
- f = new int;
- if (CHECK_NOTNULL(f))
- {
- }
- delete f;
- f = nullptr;
- int min = 2;
- int max = 5;
- CHECK_BOUNDS(1, min, max) << "CHECK_BOUNDS(val, min, max) example 1";
- CHECK_BOUNDS(2, min, max) << "CHECK_BOUNDS(val, min, max) example 2";
- CHECK_BOUNDS(3, min, max) << "CHECK_BOUNDS(val, min, max) example 3";
- CHECK_BOUNDS(4, min, max) << "CHECK_BOUNDS(val, min, max) example 4";
- CHECK_BOUNDS(5, min, max) << "CHECK_BOUNDS(val, min, max) example 5";
- CHECK_BOUNDS(6, min, max) << "CHECK_BOUNDS(val, min, max) example 6";
- system("pause");
- return 0;
- }
- info(const char*, const T&, const Args&...)
- warn(const char*, const T&, const Args&...)
- error(const char*, const T&, const Args&...)
- debug(const char*, const T&, const Args&...)
- fatal(const char*, const T&, const Args&...)
- trace(const char*, const T&, const Args&...)
- verbose(int vlevel, const char*, const T&, const Args&...)
- #define ELPP_STL_LOGGING
- #include "easylogging++.h"
- INITIALIZE_EASYLOGGINGPP
- int main(int argc, char** argv)
- {
- el::Logger* defaultLogger = el::Loggers::getLogger("default");
- std::vector<int> i;
- i.push_back(1);
- i.push_back(2);
- /// 記錄STL容器數據
- defaultLogger->warn("My first ultimate log message %v %v %v", 123, 222, i);
- // 利用轉義字符輸出 % 和 %v
- defaultLogger->info("My first ultimate log message %% %%v %v %v", 123, 222);
- system("pause");
- return 0;
- }
perror風格日志記錄
在C語言庫函數中有個errno變量,每個errno值對應着以字符串表示的錯誤類型。當你調用某些庫函數出錯時,被調用的函數會重新設置了errno的值,而 perror() 函數就是用於將你輸入的一些信息和現在的errno所對應的錯誤一起輸出。在 Easylogging++ 中,也支持在作用上類似 perror() 函數的宏定義:
PLOG(LEVEL)
PLOG_IF(Condition,
LEVEL)PCHECK()
CPLOG(LEVEL, LoggerId)
CPLOG_IF(Condition, LEVEL, LoggerId)
下面的代碼演示了 perror 風格日志記錄的使用方式:
- #include "easylogging++.h"
- INITIALIZE_EASYLOGGINGPP
- int main(void)
- {
- el::Loggers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog);
- /// 讀取一個不存在的文件,改變errno變量的值
- std::fstream f("a file that does not exist", std::ifstream::in);
- /// PLOG(LEVEL)
- PLOG(INFO) << "A message with plog";
- /// PLOG_IF(Condition, LEVEL), 條件為真時才記錄
- PLOG_IF(true, INFO) << "A message with plog 1";
- PLOG_IF(false, INFO) << "A message with plog 2";
- /// PCHECK() 滿足條件不記錄,不滿足才記錄
- PCHECK(true) << "check message with plog 1";
- PCHECK(false) << "check message with plog 2";
- system("pause");
- return 0;
- }
DEBUG模式日志記錄
在前面的文章《日志庫EasyLogging++學習系列(2)—— 日志級別》中,簡單地提到了DEBUG模式的日志記錄。所謂的DEBUG模式,就是只在Debug版本的程序中才有意義,而在Release版本的程序中則沒有意義。在本文以上內容所提到的全部的日志記錄方式中,只要是以宏定義形式記錄日志信息的,基本上都會有一個DEBUG模式對應的宏定義。在 Easylogging++ 中,所有用於DEBUG模式的宏定義都是以大寫字母 D 開頭的,由於DEBUG模式宏定義的使用方式和常規的宏定義使用方式都是一樣的,這里不再贅述,建議有興趣了解更多關於DEBUG模式日志詳細信息的小伙伴可以親自動手試一試。