日志库EasyLogging++学习系列(7)—— 记录方式详解

来源:互联网 发布:蜘蛛爬行软件 编辑:程序博客网 时间:2024/06/15 11:46

在前面所列文章的演示代码中,其实已经展示了一部分记录日志的方式。为了使用方便,在 Easylogging++ 中,通过使用宏的集合来完成日志记录。


普通日志记录


对于普通的日志记录,我们可以选择以下两种比较常用的方式:

  • LOG(LEVEL)
  • CLOG(LEVEL, logger ID)
两个宏定义的中 LEVEL 请参看《日志库EasyLogging++学习系列(2)—— 日志级别》,而宏CLOG(CUSTOM LOG)中的 logger ID 可以参看《日志库EasyLogging++学习系列(6)—— 日志记录器》。下面的代码简单演示了如何使用这两个宏:

[cpp] view plain copy
print?
  1. #include "easylogging++.h"  
  2.   
  3. INITIALIZE_EASYLOGGINGPP  
  4.   
  5. int main(int argc, char** argv)  
  6. {  
  7.     /// 可以直接使用,不记录任何日志信息  
  8.     LOG(INFO);  
  9.     CLOG(INFO, "default");  
  10.   
  11.     LOG(INFO) << "Here is very simple example.";  
  12.     CLOG(INFO, "default") << "Here is very simple example.";  
  13.       
  14.     system("pause");  
  15.     return 0;  
  16. }  
通过上面的演示代码可以发现,其实 LOG(LEVEL) 就是 CLOG(LEVEL, "default") 的缩写。不过,如果我们在包含头文件 #include "easylogging++.h" 的代码前面使用另外一个指定的 ID 来定义宏 ELPP_DEFAULT_LOGGER ,那么这个指定的 ID 就会自动地替换掉 "default" 。需要注意的是,这个指定 ID 所标识的日志记录器必须保证使用前已经被注册,否则,将无法正常使用宏 LOG(LEVEL) 来记录日志信息。下面的代码演示这个功能:

[cpp] view plain copy
print?
  1. #define ELPP_DEFAULT_LOGGER "testlog"  
  2. #include "easylogging++.h"  
  3.   
  4. INITIALIZE_EASYLOGGINGPP  
  5.   
  6. int main(int argc, char** argv)  
  7. {  
  8.     /// 使用前,注册ID为testlog的日志记录器  
  9.     el::Logger* newLogger = el::Loggers::getLogger("testlog");  
  10.     LOG(INFO) << "Here is very simple example.";  
  11.   
  12.     system("pause");  
  13.     return 0;  
  14. }  

条件日志记录


条件日志就是只有当满足某一个条件的时候才进行日志记录,否则将忽略记录。下面是记录条件日志的两个宏定义:

  • LOG_IF(condition, LEVEL)
  • CLOG_IF(condition, LEVEL, logger ID)

上面两个宏定义中 condition 条件为真时,日志信息才被记录,在某些应用场景下这会显得十分便利,下面的代码演示了条件日志宏定义的用法:

[cpp] view plain copy
print?
  1. #include "easylogging++.h"  
  2.   
  3. INITIALIZE_EASYLOGGINGPP  
  4.   
  5. int main(int argc, char** argv)  
  6. {  
  7.     /// 下面这三个日志,只有第一个会输出  
  8.     LOG_IF(1 == 1, INFO) << "1 is equal to 1";  
  9.   
  10.     LOG_IF(1 > 2, INFO) << "1 is greater than 2";  
  11.   
  12.     LOG_IF(1 == 2, DEBUG) << "1 is equal to 2";  
  13.   
  14.     system("pause");  
  15.     return 0;  
  16. }  

偶然日志记录


偶然日志可以分为以下三种常见的情况,具体使用方法请参考示例代码:

  • 每 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)

[cpp] view plain copy
print?
  1. #include "easylogging++.h"  
  2.   
  3. INITIALIZE_EASYLOGGINGPP  
  4.   
  5. int main(int argc, char** argv)  
  6. {  
  7.     /// 每 N 次记录一次日志  
  8.     for (int i = 1; i <= 200; ++i)  
  9.     {  
  10.         LOG_EVERY_N(20, INFO) << "LOG_EVERY_N i = " << i;  
  11.         LOG_EVERY_N(100, INFO) << "LOG_EVERY_N Current position is " << ELPP_COUNTER_POS;  
  12.     }  
  13.   
  14.     /// 当计数达到 N 次之后,才开始记录日志  
  15.     for (int i = 1; i <= 10; ++i)   
  16.     {  
  17.         LOG_AFTER_N(6, INFO) << "LOG_AFTER_N i = " << i;  
  18.     }  
  19.   
  20.     /// 当记录次数达到 N 次之后,就不再记录  
  21.     for (int i = 1; i < 10; ++i)   
  22.     {  
  23.         LOG_N_TIMES(6, INFO) << "LOG_N_TIMES i = " << i;  
  24.     }  
  25.   
  26.     system("pause");  
  27.     return 0;  
  28. }  

STL容器日志记录


在前面的文章《日志库EasyLogging++学习系列(5)—— 辅助配置功能》中,已经给过示例演示了如何记录 STL 容器日志的方式。再次提示,使用 STL容器日志记录需要定义宏 ELPP_STL_LOGGING,每个容器默认最大容量是 100。下面是 Easylogging++ 支持的 STL 容器类型:


****std::vectorstd::liststd::dequestd::queuestd::stackstd::priority_queuestd::setstd::multisetstd::pairstd::bitsetstd::mapstd::multimap下面的是 C++11 标准才支持的 STL 容器,在 Easylogging++ V9.80版本中定义相应的宏就可以使用,别忘记同样还需要定义宏 ELPP_STL_LOGGING


TemplateMacro Neededstd::arrayELPP_LOG_STD_ARRAYstd::unordered_mapELPP_LOG_UNORDERED_MAPstd::unordered_multimapELPP_LOG_UNORDERED_MAPstd::unordered_setELPP_LOG_UNORDERED_SETstd::unordered_multisetELPP_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宏日志记录

利用CHECK宏日志可以快速地判断某个条件是否满足,当满足条件时不作记录;而不满足条件时就会记录 FATAL 级别的日志,并且会中断程序,除非你添加了标记 DisableApplicationAbortOnFatalLog ,下面是 Easylogging++ 支持的 CHECK 宏:

CHECK NameNotes + ExampleCHECK(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";详细的用法请看演示代码,需要特别注意的是宏 CHECK_NOTNULL(pointer) 的用法:
[cpp] view plain copy
print?
  1. #include "easylogging++.h"  
  2.   
  3. INITIALIZE_EASYLOGGINGPP  
  4.   
  5. int main(void)   
  6. {  
  7.     el::Loggers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog);  
  8.   
  9.     /// CHECK(condition)  
  10.     CHECK(1 < 2) << "CHECK(condition) example 1";      // condition为真时不记录  
  11.     CHECK(1 > 2) << "CHECK(condition) example 2";      // condition为假时才记录  
  12.   
  13.     /// CHECK_EQ(a, b)  
  14.     CHECK_EQ(1, 1) << "CHECK_EQ(a, b) example 1";     // 满足a = b时不记录  
  15.     CHECK_EQ(1, 0) << "CHECK_EQ(a, b) example 3";     // 不满足a = b时记录  
  16.   
  17.     /// CHECK_NE(a, b)   
  18.     CHECK_NE(1, 2) << "CHECK_NE(a, b) example 1";     // 满足a != b 时不记录  
  19.     CHECK_NE(1, 1) << "CHECK_NE(a, b) example 2";     // 不满足a != b 时记录  
  20.   
  21.     /// CHECK_LT(a, b)   
  22.     CHECK_LT(1, 2) << "CHECK_LT(a, b) example 1";     // 满足a < b时不记录  
  23.     CHECK_LT(1, 1) << "CHECK_LT(a, b) example 2";     // 不满足a < b时记录  
  24.   
  25.     /// CHECK_GT(a, b)   
  26.     CHECK_GT(2, 1) << "CHECK_GT(a, b) example 1";     // 满足a > b时不记录  
  27.     CHECK_GT(1, 1) << "CHECK_GT(a, b) example 2";     // 不满足a > b时记录  
  28.   
  29.     /// CHECK_LE(a, b)   
  30.     CHECK_LE(1, 1) << "CHECK_LE(a, b) example 1";     // 满足a <= b时不记录  
  31.     CHECK_LE(1, 0) << "CHECK_LE(a, b) example 2";     // 不满足a <= b时记录  
  32.   
  33.     /// CHECK_GE(a, b)   
  34.     CHECK_GE(1, 1) << "CHECK_GE(a, b) example 1";     // 满足a >= b时不记录  
  35.     CHECK_GE(1, 2) << "CHECK_GE(a, b) example 2";     // 不满足a >= b时记录  
  36.   
  37.     /// CHECK_STREQ(str1, str2) C风格字符串区分大小写  
  38.     CHECK_STREQ("abc""abc") << "CHECK_STREQ(str1, str2) example 1"// 满足str1 = str2时不记录  
  39.     CHECK_STREQ("abc""ABC") << "CHECK_STREQ(str1, str2) example 2"// 不满足str1 = str2时记录  
  40.   
  41.     /// CHECK_STRNE(str1, str2) C风格字符串区分大小写  
  42.     CHECK_STRNE("abc""ABC") << "CHECK_STRNE(str1, str2) example 1";   // 满足str1 != str2时不记录  
  43.     CHECK_STRNE("abc""abc") << "CHECK_STRNE(str1, str2) example 2";   // 不满足str1 != str2时记录  
  44.   
  45.     /// CHECK_STRCASEEQ(str1, str2) C风格字符串不区分大小写  
  46.     CHECK_STRCASEEQ("abc""ABC") << "CHECK_STRCASEEQ(str1, str2) example 1"// 满足str1 = str2时不记录  
  47.     CHECK_STRCASEEQ("abc""abd") << "CHECK_STRCASEEQ(str1, str2) example 2"// 不满足str1 = str2时记录  
  48.   
  49.     /// CHECK_STRCASENE(str1, str2) C风格字符串不区分大小写  
  50.     CHECK_STRCASENE("abc""abd") << "CHECK_STRCASENE(str1, str2) example 1"// 满足str1 != str2时不记录  
  51.     CHECK_STRCASENE("abc""ABC") << "CHECK_STRCASENE(str1, str2) example 2"// 不满足str1 != str2时记录  
  52.   
  53.     /// CHECK_NOTNULL(pointer) 判断指针pointer是否为空,不为空时返回pointer  
  54.     int* f = nullptr;  
  55.     if (CHECK_NOTNULL(f))  
  56.     {  
  57.     }  
  58.   
  59.     f = new int;  
  60.     if (CHECK_NOTNULL(f))  
  61.     {  
  62.     }  
  63.     delete f;  
  64.     f = nullptr;  
  65.   
  66.     int min = 2;  
  67.     int max = 5;  
  68.     CHECK_BOUNDS(1, min, max) << "CHECK_BOUNDS(val, min, max) example 1";  
  69.     CHECK_BOUNDS(2, min, max) << "CHECK_BOUNDS(val, min, max) example 2";  
  70.     CHECK_BOUNDS(3, min, max) << "CHECK_BOUNDS(val, min, max) example 3";  
  71.     CHECK_BOUNDS(4, min, max) << "CHECK_BOUNDS(val, min, max) example 4";  
  72.     CHECK_BOUNDS(5, min, max) << "CHECK_BOUNDS(val, min, max) example 5";  
  73.     CHECK_BOUNDS(6, min, max) << "CHECK_BOUNDS(val, min, max) example 6";  
  74.   
  75.     system("pause");  
  76.     return 0;  
  77. }  

printf风格日志记录

如果编译器支持 C++11 标准的可变参数模板,那么我们可以像 printf 函数一样来记录日志,唯一和 printf 函数不一样的是,我们只需用 %v 来带代替所有的参数即可,而不需要根据变量类型来指定格式。如果想要输出 %v ,只需加上转义字符 %%v 即可。请看下面 el::Logger 类的成员函数:
  • 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&...)
下面的代码演示了printf风格日志记录的使用方法:
[cpp] view plain copy
print?
  1. #define  ELPP_STL_LOGGING  
  2. #include "easylogging++.h"  
  3.   
  4. INITIALIZE_EASYLOGGINGPP  
  5.   
  6. int main(int argc, char** argv)   
  7. {  
  8.     el::Logger* defaultLogger = el::Loggers::getLogger("default");  
  9.   
  10.     std::vector<int> i;  
  11.     i.push_back(1);  
  12.     i.push_back(2);  
  13.   
  14.     /// 记录STL容器数据  
  15.     defaultLogger->warn("My first ultimate log message %v %v %v", 123, 222, i);  
  16.   
  17.     // 利用转义字符输出 % 和 %v  
  18.     defaultLogger->info("My first ultimate log message %% %%v %v %v", 123, 222);   
  19.   
  20.     system("pause");  
  21.     return 0;  
  22. }  


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 风格日志记录的使用方式:

[cpp] view plain copy
print?
  1. #include "easylogging++.h"  
  2.   
  3. INITIALIZE_EASYLOGGINGPP  
  4.   
  5. int main(void)   
  6. {  
  7.     el::Loggers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog);  
  8.   
  9.     /// 读取一个不存在的文件,改变errno变量的值  
  10.     std::fstream f("a file that does not exist", std::ifstream::in);  
  11.   
  12.     /// PLOG(LEVEL)  
  13.     PLOG(INFO) << "A message with plog";  
  14.   
  15.     /// PLOG_IF(Condition, LEVEL), 条件为真时才记录  
  16.     PLOG_IF(true, INFO) << "A message with plog 1";  
  17.     PLOG_IF(false, INFO) << "A message with plog 2";  
  18.   
  19.     /// PCHECK()  满足条件不记录,不满足才记录  
  20.     PCHECK(true) << "check message with plog 1";  
  21.     PCHECK(false) << "check message with plog 2";  
  22.   
  23.     system("pause");  
  24.     return 0;  
  25. }  

DEBUG模式日志记录


在前面的文章《日志库EasyLogging++学习系列(2)—— 日志级别》中,简单地提到了DEBUG模式的日志记录。所谓的DEBUG模式,就是只在Debug版本的程序中才有意义,而在Release版本的程序中则没有意义。在本文以上内容所提到的全部的日志记录方式中,只要是以宏定义形式记录日志信息的,基本上都会有一个DEBUG模式对应的宏定义。在 Easylogging++ 中,所有用于DEBUG模式的宏定义都是以大写字母 D 开头的,由于DEBUG模式宏定义的使用方式和常规的宏定义使用方式都是一样的,这里不再赘述,建议有兴趣了解更多关于DEBUG模式日志详细信息的小伙伴可以亲自动手试一试。

阅读全文
0 0