Log4cplus是C++写的一个LOG模块,在readme中有:It is modeled after the Java log4j API,代码架构和log4j类似,对比两份代码,log4cplus几乎就是把log4j按照C++写了一遍
在log4cplus-1.0.0中README中NOTE如下,说明是和log4j 1.1.3功能类似的
Current Status
==============
This library is currently in Beta. The goal is have approximately the same
level of functionality of log4j 1.1.3 before the first release. The current
estimate for the first Production release is September 2003.
分析了一下log4cplus的代码,因为代码量较小,所以不怎么借助doxygen基本上可以看懂,顺便好好的对比一下log4c和log4cplus的实现对比,一个C和C++分别实现面向对象的不同方式
而且log4cplus代码中用到了Singleton/Factory Method/Bridge/Observer等Pattern,还有Pimpl机制,引用计数机制,等等,值得总结
Log输出有几个模块:
(1)Logger--日志模块,程序中唯一一个必须得使用的模块,解决了在哪里使用日志的问题
(2)Appender--接收日志的各个设备,如控制台、文件、网络等。解决了输出到哪里去的问题
(3)Layout--格式化输出信息,解决了如何输出的问题。
(Log4c是log4x的C语言版本,logger对应的是category)
1. 应用实例
简单定义LOG输出:
int main() { …… // 定义Logger,并设置优先级makeNewLoggerInstance LoggerpTestLogger = Logger::getInstance(LOG4CPLUS_TEXT("LoggerName")); pTestLogger.setLogLevel(WARN_LOG_LEVEL); // 定义一个FileAppender SharedAppenderPtrappend_2(new FileAppender(LOG4CPLUS_TEXT("Test.log"))); append_2->setName(LOG4CPLUS_TEXT("Sencondappender")); // 定义一个TTCLayout,并绑定到Appender auto_ptr<Layout>pTTCCLayout(new TTCCLayout()); append_2->setLayout(pTTCCLayout); // 将需要关联Logger的Appender添加到Logger上 pTestLogger.addAppender(append_1); printAppenderList(pTestLogger.getAllAppenders()); // 输出日志信息 LOG4CPLUS_WARN(pTestLogger,"This is a <Warn> log message..."); …… }
在test.log文件中将可以看到打印信息:
63 [3784] WARNLoggerName <> - This is a <Warn> log message...
对比log4c不同之处,log4cplus默认已经创建了root logger,可以直接使用的,这个一直都是默认创建的
int main() { …… // 获取rootlogger Logger root =Logger::getRoot(); root.setLogLevel(WARN_LOG_LEVEL); // 定义一个控制台的Appender SharedAppenderPtrappend_1(new ConsoleAppender()); append_1->setName(LOG4CPLUS_TEXT("Firstappender")); // 关联appender到rootlogger Logger::getRoot().addAppender(append_1); LOG4CPLUS_WARN(root,"This is a <Warn> log message..."); …… }
log4cplus.rootLogger=TRACE,STDOUT log4cplus.logger.testlogger=TRACE,TEST log4cplus.additivity.testlogger=FALSE log4cplus.appender.STDOUT=log4cplus::ConsoleAppender log4cplus.appender.STDOUT.layout=log4cplus::SimpleLayout log4cplus.appender.TEST=log4cplus::FileAppender log4cplus.appender.TEST.File=test_output.log log4cplus.appender.TEST.layout=log4cplus::TTCCLayout
int main() { …… PropertyConfigurator::doConfigure(LOG4CPLUS_TEXT("log4cplus.properties")); Logger filelogger= Logger::getInstance(LOG4CPLUS_TEXT("testlogger")); LOG4CPLUS_WARN(filelogger,"filelogger test....") LOG4CPLUS_WARN(root,"root test....") …… }
List size: 2
Loop Body:Appender name = Sencond appender
Loop Body:Appender name = First appender
2. appender和layout的Factory Method模式实现
如果不是通过configure配置来创建logger,而是手动创建,代码比较直观,直接创建的子类,比如ConsoleAppender,SimpleLayout等等
下图是appender和layout创建的多态实现
在log4cplus-1.0.4中全部用的模板来实现的,在log4cplus-1.0.0中对于AppenderFactory、LayoutFactory采用Factory Method实现的
在configurator.cxx中代码
factoryName =appenderProperties.getProperty(*it);
AppenderFactory*factory = getAppenderFactoryRegistry().get(factoryName);
SharedAppenderPtrappender = factory->createObject(my_properties);
AppenderFactory就是一个Factory Method概念,factoryName就是ConsoleAppender/FileAppender/RollingFileAppender等,factory就是获取到的工厂ConsoleAppenderFactory/ FileAppenderFactory等,appender就是new的ConsoleAppender
这样就和手动创建的
SharedAppenderPtrappend_1(new ConsoleAppender());
等同
3. logger的Pimpl机制
Pimpl机制其实这是Bridge模式的一种变种,Bridge模式也被称为pimpl惯用法
logger部分的代码,写的N复杂,主要因为要维护root,的层次关系,在filter过滤会使用到,运行hierarchy_test下的测试程序打印结果如下:
Logger name:test Parent = root Logger name:test2 Parent = root Logger name:test.subtest.a.b.c Parent = test Logger name:test.subtest.a Parent = test Logger name:test.subtest Parent = test Logger name:test.subtest.a Parent = test.subtest Logger name:test.subtest.a.b.c Parent = test.subtest.a Logger name:test.subtest.a.b.c.d Parent = test.subtest.a.b.c Logger name:test.subtest.a.b.c Parent = test.subtest.a Logger name:test.subtest.a Parent = test.subtest Logger name:test.subtest Parent = test
主要关心logger.cxx和loggerimpl.cxx的代码,看命名就可以看出来loggerimpl.cxx采用了pimpl的机制
Pimpl(private implementation)主要是解开类接口使用的耦合,因为组合的耦合性是比较低的
在类logger中
/** This is apointer to the implementation class. */
spi::LoggerImpl* value;
就是指向内部类loggerimpl的指针,真正的动作是由loggerimpl来实现的,比如
virtual voidlog(LogLevel ll, const log4cplus::tstring& message,
const char* file=NULL, intline=-1);
virtual voidcallAppenders(const InternalLoggingEvent& event);
log是将log message打包成LoggingEvent
callAppenders是将LoggingEvent对象分发给本logger关联的所有的Appender
下面是一个pimpl例子
//头文件 class CSample { private: class CImpl; shared_ptr<CImpl> mp; public: CSample(); void print(); }; //实现 classCSample::CImpl { public: void doPrint() { cout << "impl print" << endl; } }; CSample::CSample(): mp(new CImpl) {} voidCSample::print() { mp->doPrint(); }
4. 带引用计数的指针SharedObjectPtr
上面的Pimpl例子中shared_ptr,是一个智能指针,是boost库自带的,而且是一个带引用计数的智能指针
在point.h中也有这样一个类SharedObjectPtr,注释中写到
// Note: Someof this code uses ideas from "More Effective C++" by Scott
// Myers,Addison Wesley Longmain, Inc., (c) 1996, Chapter 29, pp. 183-213
其实就是More Effective C++中第29节的引用计数的多线程版
参考地址:Scott Meyers:MoreEffective C++ Item 29 Source Code
5. AppenderAttachableImpl和Appender的Observer模式
AppenderAttachable,它可以被视为一个Appender的容器,封装了对Appender聚集的一些操作,Log4cplus使用AppenderAttachableImpl提供了默认的实现,其中
Vector appenderList
用Vector实现了Appender聚集的管理方法
Appender做为AppenderAttachableImpl的观察者(Observer),被观察的是AppenderAttachableImpl,可以attach很多Appender
#define LOG4CPLUS_INFO(logger, logEvent) \ if(logger.isEnabledFor(log4cplus::INFO_LOG_LEVEL)) { \ log4cplus::tostringstream _log4cplus_buf; \ _log4cplus_buf << logEvent; \ logger.forcedLog(log4cplus::INFO_LOG_LEVEL, _log4cplus_buf.str(), __FILE__, __LINE__); \ //////////////// void Logger::forcedLog(LogLevel ll, const log4cplus::tstring& message, const char* file, int line) //////////////// void LoggerImpl::forcedLog(LogLevel ll, const log4cplus::tstring& message, const char* file, int line) { callAppenders(spi::InternalLoggingEvent(this->getName(), ll, message, file, line)); } //////////////// int AppenderAttachableImpl::appendLoopOnAppenders(const spi::InternalLoggingEvent& event) const //////////////// void Appender::doAppend(const log4cplus::spi::InternalLoggingEvent& event) //////////////// int AppenderAttachableImpl::appendLoopOnAppenders(const spi::InternalLoggingEvent& event) const { int count = 0; LOG4CPLUS_BEGIN_SYNCHRONIZE_ON_MUTEX( appender_list_mutex ) for(ListType::const_iterator it=appenderList.begin(); it!=appenderList.end(); ++it) { ++count; (*it)->doAppend(event); } LOG4CPLUS_END_SYNCHRONIZE_ON_MUTEX return count; }appendLoopOnAppenders就是Observer模式中的notify功能,通知Event到所有关联的appender
6. 内部调试类loglog的singleton模式实现
LogLog类是内部调试用的,所以只容许一个引用存在的,采用singleton实现,
一般有两种方式实现singleton,或者使用一个私有的静态成员,或者在方法内部使用局部静态变量。Log4cplus采用的是后面一种,代码如下:
staticlog4cplus::helpers::SharedObjectPtr<LogLog> getLogLog(); LogLog::getLogLog() { static SharedObjectPtr<LogLog>singleton(new LogLog()); return singleton; }