技术开发 频道

基于LGPL开源项目 Log4cpp安装与使用

  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 << as " << 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
<< as 1 critical message at time 2009-07-29 21:54:
09,531
1248875649 CRIT sub1.sub2 : This will show up << as 1 critical message
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等语言族共享其概念与使用方法。实在是进行日志记录、程序调试的利器。

4
相关文章