9、Log4cpp的自动内存管理
9.1 项目的多线程设置
VC中必须将项目设置为Debug MultiThreaded DLL,总之这个设置必须与你使用的Log4cpp库一致。如果你使用的是Release版本的log4cpp.dll,则应该设置为MultiThreaded DLL。
否则在程序结束时会报错,报错处的调用堆栈为:
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。如果程序员手动释放这些对象,则会造成内存报错。
从下面的代码可以看出这个特征:
这个new出来的BasicLayout根本就没有保存其指针,所以它只能被log4cpp的内存管理类HierarchyMaintainer释放。
了解到HierarchyMaintainer的内存管理方法后,程序员在使用log4cpp时应该遵循以下几个使用原则:
1. 不要手动释放Category、Appender和Layout;
2. 同一个Appender不要加入多个Category,否则它会被释放多次从而导致程序崩溃;
3. 同一个Layout不要附着到多个Appender上,否则也会被释放多次导致程序崩溃;
下面这个简单的程序PointerErrorExam会造成经典的崩溃:
#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;
}
运行后出现对话框:
其原因就是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手册):
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 <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 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等语言族共享其概念与使用方法。实在是进行日志记录、程序调试的利器。