log4cplus代码OO面向对象设计

来源:互联网 发布:mac 安装rar 命令行 编辑:程序博客网 时间:2024/06/12 21:24

 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输出:

[cpp] view plaincopy
  1. int main()  
  2. {  
  3. ……  
  4. // 定义Logger,并设置优先级makeNewLoggerInstance  
  5. LoggerpTestLogger = Logger::getInstance(LOG4CPLUS_TEXT("LoggerName"));   
  6. pTestLogger.setLogLevel(WARN_LOG_LEVEL);  
  7.    
  8. // 定义一个FileAppender  
  9. SharedAppenderPtrappend_2(new FileAppender(LOG4CPLUS_TEXT("Test.log")));  
  10. append_2->setName(LOG4CPLUS_TEXT("Sencondappender"));  
  11.    
  12. // 定义一个TTCLayout,并绑定到Appender  
  13. auto_ptr<Layout>pTTCCLayout(new TTCCLayout());  
  14. append_2->setLayout(pTTCCLayout);  
  15.    
  16. // 将需要关联Logger的Appender添加到Logger上  
  17. pTestLogger.addAppender(append_1);  
  18. printAppenderList(pTestLogger.getAllAppenders());  
  19.    
  20. // 输出日志信息  
  21. LOG4CPLUS_WARN(pTestLogger,"This is a <Warn> log message...");  
  22. ……  
  23. }  


       在test.log文件中将可以看到打印信息:

63 [3784] WARNLoggerName <> - This is a <Warn> log message...

       对比log4c不同之处,log4cplus默认已经创建了root logger,可以直接使用的,这个一直都是默认创建的

[html] view plaincopy
  1. int main()  
  2. {  
  3. ……  
  4. // 获取rootlogger  
  5. Logger root =Logger::getRoot();  
  6. root.setLogLevel(WARN_LOG_LEVEL);  
  7. // 定义一个控制台的Appender  
  8. SharedAppenderPtrappend_1(new ConsoleAppender());  
  9. append_1->setName(LOG4CPLUS_TEXT("Firstappender"));  
  10. // 关联appender到rootlogger  
  11. Logger::getRoot().addAppender(append_1);  
  12. LOG4CPLUS_WARN(root,"This is a <Warn> log message...");  
  13. ……  
  14. }  

       实际中常用的是通过读取properties配置文件来操作的,对应上面手工创建的logger,配置模式创建log4cplus.properties文件如下:

[cpp] view plaincopy
  1. log4cplus.rootLogger=TRACE,STDOUT  
  2. log4cplus.logger.testlogger=TRACE,TEST  
  3. log4cplus.additivity.testlogger=FALSE  
  4.    
  5. log4cplus.appender.STDOUT=log4cplus::ConsoleAppender  
  6. log4cplus.appender.STDOUT.layout=log4cplus::SimpleLayout  
  7.    
  8. log4cplus.appender.TEST=log4cplus::FileAppender  
  9. log4cplus.appender.TEST.File=test_output.log  
  10. log4cplus.appender.TEST.layout=log4cplus::TTCCLayout  

       编写测试程序

[cpp] view plaincopy
  1. int main()  
  2. {  
  3. ……  
  4. PropertyConfigurator::doConfigure(LOG4CPLUS_TEXT("log4cplus.properties"));  
  5. Logger filelogger= Logger::getInstance(LOG4CPLUS_TEXT("testlogger"));  
  6. LOG4CPLUS_WARN(filelogger,"filelogger test....")  
  7. LOG4CPLUS_WARN(root,"root test....")  
  8. ……  
  9. }  

       用户可以根据自己创建的filelogger,或者选择root logger来打印log,另外log4cplus.logger.testlogger=TRACE, TEST,这里还可以添加不同的appender到logger中去,比如添加了2个不同appender,通过pTestLogger.getAllAppenders()得到pTestLogger关联的所有appenders,打印如下:

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下的测试程序打印结果如下:

[cpp] view plaincopy
  1. Logger name:test Parent = root  
  2. Logger name:test2 Parent = root  
  3. Logger name:test.subtest.a.b.c Parent = test  
  4. Logger name:test.subtest.a Parent = test  
  5. Logger name:test.subtest Parent = test  
  6. Logger name:test.subtest.a Parent = test.subtest  
  7. Logger name:test.subtest.a.b.c Parent = test.subtest.a  
  8. Logger name:test.subtest.a.b.c.d Parent = test.subtest.a.b.c  
  9. Logger name:test.subtest.a.b.c Parent = test.subtest.a  
  10. Logger name:test.subtest.a Parent = test.subtest  
  11. Logger name:test.subtest Parent = test  

       Hierarchy.cxx主要就是维护这部分工作

       主要关心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例子

[cpp] view plaincopy
  1. //头文件  
  2. class CSample  
  3. {  
  4. private:  
  5.        class CImpl;  
  6.        shared_ptr<CImpl> mp;  
  7.    
  8. public:  
  9.        CSample();  
  10.        void print();  
  11. };  
  12.    
  13. //实现  
  14. classCSample::CImpl  
  15. {  
  16. public:  
  17.        void doPrint()  
  18.        {  
  19.         cout << "impl print" << endl;  
  20.        }  
  21. };  
  22.    
  23. CSample::CSample(): mp(new CImpl)  
  24. {}  
  25.    
  26. voidCSample::print() { mp->doPrint(); }  

       用户只需要操作CSample类的print,不需要关心CImpl类如何工作(1)

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

[cpp] view plaincopy
  1. #define LOG4CPLUS_INFO(logger, logEvent) \  
  2.     if(logger.isEnabledFor(log4cplus::INFO_LOG_LEVEL)) { \  
  3.         log4cplus::tostringstream _log4cplus_buf; \  
  4.         _log4cplus_buf << logEvent; \  
  5.         logger.forcedLog(log4cplus::INFO_LOG_LEVEL, _log4cplus_buf.str(), __FILE__, __LINE__); \  
  6. ////////////////  
  7. void Logger::forcedLog(LogLevel ll, const log4cplus::tstring& message,  
  8.                     const char* file, int line)  
  9. ////////////////  
  10. void LoggerImpl::forcedLog(LogLevel ll,  
  11.                       const log4cplus::tstring& message,  
  12.                       const char* file,   
  13.                       int line)  
  14. {  
  15.     callAppenders(spi::InternalLoggingEvent(this->getName(), ll, message, file, line));  
  16. }  
  17. ////////////////  
  18. int AppenderAttachableImpl::appendLoopOnAppenders(const spi::InternalLoggingEvent& event) const          
  19. ////////////////  
  20. void Appender::doAppend(const log4cplus::spi::InternalLoggingEvent& event)  
  21. ////////////////  
  22. int AppenderAttachableImpl::appendLoopOnAppenders(const spi::InternalLoggingEvent& event) const  
  23. {  
  24.     int count = 0;  
  25.     LOG4CPLUS_BEGIN_SYNCHRONIZE_ON_MUTEX( appender_list_mutex )  
  26.         for(ListType::const_iterator it=appenderList.begin();  
  27.             it!=appenderList.end();  
  28.             ++it)  
  29.         {  
  30.             ++count;  
  31.             (*it)->doAppend(event);  
  32.         }  
  33.     LOG4CPLUS_END_SYNCHRONIZE_ON_MUTEX  
  34.     return count;  
  35. }  
       appendLoopOnAppenders就是Observer模式中的notify功能,通知Event到所有关联的appender 

6.       内部调试类loglog的singleton模式实现

       LogLog类是内部调试用的,所以只容许一个引用存在的,采用singleton实现,

       一般有两种方式实现singleton,或者使用一个私有的静态成员,或者在方法内部使用局部静态变量。Log4cplus采用的是后面一种,代码如下:

[cpp] view plaincopy
  1. staticlog4cplus::helpers::SharedObjectPtr<LogLog> getLogLog();  
  2. LogLog::getLogLog()  
  3. {  
  4.     static SharedObjectPtr<LogLog>singleton(new LogLog());  
  5.     return singleton;  
  6. }  

       实际上log4cplus是采用LogLogUser类做为所有log4cplus类的基类来简化loglog的使用的,这样让loglog类隐藏不直接面对User
0 0
原创粉丝点击