(一)
log4cplus是C++编写的开源日志系统,功能很全面,用到自己开发的工程中会比较专业的,本文介绍了log4cplus基本概念,以及如何安装,配置。
### 简介 ###
log4cplus是C++编写的开源的日志系统,前身是java编写的log4j系统.受Apache Software License保护。作者是Tad E. Smith。log4cplus具有线程安全、灵活、以及多粒度控制的特点,通过将信息划分优先级使其可以面向程序调试、运行、测试、和维护等全生命周期; 你可以选择将信息输出到屏幕、文件、NT event log、甚至是远程服务器;通过指定策略对日志进行定期备份等等。
### 下载 ###
最新的log4cplus可以从以下网址下载 http://log4cplus.sourceforge.net本文使用的版本为:1.0.2
### 安装 ###
1. linux下安装
tar xvzf log4cplus-x.x.x.tar.gz cd log4cplus-x.x.x ./configure --prefix=/where/to/install make make install
2. windows下安装
不需要安装,有一个msvc6存放包括源代码和用例在内的开发工程(for VC6 only),使用之前请先编译"log4cplus_dll class"工程生成dll,或者编译"log4cplus_static class"工程生成lib.
### 使用前的配置 ###
1. linux下的配置
确保你的Makefile中包含 /usr/local/lib/liblog4cplus.a(静态库)或 -llog4cplus(动态库)即可,头文件在/usr/local/include/log4cplus目录下。对于动态库,要想正常使用,还得将库安装路径加入到LD_LIBRARY_PATH 中,我一般是这样做的:以管理员身份登录,在/etc/ld.so.conf中加入安装路径,这里是/usr/local/lib,然后执行ldconfig使设置生效即可。
2. windows下的配置
将"log4cplus_dll class"工程或"log4cplus_static class"工程的dsp 文件插入到你的工程中,或者直接把两个工程编译生成的库以及头文件所在目录放到你的工程的搜索路径中,如果你使用静态库,请在你的工程中"project/setting/C++"的preprocessor definitions中加入LOG4CPLUS_STATIC。
### 构成要素介绍 ###
虽然功能强大,应该说log4cplus用起来还是比较复杂的,为了更好地使用它,先介绍一下它的基本要素。
Layouts :布局器,控制输出消息的格式.
Appenders :挂接器,与布局器紧密配合,将特定格式的消息输出到所挂接的设备终端(如屏幕,文件等等)。
Logger :记录器,保存并跟踪对象日志信息变更的实体,当你需要对一个对象进行记录时,就需要生成一个logger。
Categories :分类器,层次化(hierarchy)的结构,用于对被记录信息的分类,层次中每一个节点维护一个logger的所有信息。
Priorities :优先权,包括TRACE, DEBUG, INFO, WARNING, ERROR, FATAL。
本文介绍了log4cplus基本概念,以及如何安装,配置,下一篇将通过例子介绍如何使用log4cplus。
(二)
本文介绍了使用log4cplus有六个步骤,并提供了一些例子引导你了解log4cplus的基本使用。
### 基本使用 ###
使用log4cplus有六个基本步骤:
1. 实例化一个appender对象
2. 实例化一个layout对象
3. 将layout对象绑定(attach)到appender对象
4. 实例化一个logger对象,调用静态函数:log4cplus::Logger::getInstance("logger_name")
5. 将appender对象绑定(attach)到logger对象,如省略此步骤,标准输出(屏幕)appender对象会绑定到logger
6. 设置logger的优先级,如省略此步骤,各种有限级的消息都将被记录
下面通过一些例子来了解log4cplus的基本使用。
〖例1〗
cpp 代码
/* 严格实现步骤1-6,appender输出到屏幕, 其中的布局格式和LogLevel后面会详细解释。*/ #include <log4cplus\logger.h> #include <log4cplus\consoleappender.h> #include <log4cplus\layout.h> #ifndef _DEBUG #pragma comment(lib, "log4cplus.lib") #else #pragma comment(lib, "log4cplusD.lib") #endif using namespace log4cplus; using namespace log4cplus::helpers; int main() { /* step 1: Instantiate an appender object */ SharedObjectPtr<Appender> _append (new ConsoleAppender()); _append->setName("append for test"); /* step 2: Instantiate a layout object */ std::string pattern = "%d{%m/%d/%y %H:%M:%S} - %m [%l]%n"; std::auto_ptr<Layout> _layout(new PatternLayout(pattern)); /* step 3: Attach the layout object to the appender */ _append->setLayout( _layout ); /* step 4: Instantiate a logger object */ Logger _logger = Logger::getInstance("test"); /* step 5: Attach the appender object to the logger */ _logger.addAppender(_append); /* step 6: Set a priority for the logger */ _logger.setLogLevel(ALL_LOG_LEVEL); /* log activity */ LOG4CPLUS_DEBUG(_logger, "This is the FIRST log message..."); sleep(1); LOG4CPLUS_WARN(_logger, "This is the SECOND log message..."); return 0; }
/* 简洁使用模式,appender输出到屏幕。 */ #include <log4cplus/logger.h> #include <log4cplus/consoleappender.h> #ifndef _DEBUG #pragma comment(lib, "log4cplus.lib") #else #pragma comment(lib, "log4cplusD.lib") #endif using namespace log4cplus; using namespace log4cplus::helpers; int main() { /* step 1: Instantiate an appender object */ SharedAppenderPtr _append(new ConsoleAppender()); _append->setName("append test"); /* step 4: Instantiate a logger object */ Logger _logger = Logger::getInstance("test"); /* step 5: Attach the appender object to the logger */ _logger.addAppender(_append); /* log activity */ LOG4CPLUS_DEBUG(_logger, "This is the FIRST log message..."); sleep(1); LOG4CPLUS_WARN(_logger, "This is the SECOND log message..."); return 0; }
/* iostream模式,appender输出到屏幕。 */ #include <log4cplus/logger.h> #include <log4cplus/consoleappender.h> #include <iomanip> /* 其实这个东东还是放到log4cplus头文件中比较合适些,个人意见:) */ #ifndef _DEBUG #pragma comment(lib, "log4cplus.lib") #else #pragma comment(lib, "log4cplusD.lib") #endif using namespace log4cplus; int main() { /* step 1: Instantiate an appender object */ SharedAppenderPtr _append(new ConsoleAppender()); _append->setName("append test"); /* step 4: Instantiate a logger object */ Logger _logger = Logger::getInstance("test"); /* step 5: Attach the appender object to the logger */ _logger.addAppender(_append); /* log activity */ LOG4CPLUS_TRACE(_logger, "This is" << " just a t" << "est." << std::endl); LOG4CPLUS_DEBUG(_logger, "This is a bool: " << true); LOG4CPLUS_INFO(_logger, "This is a char: " << 'x'); LOG4CPLUS_WARN(_logger, "This is a int: " << 1000); LOG4CPLUS_ERROR(_logger, "This is a long(hex): " << std::hex << 100000000); LOG4CPLUS_FATAL(_logger, "This is a double: " << std::setprecision(15) << 1.2345234234); return 0; }
/* 调试模式,通过loglog来控制输出调试、警告或错误信息,appender输出到屏幕。 */ #include <iostream> #include <log4cplus/helpers/loglog.h> #ifndef _DEBUG #pragma comment(lib, "log4cplus.lib") #else #pragma comment(lib, "log4cplusD.lib") #endif using namespace log4cplus::helpers; void printMsgs(void) { std::cout << "Entering printMsgs()..." << std::endl; LogLog::getLogLog()->debug("This is a Debug statement..."); LogLog::getLogLog()->warn("This is a Warning..."); LogLog::getLogLog()->error("This is a Error..."); std::cout << "Exiting printMsgs()..." << std::endl << std::endl; } int main() { /* LogLog类实现了debug, warn, error 函数用于输出调试、警告或错误信息, 同时提供了两个方法来进一步控制所输出的信息,其中: setInternalDebugging方法用来控制是否屏蔽输出信息中的调试信息,当输入 参数为false则屏蔽,缺省设置为false。 setQuietMode方法用来控制是否屏蔽所有输出信息,当输入参数为true则屏蔽, 缺省设置为false。 LogLog::getLogLog()->setInternalDebugging(false); */ printMsgs(); std::cout << "Turning on debug..." << std::endl; LogLog::getLogLog()->setInternalDebugging(true); printMsgs(); std::cout << "Turning on quiet mode..." << std::endl; LogLog::getLogLog()->setQuietMode(true); printMsgs(); return 0; }输出结果:
LogLog::LogLog() : mutex(LOG4CPLUS_MUTEX_CREATE), debugEnabled(false), quietMode(false), PREFIX( LOG4CPLUS_TEXT("log4cplus: ") ), WARN_PREFIX( LOG4CPLUS_TEXT("log4cplus:WARN ") ), ERR_PREFIX( LOG4CPLUS_TEXT("log4cplus:ERROR ") ) { }你可以把这些前缀换成自己看着爽的提示符号,然后重新编译,hihi。除非万不得已或者实在郁闷的不行,否则还是不要这样干。
/* 文件模式,appender输出到文件。*/ #include <log4cplus/logger.h> #include <log4cplus/fileappender.h> #ifndef _DEBUG #pragma comment(lib, "log4cplus.lib") #else #pragma comment(lib, "log4cplusD.lib") #endif using namespace log4cplus; int main() { /* step 1: Instantiate an appender object */ SharedAppenderPtr _append(new FileAppender("Test.log")); _append->setName("file log test"); /* step 4: Instantiate a logger object */ Logger _logger = Logger::getInstance("test.subtestof_filelog"); /* step 5: Attach the appender object to the logger */ _logger.addAppender(_append); /* log activity */ int i; for( i = 0; i < 5; ++i ) { LOG4CPLUS_DEBUG(_logger, "Entering loop #" << i << "End line #"); } return 0; }输出结果(Test.log文件):
... ... /* step 1: Instantiate an appender object */ SharedObjectPtr<Appender> _append (new ConsoleAppender()); _append->setName("append for test"); /* step 2: Instantiate a layout object */ std::auto_ptr<Layout> _layout(new log4cplus::SimpleLayout()); /* step 3: Attach the layout object to the appender */ _append->setLayout( _layout ); /* step 4: Instantiate a logger object */ Logger _logger = Logger::getInstance("test"); /* step 5: Attach the appender object to the logger */ _logger.addAppender(_append); /* log activity */ LOG4CPLUS_DEBUG(_logger, "This is the simple formatted log message..."); ... ...
将打印结果:
DEBUG - This is the simple formatted log message...
2. PatternLayout
是一种有词法分析功能的模式布局器,一提起模式就会想起正则表达式,这里的模式和正则表达式类似,但是远比后者简单,能够对预定义的标识符(称为conversion specifiers)进行解析,转换成特定格式输出。以下代码片段演示了如何使用PatternLayout:
... ... /* step 1: Instantiate an appender object */ SharedObjectPtr<Appender> _append (new ConsoleAppender()); _append->setName("append for test"); /* step 2: Instantiate a layout object */ std::string pattern = "%d{%m/%d/%y %H:%M:%S} - %m [%l]%n"; std::auto_ptr<Layout> _layout(new PatternLayout(pattern)); /* step 3: Attach the layout object to the appender */ _append->setLayout( _layout ); /* step 4: Instantiate a logger object */ Logger _logger = Logger::getInstance("test_logger.subtest"); /* step 5: Attach the appender object to the logger */ _logger.addAppender(_append); /* log activity */ LOG4CPLUS_DEBUG(_logger, "teststr"); ... ...
TTCCLayout::TTCCLayout(bool use_gmtime = false)以下代码片段演示了如何使用TTCCLayout:
... ... /* step 1: Instantiate an appender object */ SharedObjectPtr<Appender> _append (new ConsoleAppender()); _append->setName("append for test"); /* step 2: Instantiate a layout object */ std::auto_ptr<Layout> _layout(new TTCCLayout()); /* step 3: Attach the layout object to the appender */ _append->setLayout( _layout ); /* step 4: Instantiate a logger object */ Logger _logger = Logger::getInstance("test_logger"); /* step 5: Attach the appender object to the logger */ _logger.addAppender(_append); /* log activity */ LOG4CPLUS_DEBUG(_logger, "teststr"); ... ...输出结果:
... ... /* step 2: Instantiate a layout object */ std::auto_ptr<Layout> _layout(new TTCCLayout(true)); ... ...输出结果:
FileAppender(const log4cplus::tstring& filename, LOG4CPLUS_OPEN_MODE_TYPE mode = LOG4CPLUS_FSTREAM_NAMESPACE::ios::trunc, bool immediateFlush = true);filename : 文件名
log4cplus::RollingFileAppender::RollingFileAppender(const log4cplus::tstring& filename, long maxFileSize, int maxBackupIndex, bool immediateFlush)filename : 文件名
... ... #define LOOP_COUNT 200000 SharedAppenderPtr _append(new RollingFileAppender("Test.log", 5*1024, 5)); _append->setName("file test"); _append->setLayout( std::auto_ptr<layout></layout>(new TTCCLayout()) ); Logger::getRoot().addAppender(_append); Logger root = Logger::getRoot(); Logger test = Logger::getInstance("test"); Logger subTest = Logger::getInstance("test.subtest"); for(int i=0; i { NDCContextCreator _context("loop"); LOG4CPLUS_DEBUG(subTest, "Entering loop #" << i) } ... ...运行结果:
DailyRollingFileAppender::DailyRollingFileAppender(const log4cplus::tstring& filename, DailyRollingFileSchedule schedule, bool immediateFlush, int maxBackupIndex)filename : 文件名
... ... SharedAppenderPtr _append(new DailyRollingFileAppender("Test.log", MINUTELY, true, 5)); _append->setName("file test"); _append->setLayout( std::auto_ptr<layout></layout>(new TTCCLayout()) ); Logger::getRoot().addAppender(_append); Logger root = Logger::getRoot(); Logger test = Logger::getInstance("test"); Logger subTest = Logger::getInstance("test.subtest"); for(int i=0; i { NDCContextCreator _context("loop"); LOG4CPLUS_DEBUG(subTest, "Entering loop #" << i) } ... ...运行结果:
Logger root = Logger::getRoot();用户定义的logger都有一个名字与之对应,比如:
Logger test = Logger::getInstance("test");可以定义该logger的子logger:
Logger subTest = Logger::getInstance("test.subtest");注意Root级别的logger只有通过getRoot方法获取,Logger::getInstance("root")获得的是它的 子对象而已。有了这些具有父子关系的logger之后可分别设置其LogLevel,比如:
root.setLogLevel( ... ); Test.setLogLevel( ... ); subTest.setLogLevel( ... );logger的这种父子关联性会体现在优先级控制方面,log4cplus将输出的log信息按照LogLevel (从低到高)分为:
#include <log4cplus/logger.h> #include <log4cplus/consoleappender.h> #include <log4cplus/loglevel.h> #include <iostream> #ifndef _DEBUG #pragma comment(lib, "log4cplus.lib") #else #pragma comment(lib, "log4cplusD.lib") #endif using namespace std; using namespace log4cplus; int main() { SharedAppenderPtr _append(new ConsoleAppender()); _append->setName("test"); Logger::getRoot().addAppender(_append); Logger root = Logger::getRoot(); Logger test = Logger::getInstance("test"); Logger subTest = Logger::getInstance("test.subtest"); LogLevelManager& llm = getLogLevelManager(); cout << endl << "Before Setting, Default LogLevel" << endl; LOG4CPLUS_FATAL(root, "root: " << llm.toString(root.getChainedLogLevel())); LOG4CPLUS_FATAL(root, "test: " << llm.toString(test.getChainedLogLevel())); LOG4CPLUS_FATAL(root, "test.subtest: " << llm.toString(subTest.getChainedLogLevel())); cout << endl << "Setting test.subtest to WARN" << endl; subTest.setLogLevel(WARN_LOG_LEVEL); LOG4CPLUS_FATAL(root, "root: " << llm.toString(root.getChainedLogLevel())); LOG4CPLUS_FATAL(root, "test: " << llm.toString(test.getChainedLogLevel())); LOG4CPLUS_FATAL(root, "test.subtest: " << llm.toString(subTest.getChainedLogLevel())); cout << endl << "Setting test.subtest to TRACE" << endl; test.setLogLevel(TRACE_LOG_LEVEL); LOG4CPLUS_FATAL(root, "root: " << llm.toString(root.getChainedLogLevel())); LOG4CPLUS_FATAL(root, "test: " << llm.toString(test.getChainedLogLevel())); LOG4CPLUS_FATAL(root, "test.subtest: " << llm.toString(subTest.getChainedLogLevel())); cout << endl << "Setting test.subtest to NO_LEVEL" << endl; subTest.setLogLevel(NOT_SET_LOG_LEVEL); LOG4CPLUS_FATAL(root, "root: " << llm.toString(root.getChainedLogLevel())); LOG4CPLUS_FATAL(root, "test: " << llm.toString(test.getChainedLogLevel())); LOG4CPLUS_FATAL(root, "test.subtest: " << llm.toString(subTest.getChainedLogLevel()) << '\n'); cout << "create a logger test_bak, named \"test_\", too. " << endl; Logger test_bak = Logger::getInstance("test"); cout << "Setting test to INFO, so test_bak also be set to INFO" << endl; test.setLogLevel(INFO_LOG_LEVEL); LOG4CPLUS_FATAL(root, "test: " << llm.toString(test.getChainedLogLevel())); LOG4CPLUS_FATAL(root, "test_bak: " << llm.toString(test_bak.getChainedLogLevel())); return 0; }输出结果:
#include <log4cplus/logger.h> #include <log4cplus/consoleappender.h> #include <log4cplus/loglevel.h> #include <iostream> #ifndef _DEBUG #pragma comment(lib, "log4cplus.lib") #else #pragma comment(lib, "log4cplusD.lib") #endif using namespace std; using namespace log4cplus; void ShowMsg(void) { LOG4CPLUS_TRACE(Logger::getRoot(),"info"); LOG4CPLUS_DEBUG(Logger::getRoot(),"info"); LOG4CPLUS_INFO(Logger::getRoot(),"info"); LOG4CPLUS_WARN(Logger::getRoot(),"info"); LOG4CPLUS_ERROR(Logger::getRoot(),"info"); LOG4CPLUS_FATAL(Logger::getRoot(),"info"); } int main() { SharedAppenderPtr _append(new ConsoleAppender()); _append->setName("test"); _append->setLayout(std::auto_ptr<Layout>(new TTCCLayout())); Logger root = Logger::getRoot(); root.addAppender(_append); cout << endl << "all-log allowed" << endl; root.setLogLevel(ALL_LOG_LEVEL); ShowMsg(); cout << endl << "trace-log and above allowed" << endl; root.setLogLevel(TRACE_LOG_LEVEL); ShowMsg(); cout << endl << "debug-log and above allowed" << endl; root.setLogLevel(DEBUG_LOG_LEVEL); ShowMsg(); cout << endl << "info-log and above allowed" << endl; root.setLogLevel(INFO_LOG_LEVEL); ShowMsg(); cout << endl << "warn-log and above allowed" << endl; root.setLogLevel(WARN_LOG_LEVEL); ShowMsg(); cout << endl << "error-log and above allowed" << endl; root.setLogLevel(ERROR_LOG_LEVEL); ShowMsg(); cout << endl << "fatal-log and above allowed" << endl; root.setLogLevel(FATAL_LOG_LEVEL); ShowMsg(); cout << endl << "log disabled" << endl; root.setLogLevel(OFF_LOG_LEVEL); ShowMsg(); return 0; }输出结果:
/* DEBUG_LOG_LEVEL < HELLO_LOG_LEVEL < INFO_LOG_LEVEL */ const LogLevel HELLO_LOG_LEVEL = 15000;然后定义以下宏即可:
/* define MACRO LOG4CPLUS_HELLO */ #define LOG4CPLUS_HELLO(logger, logEvent) \ if(logger.isEnabledFor(HELLO_LOG_LEVEL)) { \ log4cplus::tostringstream _log4cplus_buf; \ _log4cplus_buf << logEvent; \ logger.forcedLog(HELLO_LOG_LEVEL, _log4cplus_buf.str(), __FILE__, __LINE__); \ }不过log4cplus没有提供给用户一个接口来实现LEVEL值与字符串的转换,所以当带格式输出LogLevel字符 串时候会显示"UNKNOWN", 不够理想。比如用TTCCLayout控制输出的结果可能会如下所示:
#define _HELLO_STRING LOG4CPLUS_TEXT("HELLO")然后修改log4cplus::tstring defaultLogLevelToStringMethod(LogLevel ll)函数,增加一个判断:
case HELLO_LOG_LEVEL: return _HELLO_STRING;重新编译log4cplus源代码后生成库文件,再使用时即可实现满意效果。
/*设置方法*/ log4cplus.appender.appenderName=fully.qualified.name.of.appender.class例如(列举了所有可能的Appender,其中SocketAppender后面会讲到):
log4cplus.appender.append_1=log4cplus::ConsoleAppender log4cplus.appender.append_2=log4cplus::FileAppender log4cplus.appender.append_3=log4cplus::RollingFileAppender log4cplus.appender.append_4=log4cplus::DailyRollingFileAppender log4cplus.appender.append_4=log4cplus::SocketAppender(2)设置Filter:
log4cplus.appender.append_1.filters.1=log4cplus::spi::LogLevelMatchFilter log4cplus.appender.append_1.filters.1.LogLevelToMatch=TRACE log4cplus.appender.append_1.filters.1.AcceptOnMatch=true #log4cplus.appender.append_1.filters.2=log4cplus::spi::DenyAllFilter会首先执行filters.2的过滤条件,关闭所有过滤器,然后执行filters.1,仅匹配TRACE信息。
log4cplus.appender.ALL_MSGS.layout=log4cplus::TTCCLayout设置PatternLayout如下所示:
log4cplus.appender.append_1.layout=log4cplus::PatternLayout log4cplus.appender.append_1.layout.ConversionPattern=%d{%m/%d/%y %H:%M:%S,%Q} [%t] %-5p - %m%n2.logger的配置语法
log4cplus.rootLogger=[LogLevel], appenderName, appenderName, ...对于non-root logger来说:
log4cplus.logger.logger_name=[LogLevel|INHERITED], appenderName, appenderName, ...脚本方式使用起来非常简单,只要首先加载配置即可(urconfig.properties是自行定义的配置文件):
PropertyConfigurator::doConfigure("urconfig.properties");下面我们通过例子体会一下log4cplus强大的基于脚本过滤log信息的功能。
/* * urconfig.properties */ // log4cplus.rootLogger=TRACE, ALL_MSGS, TRACE_MSGS, DEBUG_INFO_MSGS, FATAL_MSGS // log4cplus.appender.ALL_MSGS=log4cplus::RollingFileAppender // log4cplus.appender.ALL_MSGS.File=all_msgs.log // log4cplus.appender.ALL_MSGS.layout=log4cplus::TTCCLayout // log4cplus.appender.TRACE_MSGS=log4cplus::RollingFileAppender // log4cplus.appender.TRACE_MSGS.File=trace_msgs.log // log4cplus.appender.TRACE_MSGS.layout=log4cplus::TTCCLayout // log4cplus.appender.TRACE_MSGS.filters.1=log4cplus::spi::LogLevelMatchFilter // log4cplus.appender.TRACE_MSGS.filters.1.LogLevelToMatch=TRACE // log4cplus.appender.TRACE_MSGS.filters.1.AcceptOnMatch=true // log4cplus.appender.TRACE_MSGS.filters.2=log4cplus::spi::DenyAllFilter // log4cplus.appender.DEBUG_INFO_MSGS=log4cplus::RollingFileAppender // log4cplus.appender.DEBUG_INFO_MSGS.File=debug_info_msgs.log // log4cplus.appender.DEBUG_INFO_MSGS.layout=log4cplus::TTCCLayout // log4cplus.appender.DEBUG_INFO_MSGS.filters.1=log4cplus::spi::LogLevelRangeFilter // log4cplus.appender.DEBUG_INFO_MSGS.filters.1.LogLevelMin=DEBUG // log4cplus.appender.DEBUG_INFO_MSGS.filters.1.LogLevelMax=INFO // log4cplus.appender.DEBUG_INFO_MSGS.filters.1.AcceptOnMatch=true // log4cplus.appender.DEBUG_INFO_MSGS.filters.2=log4cplus::spi::DenyAllFilter // log4cplus.appender.FATAL_MSGS=log4cplus::RollingFileAppender // log4cplus.appender.FATAL_MSGS.File=fatal_msgs.log // log4cplus.appender.FATAL_MSGS.layout=log4cplus::TTCCLayout // log4cplus.appender.FATAL_MSGS.filters.1=log4cplus::spi::StringMatchFilter // log4cplus.appender.FATAL_MSGS.filters.1.StringToMatch=FATAL // log4cplus.appender.FATAL_MSGS.filters.1.AcceptOnMatch=true // log4cplus.appender.FATAL_MSGS.filters.2=log4cplus::spi::DenyAllFilter /* * main.cpp */ #include <log4cplus/logger.h> #include <log4cplus/configurator.h> #include <log4cplus/helpers/stringhelper.h> #ifndef _DEBUG #pragma comment(lib, "log4cplus.lib") #else #pragma comment(lib, "log4cplusD.lib") #endif using namespace log4cplus; static Logger logger = Logger::getInstance("log"); void printDebug() { LOG4CPLUS_TRACE_METHOD(logger, "::printDebug()"); LOG4CPLUS_DEBUG(logger, "This is a DEBUG message"); LOG4CPLUS_INFO(logger, "This is a INFO message"); LOG4CPLUS_WARN(logger, "This is a WARN message"); LOG4CPLUS_ERROR(logger, "This is a ERROR message"); LOG4CPLUS_FATAL(logger, "This is a FATAL message"); } int main() { Logger root = Logger::getRoot(); PropertyConfigurator::doConfigure("urconfig.properties"); printDebug(); return 0; }运行结果:
(六)
log4cplus在很多方面做的都很出色,但是使用过程有些地方感觉不爽。在继续吹捧之前我先把不爽之处稍微提一提,然后继续介绍关于线程和套接字的知识。
### 一些可以改进之处 ###
1. 用户自定义LogLevel的实现机制不够开放
在第五篇中曾经介绍过如何实现用户自行定义LogLevel,为了实现比较理想的效果,甚至还需要改log4cplus的源代码。
2. 生成Logger对象的机制可以改进
我在使用时候,经常需要在不同的文件、函数中操作同一个logger,虽然log4cplus实现了树状存储以及根据名称生成Logger,却没有充分利用这样的特点确保同一个名称对应的logger对象的唯一性,比如以下代码:
... ... Logger logger1 = Logger::getInstance("test"); Logger logger2 = Logger::getInstance("test"); Logger * plogger1 = &logger1; Logger * plogger2 = &logger2; std::cout << "plogger1: " << plogger1 << std::endl << "plogger2: " << plogger2 << std::endl; ... ...运行结果:
#include <iostream> #include <string> #include <log4cplus/logger.h> #ifndef _DEBUG #pragma comment(lib, "log4cplus.lib") #else #pragma comment(lib, "log4cplusD.lib") #endif /* forward declaration */ class Logger; class LoggerContainer { public: ~LoggerContainer(); Logger * getinstance(const std::string & strLogger); private: typedef std::map<std::string, Logger*> LoggerMap; LoggerMap loggerPtrs; }; class Logger { public: Logger() {std::cout << "ctor of Logger " << std::endl; } ~Logger() {std::cout << "dtor of Logger " << std::endl; } static Logger * getInstance( const std::string & strLogger) { static LoggerContainer defaultLoggerContainer; return defaultLoggerContainer.getinstance(strLogger); } }; LoggerContainer::~LoggerContainer() { /* release all ptr in LoggerMap */ LoggerMap::iterator itr = loggerPtrs.begin(); for( ; itr != loggerPtrs.end(); ++itr ) { delete (*itr).second; } } Logger * LoggerContainer::getinstance(const std::string & strLogger) { LoggerMap::iterator itr = loggerPtrs.find(strLogger); if(itr != loggerPtrs.end()) { /* logger exist, just return it */ return (*itr).second; } else { /* return a new logger */ Logger * plogger = new Logger(); loggerPtrs.insert(std::make_pair(strLogger, plogger)); return plogger; } } int main() { Logger * plogger1 = Logger::getInstance("test"); Logger * plogger2 = Logger::getInstance("test"); std::cout << "plogger1: " << plogger1 << std::endl << "plogger2: " << plogger2 << std::endl; return 0; }
linux pthread # define LOG4CPLUS_THREAD_LOCAL_TYPE pthread_key_t* # define LOG4CPLUS_THREAD_LOCAL_INIT ::log4cplus::thread::createPthreadKey() # define LOG4CPLUS_GET_THREAD_LOCAL_VALUE( key ) pthread_getspecific(*key) # define LOG4CPLUS_SET_THREAD_LOCAL_VALUE( key, value ) pthread_setspecific(*key, value) # define LOG4CPLUS_THREAD_LOCAL_CLEANUP( key ) pthread_key_delete(*key) win32 # define LOG4CPLUS_THREAD_LOCAL_TYPE DWORD # define LOG4CPLUS_THREAD_LOCAL_INIT TlsAlloc() # define LOG4CPLUS_GET_THREAD_LOCAL_VALUE( key ) TlsGetValue(key) # define LOG4CPLUS_SET_THREAD_LOCAL_VALUE( key, value ) \ TlsSetValue(key, static_cast<lpvoid></lpvoid>(value)) # define LOG4CPLUS_THREAD_LOCAL_CLEANUP( key ) TlsFree(key)
NDC& ndc = log4cplus::getNDC(); ndc.push("ur ndc string"); LOG4CPLUS_DEBUG(logger, "this is a NDC test"); ... ... ndc.pop(); ... ... LOG4CPLUS_DEBUG(logger, "There should be no NDC..."); ndc.remove();当设定输出格式(Layout)为TTCCLayout时,输出如下:
... ... std::string pattern = "NDC:[%x] - %m %n"; std::auto_ptr<layout></layout> _layout(new PatternLayout(pattern)); ... ... LOG4CPLUS_DEBUG(_logger, "This is the FIRST log message...") NDC& ndc = log4cplus::getNDC(); ndc.push("ur ndc string"); LOG4CPLUS_WARN(_logger, "This is the SECOND log message...") ndc.pop(); ndc.remove(); ... ...输出如下:
NDCContextCreator _first_ndc("ur ndc string"); LOG4CPLUS_DEBUG(logger, "this is a NDC test")不必显式地调用push/pop了,而且当出现异常时,能够确保push与pop的调用是匹配的。
### 线程 ###
线程是log4cplus中的副产品, 而且仅作了最基本的实现,使用起来也异常简单,只要且必须要在派生类中重载run函数即可:
class TestThread : public AbstractThread { public: virtual void run(); }; void TestThread::run() { /* do sth. */ ... ... }
void log4cplus::thread::yield() { #if defined(LOG4CPLUS_USE_PTHREADS) ::sched_yield(); #elif defined(LOG4CPLUS_USE_WIN32_THREADS) ::Sleep(0); #endif }
/* 定义一个SocketAppender类型的挂接器 */ SharedAppenderPtr _append(new SocketAppender(host, 8888, "ServerName")); /* 把_append加入到logger中 */ Logger::getRoot().addAppender(_append); /* SocketAppender类型不需要Layout, 直接调用宏就可以将信息发往loggerServer了 */ LOG4CPLUS_INFO(Logger::getRoot(), "This is a test: ")
... ... SocketBuffer buffer = convertToBuffer(event, serverName); SocketBuffer msgBuffer(LOG4CPLUS_MAX_MESSAGE_SIZE); msgBuffer.appendSize_t(buffer.getSize()); msgBuffer.appendBuffer(buffer); ... ...
/* 定义一个ServerSocket */ ServerSocket serverSocket(port); /* 调用accept函数创建一个新的socket与客户端连接 */ Socket sock = serverSocket.accept();
SocketBuffer msgSizeBuffer(sizeof(unsigned int)); if(!clientsock.read(msgSizeBuffer)) { return; } unsigned int msgSize = msgSizeBuffer.readInt(); SocketBuffer buffer(msgSize); if(!clientsock.read(buffer)) { return; }
spi::InternalLoggingEvent event = readFromBuffer(buffer);
Logger logger = Logger::getInstance(event.getLoggerName()); logger.callAppenders(event);
http://log4cplus.sourceforge.net/codeexamples.html
里面自带的三个例程
1. Hello World Example
#include <log4cplus/logger.h> #include <log4cplus/configurator.h> #include <iomanip> #ifndef _DEBUG #pragma comment(lib, "log4cplus.lib") #else #pragma comment(lib, "log4cplusD.lib") #endif using namespace log4cplus; int main() { BasicConfigurator config; config.configure(); Logger logger = Logger::getInstance("main"); LOG4CPLUS_WARN(logger, "Hello, World!"); return 0; }
#include <log4cplus/logger.h> #include <log4cplus/configurator.h> #include <iomanip> #ifndef _DEBUG #pragma comment(lib, "log4cplus.lib") #else #pragma comment(lib, "log4cplusD.lib") #endif using namespace std; using namespace log4cplus; int main() { BasicConfigurator config; config.configure(); Logger logger = Logger::getInstance("logger"); LOG4CPLUS_WARN(logger, "This is" << " a reall" << "y long message." << endl << "Just testing it out" << endl << "What do you think?"); LOG4CPLUS_WARN(logger, "This is a bool: " << true); LOG4CPLUS_WARN(logger, "This is a char: " << 'x'); LOG4CPLUS_WARN(logger, "This is a short: " << (short)-100); LOG4CPLUS_WARN(logger, "This is a unsigned short: " << (unsigned short)100); LOG4CPLUS_WARN(logger, "This is a int: " << (int)1000); LOG4CPLUS_WARN(logger, "This is a unsigned int: " << (unsigned int)1000); LOG4CPLUS_WARN(logger, "This is a long(hex): " << hex << (long)100000000); LOG4CPLUS_WARN(logger, "This is a unsigned long: " << (unsigned long)100000000); LOG4CPLUS_WARN(logger, "This is a float: " << (float)1.2345); LOG4CPLUS_WARN(logger, "This is a double: " << setprecision(15) << (double)1.2345234234); LOG4CPLUS_WARN(logger, "This is a long double: " << setprecision(15) << (long double)123452342342.342); return 0; }
#include <log4cplus/logger.h> #include <log4cplus/configurator.h> #include <iostream> #ifndef _DEBUG #pragma comment(lib, "log4cplus.lib") #else #pragma comment(lib, "log4cplusD.lib") #endif using namespace std; using namespace log4cplus; Logger logger = Logger::getInstance("main"); void printMessages() { LOG4CPLUS_TRACE(logger, "printMessages()"); LOG4CPLUS_DEBUG(logger, "This is a DEBUG message"); LOG4CPLUS_INFO(logger, "This is a INFO message"); LOG4CPLUS_WARN(logger, "This is a WARN message"); LOG4CPLUS_ERROR(logger, "This is a ERROR message"); LOG4CPLUS_FATAL(logger, "This is a FATAL message"); } int main() { BasicConfigurator config; config.configure(); logger.setLogLevel(TRACE_LOG_LEVEL); cout << "*** calling printMessages() with TRACE set: ***" << endl; printMessages(); logger.setLogLevel(DEBUG_LOG_LEVEL); cout << "\n*** calling printMessages() with DEBUG set: ***" << endl; printMessages(); logger.setLogLevel(INFO_LOG_LEVEL); cout << "\n*** calling printMessages() with INFO set: ***" << endl; printMessages(); logger.setLogLevel(WARN_LOG_LEVEL); cout << "\n*** calling printMessages() with WARN set: ***" << endl; printMessages(); logger.setLogLevel(ERROR_LOG_LEVEL); cout << "\n*** calling printMessages() with ERROR set: ***" << endl; printMessages(); logger.setLogLevel(FATAL_LOG_LEVEL); cout << "\n*** calling printMessages() with FATAL set: ***" << endl; printMessages(); return 0; }