基于LGPL开源项目 Log4cpp安装与使用(二)(代码详解)

6.5 NTEventLogAppender
  该Appender可以将日志发送到windows的日志,在运行程序后可以打开windows的计算机管理?系统工具?事件查看器?应用程序,可以看到下图,注意图中第一行和第二行的两个日志。
  例子程序NTAppenderExam如下:
View Code
 1 #include <iostream>
 2 #include "log4cpp/Category.hh"
 3 #include "log4cpp/Appender.hh" #include "log4cpp/NTEventLogAppender.hh"
 4 #include "log4cpp/PatternLayout.hh" 
 5 #include "log4cpp/Priority.hh" 
 6 #pragma comment(lib,"log4cppD.lib") 
 7 //using namespace std; 
 8 int main(int arg,char* argv[]) { 
 9     //定义一个Appender,类型为输出Windows系统日志 
10    log4cpp::Appender* ntEventApp = new log4cpp::NTEventLogAppender("ntEventApp","wxbnt.log"); 
11     //定义一个Layout,类型为PatternLayout     
12     log4cpp::PatternLayout* ptnLayout = new log4cpp::PatternLayout(); //设置该Layout的格式     
13    ptnLayout->setConversionPattern("%d: %c %p %x:%m%n"); //将该Layout添加到定义的Appender中     
14    ntEventApp->setLayout(ptnLayout); //定义一个Category的子类,名字为infoCategory     
15     log4cpp::Category& infoCategory = log4cpp::Category::getRoot().getInstance("infoCategory"); //将上述的Appender添加到该Category中
16    infoCategory.setAppender(ntEventApp); //设置该Category的优先级
17    infoCategory.setPriority(log4cpp::Priority::DEBUG); //记录日志,将日志写入Windows系统日志
18    infoCategory.error("a error occurred!!");
19     infoCategory.warn("a progam is in debug");
20     infoCategory.shutdown();
21     return 0;
22  } 

 

结果:

 

图片

 

  右键事件属性

 

图片

 

 
  7、Category
  Log4cpp中有一个总是可用并实例化好的Category,即根Category。使用log4cpp::Category::getRoot()可以得到根Category。在大多数情况下,一个应用程序只需要一个日志种类(Category),但是有时也会用到多个Category,此时可以使用根Category的getInstance方法来得到子Category。不同的子Category用于不同的场合。一个简单的例子CategoryExam如下所示:
  
  #include iostream>
  #include log4cpp/Category.hh>
  #include log4cpp/OstreamAppender.hh>
  #include log4cpp/FileAppender.hh>
  #include log4cpp/BasicLayout.hh>
  #include log4cpp/Priority.hh>
  using namespace std;
  int main(int argc, char* argv[])
  {
  log4cpp::OstreamAppender* osAppender1 = new log4cpp::OstreamAppender("osAppender1", &cout);
  osAppender1->setLayout(new log4cpp::BasicLayout());
  log4cpp::OstreamAppender* osAppender2 = new log4cpp::OstreamAppender("osAppender2", &cout);
  osAppender2->setLayout(new log4cpp::BasicLayout());
  log4cpp::Category& root = log4cpp::Category::getRoot();
  root.setPriority(log4cpp::Priority::DEBUG);
  log4cpp::Category& sub1 = root.getInstance("sub1");
  sub1.addAppender(osAppender1);
  sub1.setPriority(log4cpp::Priority::DEBUG);
  sub1.error("sub error");
  log4cpp::Category& sub2 = root.getInstance("sub2");
  sub2.addAppender(osAppender2);
  sub2.setPriority(101);
  sub2.warn("sub2 warning");
  sub2.fatal("sub2 fatal");
  sub2.alert("sub2 alert");
  sub2.crit("sub2 crit");
  log4cpp::Category::shutdown();
  return 0;
  }
  运行结果如下:
  
  1248869982 ERROR sub1 : sub error
  1248869982 FATAL sub2 : sub2 fatal
  1248869982 ALERT sub2 : sub2 alert
  这个例子中共有三个Category,分别是根、sub1和sub2,其中sub1记录了一条日志,sub2记录了两条日志。Sub2另外两个日志由于优先级不够未能记录。
  8、NDC
  NDC是nested Diagnostic Context的缩写,意思是“嵌套的诊断上下文”。NDC是一种用来区分不同源代码中交替出现的日志的手段。当一个服务端程序同时记录好几个并行客户时,输出的日志会混杂在一起难以区分。但如果不同上下文的日志入口拥有一个特定的标识,则可以解决这个问题。NDC就是在这种情况下发挥作用。注意NDC是以线程为基础的,每个线程拥有一个NDC,每个NDC的操作仅对执行该操作的线程有效。
  NDC的几个有用的方法是:push、pop、get和clear。注意它们都是静态函数:
  Push可以让当前线程进入一个NDC,如果该NDC不存在,则根据push的参数创建一个NDC并进入;如果再调用一次push,则进入子NDC;
  Pop可以让当前线程从上一级NDC中退出,但是一次只能退出一级。
  Clear可以让当前线程从所有嵌套的NDC中退出。
  Get可以得到当前NDC的名字,如果有嵌套,则不同级别之间的名字用空格隔开。
  一个简单的例子NDCExam如下:
 1 #include <iostream> 
 2 #include "log4cpp/NDC.hh" 
 3 #pragma comment(lib,"log4cppD.lib")
 4 
 5 using namespace log4cpp; 
 6 int main(int argc, char** argv) 
 7 { 
 8     std::cout<<"1. empty "<<NDC::get()<<std::endl; 
 9     NDC::push("context1");  //添加第一层 
10     std::cout<<"2. push context1: "<<NDC::get()<<std::endl; NDC::push("context2");//添加第二层 
11     std::cout<<"3. push context2: "<<NDC::get()<<std::endl; NDC::push("context3");//添加第三层 
12     std::cout<<"4. push context3: "<<NDC::get()<<std::endl;
13       std::cout<<"5. get depth: "<<NDC::getDepth()<<std::endl; //显示层数,有3层 
14     std::cout<<"6. pop: "<<NDC::pop()<<std::endl; //弹出最上面的一层,这里是context3 
15       std::cout<<"7. after pop: "<<NDC::get()<<std::endl;//弹出后,显示剩下的层 
16     NDC::clear(); //将所有的层都清除 
17     std::cout<<"8. clear: "<<NDC::get()<<std::endl; 
18       return 0; 
19 }

 

运行结果:

 

图片

 

  该例子来自log4cpp的例子程序,我做了简单的修改。在记录日志的时候,可以从NDC中得知当前线程的嵌套关系。
  9、Log4cpp的自动内存管理
  9.1 项目的多线程设置
  VC中必须将项目设置为Debug MultiThreaded DLL,总之这个设置必须与你使用的Log4cpp库一致。如果你使用的是Release版本的log4cpp.dll,则应该设置为MultiThreaded DLL。
  否则在程序结束时会报错,报错处的调用堆栈为:
  
  log4cpp::BasicLayout::`vector deleting destructor"(unsigned int 1) + 122 bytes
  log4cpp::LayoutAppender::~LayoutAppender() line 21 + 35 bytes
  log4cpp::OstreamAppender::~OstreamAppender() line 28 + 15 bytes
  log4cpp::OstreamAppender::`vector deleting destructor"(unsigned int 1) + 103 bytes
  log4cpp::Category::removeAllAppenders() line 159 + 39 bytes
  log4cpp::HierarchyMaintainer::shutdown() line 101 + 27 bytes
  log4cpp::HierarchyMaintainer::~HierarchyMaintainer() line 36
  9.2 Log4cpp的内存对象管理
  也许读者已经注意到,在前面的所有代码中,log4cpp中所有动态分配的对象都没有手动释放。
  Log4cpp中new出来的Category、Appender和Layout都不需要手动释放,因为Log4cpp使用了一个内部类来管理这些对象。此类的名称是HierarchyMaintainer,它负责管理Category的继承关系,在程序结束时,HierarchyMaintainer会依次释放所有Category,而Category则会依次释放拥有的有效Appender,Appender则会释放所有附属的Layout。如果程序员手动释放这些对象,则会造成内存报错。
  从下面的代码可以看出这个特征:
  
  appender->setLayout(new log4cpp::BasicLayout());
  这个new出来的BasicLayout根本就没有保存其指针,所以它只能被log4cpp的内存管理类HierarchyMaintainer释放。
  了解到HierarchyMaintainer的内存管理方法后,程序员在使用log4cpp时应该遵循以下几个使用原则:
  1. 不要手动释放Category、Appender和Layout;
  2. 同一个Appender不要加入多个Category,否则它会被释放多次从而导致程序崩溃;
  3. 同一个Layout不要附着到多个Appender上,否则也会被释放多次导致程序崩溃;
  下面这个简单的程序PointerErrorExam会造成经典的崩溃:
  
  #include iostream>
  #include log4cpp/Category.hh>
  #include log4cpp/OstreamAppender.hh>
  #include log4cpp/BasicLayout.hh>
  #include log4cpp/Priority.hh>
  using namespace std;
  int main(int argc, char* argv[])
  {
  log4cpp::OstreamAppender* osAppender = new log4cpp::OstreamAppender("osAppender", &cout);
  osAppender->setLayout(new log4cpp::BasicLayout());
  log4cpp::Category& root = log4cpp::Category::getRoot();
  root.setPriority(log4cpp::Priority::DEBUG);
  log4cpp::Category& sub1 = root.getInstance("sub1");
  sub1.addAppender(osAppender);
  sub1.error("sub1 error");
  log4cpp::Category& sub2 = root.getInstance("sub2");
  sub2.addAppender(osAppender);
  sub2.warn("sub2 warning");
  log4cpp::Category::shutdown();
  return 0;
  }
  运行后出现对话框:
  
  PointerErrorExam.exe 遇到问题需要关闭。我们对此引起的不便表示抱歉。
  其原因就是osAppender被同时加入了sub1和sub2这两个Category。
  9.3 log4cpp::Category::shutdown()
  在不使用log4cpp时可调用log4cpp::Category::shutdown(),其功能如同HierarchyMaintainer的内存清理。但如果不手动调用,在程序结束时HierarchyMaintainer会调用Category的析构函数来释放所有Appender。
  10、利用配置文件定制日志
  如同log4j一样,log4cpp也可以读取配置文件来定制Category、Appender和Layout对象。其配置文件格式基本类似于log4j,一个简单的配置文件log4cpp.conf例子如下(来自log4cpp的API手册):
  
  # a simple test config
  log4j.rootCategory=DEBUG, rootAppender
  log4j.category.sub1=A1
  log4j.category.sub2=INFO
  log4j.category.sub1.sub2=ERROR, A2
  log4j.appender.rootAppender=org.apache.log4j.ConsoleAppender
  log4j.appender.rootAppender.layout=org.apache.log4j.BasicLayout
  log4j.appender.A1=org.apache.log4j.FileAppender
  log4j.appender.A1.fileName=A1.log
  log4j.appender.A1.layout=org.apache.log4j.BasicLayout
  log4j.appender.A2=org.apache.log4j.ConsoleAppender
  log4j.appender.A2.layout=org.apache.log4j.PatternLayout
  log4j.appender.A2.layout.ConversionPattern=The message %m at time %d%n
  这是一个标准的java属性文件。读取配置文件要依赖PropertyConfigurator和SimpleConfigurator类。这里仅介绍PropertyConfigurator,其使用方法代码ConfigFileExam所示(该代码来自《便利的开发工具-log4cpp快速使用指南》一文):
  
  #include iostream>
  #include log4cpp/Category.hh>
  #include log4cpp/PropertyConfigurator.hh>
  int main(int argc, char* argv[])
  ...{
  try
  ...{
  log4cpp::PropertyConfigurator::configure("./log4cpp.conf");
  }
  catch(log4cpp::ConfigureFailure& f)
  ...{
  std::cout "Configure Problem " f.what() std::endl;
  return -1;
  }
  log4cpp::Category& root = log4cpp::Category::getRoot();
  log4cpp::Category& sub1 = log4cpp::Category::getInstance(std::string("sub1"));
  log4cpp::Category& sub3 = log4cpp::Category::getInstance(std::string("sub1.sub2"));
  sub1.info("This is some info");
  sub1.alert("A warning");
  // sub3 only have A2 appender.
  sub3.debug("This debug message will fail to write");
  sub3.alert("All hands abandon ship");
  sub3.critStream() "This will show up " 1 " critical message"
  log4cpp::CategoryStream::ENDLINE;
  sub3 log4cpp::Priority::ERROR
  "And this will be an error"
  log4cpp::CategoryStream::ENDLINE;
  sub3.log(log4cpp::Priority::WARN, "This will be a logged warning");
  return 0;
  }
  该程序首先读入了配置文件log4cpp.conf,从中得到了所有Category、Appender和Layout的优先级和相互附属关系,然后输出了一些日志,其运行结果如下:
  
  1248875649 INFO sub1 : This is some info
  1248875649 ALERT sub1 : A warning
  The message All hands abandon ship at time 2009-07-29 21:54:09,515
  1248875649 ALERT sub1.sub2 : All hands abandon ship
  The message This will show up 1 critical message at time 2009-07-29 21:54:
  09,531
  1248875649 CRIT sub1.sub2 : This will show up The message And this will be an error at time 2009-07-29 21:54:09,531
  1248875649 ERROR sub1.sub2 : And this will be an error
  11、DLL的版本问题
  若在VC6中使用Log4cpp的DLL,则必须使用VC6编译链接生成的DLL,不能使用MSVS2008中生成的DLL,反之也是一样。否则会在运行时报错。
  为此专门提供了两个版本的DLL,供大家使用。
  12、小结
  Log4cpp是一个小巧的c++库,易于上手,使用方便,不依赖其他库,具有跨平台性,并可与log4j、log4c、log4p等语言族共享其概念与使用方法。实在是进行日志记录、程序调试的利器。
  作者:王学斌 +  孜孜拳拳(孜孜拳拳只是调试了源码,附加了一些注释和修改)
posted @ 2013-04-17 22:16  孜孜拳拳  阅读(844)  评论(0编辑  收藏  举报