1. Log4cplus简单介绍
log4cplus是C++编写的开源的日志系统,前身是java编写的log4j系统。受Apache Software License保护。作者是Tad E. Smith。
log4cplus具有线程安全、灵活、以及多粒度控制的特点。通过将日志划分优先级使其能够面向程序调试、执行、測试、和维护等全生命周期。
你能够选择将日志输出到屏幕、文件、NT event log、甚至是远程server;通过指定策略对日志进行定期备份等等。
2. 安装方法
2.1. linux
1- 解压: gzip -cd log4cplus-x.x.x.tar.gz | tar -xf -
2- 进入log4cplus根文件夹: cd log4cplus-x.x.x
3- 产生Makefile: ./configure --prefix=/where/to/install -enable-threads=no
假设须要指定安装路径可使用--prefix參数, 否则将缺省安装到/usr/local文件夹下。另外,假设须要单线程版本号可通过參数-enable-threads=no指定, 否则默认将安装多线程版本号。
对于HP-UNIX平台用户, 因为aCC编译器选项兼容性问题,请另外增加參数CXXFLAGS=”-AA -w”(单线程版本号)或CXXFLAGS=”-AA –mt -w”(多线程版本号)。
4- 创建: make
对于HP-UNIX用户,因为aCC编译器不包括-Wall选项来显示全部警告,创建时将导致无效的-W參数错误,请改动/log4cplus-x.x.x/src文件夹下的Makefile。将AM_CPPFLAGS = -Wall 行的-Wall选项删除或凝视掉。
此外,某些HP-UNIX平台的套接字连接接受函数accept()第三个參数要求为int*,而在socket-unix.cxx源文件153行实现中实际传入的是socklen_t*类型,平台并不支持,也将导致编译错误。
解决方法是将源码该行中的传入參数强制转换为int*类型就可以。
注意AIX和Linux平台眼下并没有上述两处创建错误。
对于AIX平台用户请保证创建时使用的编译器是xlC而不是g++。否则将导致log4cplus脚本配置功能执行时产生段异常,生成core文件。
有鉴于此。也请保证HP-UNIX用户尽量使用aCC编译器进行创建。
5- 创建/log4cplus/tests文件夹下的測试用例: make check
6- 安装: make install
成功安装后将在/usr/local文件夹或指定的文件夹下创建include和lib两个子文件夹及对应文件。当中include文件夹包括头文件,lib文件夹包括终于打包生成的静态和动态库。在动态连接log4cplus库时请使用-llog4cplus选项。
2.2. win
3. 主要类说明
类名 |
说明 |
Filter |
过滤器,过滤输出消息。 过滤器,解决哪些信息须要输出的问题,比方DEBUG,WARR,INFO等的输出控制 |
Layout |
布局器。控制输出消息的格式。格式化输出信息,攻克了怎样输出的问题。 |
Appender |
挂接器,与布局器和过滤器紧密配合,将特定格式的消息过滤后输出到所挂接的设备终端如屏幕,文件等等)。 接收日志的各个设备,如控制台、文件、网络等。 攻克了输出到哪里去的问题 |
Logger |
记录器。保存并跟踪对象日志信息变更的实体,当你须要对一个对象进行记录时。就须要生成一个logger。 日志模块,程序中唯一一个必须得使用的模块,攻克了在哪里使用日志的问题。 |
Hierarchy |
分类器,层次化的树型结构,用于对被记录信息的分类,层次中每个节点维护一个logger的全部信息。
|
LogLevel |
优先权,包含TRACE, DEBUG, INFO, WARNING, ERROR, FATAL。
|
4. 基本使用
4.1. 基本步骤
使用log4cplus有六个基本步骤:
l 实例化一个封装了输出介质的appender对象;
l 实例化一个封装了输出格式的layout对象;
l 将layout对象绑定(attach)到appender对象;如省略此步骤。简单布局器SimpleLayout(參见5.1小节)对象会绑定到logger。
l 实例化一个封装了日志输出logger对象,并调用其静态函数getInstance()获得实例,log4cplus::Logger::getInstance("logger_name")。
l 将appender对象绑定(attach)到logger对象;
l 设置logger的优先级。如省略此步骤,各种有限级的日志都将被输出。
4.2. 使用演示样例
以下通过一些样例来了解log4cplus的基本使用。
4.2.1. 例1-标准使用
/* *标准使用。严格实现步骤1-6。 */ #include <log4cplus/logger.h> #include <log4cplus/consoleappender.h> #include <log4cplus/layout.h>
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; } |
输出结果:
10/14/04 09:06:24 - This is the FIRST log message... [main.cpp:31] 10/14/04 09:06:25 - This is the SECOND log message... [main.cpp:33] |
4.2.2. 例2-简洁使用
/* *简洁使用,仅实现步骤1、4、5。
*/ #include <log4cplus/logger.h> #include <log4cplus/consoleappender.h>
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; } |
输出结果:
DEBUG - This is the FIRST log message... WARN - This is the SECOND log message... |
4.2.3. 例3-输出日志到控制台
/* *iostream模式,appender输出到控制台。 */ #include<log4cplus/logger.h> #include<log4cplus/consoleappender.h> #include<iomanip> usingnamespacelog4cplus;
int main() { /*step1:Instantiateanappenderobject*/ SharedAppenderPtr_append(new ConsoleAppender()); _append->setName("appendtest");
/*step4:Instantiatealoggerobject*/ Logger_logger = Logger::getInstance("test");
/*step5:Attachtheappenderobjecttothelogger*/ _logger.addAppender(_append);
/*logactivity*/ LOG4CPLUS_TRACE(_logger, "Thisis" << "justat" << "est." << std::endl) LOG4CPLUS_DEBUG(_logger, "Thisisabool:" << true) LOG4CPLUS_INFO(_logger, "Thisisachar:" << 'x') LOG4CPLUS_WARN(_logger, "Thisisaint:" << 1000) LOG4CPLUS_ERROR(_logger, "Thisisalong(hex):" << std::hex << 100000000) LOG4CPLUS_FATAL(_logger, "Thisisadouble:" << std::setprecision(15) << 1.2345234234)
return0; } |
输出结果:
DEBUG-Thisisabool:1 INFO-Thisisachar:x WARN-Thisisaint:1000 ERROR-Thisisalong(hex):5f5e100 FATAL-Thisisadouble:1.2345234234 |
4.2.4. 例4-输出日志到文件
/* *文件模式。appender输出到文件。
*/ #include<log4cplus/logger.h> #include<log4cplus/fileappender.h> usingnamespacelog4cplus;
int main() { /*step1:Instantiateanappenderobject*/ SharedAppenderPtr_append(new FileAppender("Test.log")); _append->setName("filelogtest");
/*step4:Instantiatealoggerobject*/ Logger_logger = Logger::getInstance("test.subtestof_filelog");
/*step5:Attachtheappenderobjecttothelogger*/ _logger.addAppender(_append);
/*logactivity*/ for (int i = 0; i < 5; ++i) { LOG4CPLUS_DEBUG(_logger, "Enteringloop#" << i << "Endline#") }
return0; } |
输出结果(Test.log文件):
DEBUG-Enteringloop#0Endline# DEBUG-Enteringloop#1Endline# DEBUG-Enteringloop#2Endline# DEBUG-Enteringloop#3Endline# DEBUG-Enteringloop#4Endline# |
4.2.5. 例5-使用loglog输出日志
LogLog类实现了debug, warn, error 函数用于logcplus执行时显示log4cplus自身的调试、警告或错误信息。是对标准输出的简单封装,它也能够用来进行简单的日志输出。
LogLog 同一时候提供了两个方法来进一步控制所输出的信息。当中setInternalDebugging()方法用来控制是否屏蔽输出信息中的调试信息。当输入參数为false则屏蔽,缺省设置为false。 setQuietMode()方法用来控制是否屏蔽全部输出信息。当输入參数为true则屏蔽。缺省设置为false。
/* 通过loglog来控制输出调试、警告或错误信息,appender输出到屏幕。 */ #include <iostream> #include <log4cplus/helpers/loglog.h> 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() { 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; } |
输出结果:
EnteringprintMsgs()... log4cplus:WARNThisisaWarning... log4cplus:ERRORThisisaError... ExitingprintMsgs()... Turningondebug... EnteringprintMsgs()... log4cplus:ThisisaDebugstatement... log4cplus:WARNThisisaWarning... log4cplus:ERRORThisisaError... ExitingprintMsgs()... Turningonquietmode... EnteringprintMsgs()... ExitingprintMsgs()... |
注意输出信息中总是包括"log4cplus:"前缀。假设须要定制使其使用其它的前缀请參见9.2小节。
4.3. 日志输出宏
log4cplus在头文件loggingmacros.h中提供了下面的日志输出宏:
LOG4CPLUS_TRACE_METHOD(logger,logEvent)
LOG4CPLUS_TRACE(logger,logEvent) LOG4CPLUS_TRACE_STR(logger,logEvent)
LOG4CPLUS_DEBUG(logger,logEvent) LOG4CPLUS_DEBUG_STR(logger,logEvent)
LOG4CPLUS_INFO(logger,logEvent) LOG4CPLUS_INFO_STR(logger,logEvent)
LOG4CPLUS_WARN(logger,logEvent) LOG4CPLUS_WARN_STR(logger,logEvent)
LOG4CPLUS_ERROR(logger,logEvent) LOG4CPLUS_ERROR_STR(logger,logEvent)
LOG4CPLUS_FATAL(logger,logEvent) LOG4CPLUS_FATAL_STR(logger,logEvent) |
当中logger 为Logger实例名称,logEvent为日志内容。因为log4cplus选用C++的流机制进行日志输出,因此为了区分包括<<运算符和不包括<<运算符的日志内容。分别提供了LOG4CPLUS_XXXX和LOG4CPLUS_XXXX_STR两种日志输出宏。 另外,日志输出宏LOG4CPLUS_TRACE_METHOD主要用来跟踪方法的调用轨迹。
5. 输出格式控制(layout)
log4cplus通过布局器(Layouts)来控制输出的格式,log4cplus提供了三种类型的Layouts,各自是SimpleLayout、PatternLayout、和TTCCLayout。
5.1. SimpleLayout
一种简单格式的布局器。在输出的原始信息之前加上LogLevel和一个"-",假设初始化时没有将布局器附加到挂接器,则默认使用SimpleLayout。
下面代码片段演示了怎样使用SimpleLayout。
... ... /* step 1: Instantiate an appender object */ SharedObjectPtr _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... |
5.2. PatternLayout
一种有词法分析功能的模式布局器,类似于C语言的printf()函数,可以对提前定义的转换标识符(conversion specifiers)进行解析。转换成特定格式输出。
下面代码片段演示了怎样使用PatternLayout。
... ... /* step 1: Instantiate an appender object */ SharedObjectPtr _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") ... ... |
输出结果:
10/16/04 18:51:25 - teststr [main.cpp:51] |
5.2.1. 转换标识符
PatterLayout支持的转换标识符主要包含:
(1)"%%",转义为%, 即,std::string pattern = "%%" 时输出"%"。
(2)"%c",输出logger名称,比方std::string pattern ="%c" 时输出: "test_logger.subtest"。 也能够控制logger名称的显示层次,比方"%c{1}"时输出"test_logger",当中数字表示层次。
(3)"%D",显示本地时间,当std::string pattern ="%D" 时输出:"2004-10-16 18:55:45"。%d显示标准时间,所以当std::string pattern ="%d" 时输出"2004-10-16 10:55:45" (由于北京时间位于东8区,差8个小时)。
能够通过%d{...}定义更具体的显示格式,比方%d{%H:%M:%s}表示要显示小时:分钟:秒。大括号里可显示的提前定义标识符例如以下:
%a -- 表示礼拜几。英文缩写形式。比方"Fri"
%A -- 表示礼拜几,比方"Friday"
%b -- 表示几月份,英文缩写形式。比方"Oct"
%B -- 表示几月份。"October"
%c -- 标准的日期+时间格式,如 "Sat Oct 16 18:56:19 2004"
%d -- 表示今天是这个月的几号(1-31)"16"
%H -- 表示当前时刻是几时(0-23),如 "18"
%I -- 表示当前时刻是几时(1-12)。如 "6"
%j -- 表示今天是哪一天(1-366),如 "290"
%m -- 表示本月是哪一月(1-12)。如 "10"
%M -- 表示当前时刻是哪一分钟(0-59),如 "59"
%p -- 表示如今是上午还是下午。 AM or PM
%q -- 表示当前时刻中毫秒部分(0-999),如 "237"
%Q -- 表示当前时刻中带小数的毫秒部分(0-999.999),如 "430.732"
%S -- 表示当前时刻的多少秒(0-59)。如 "32"
%U -- 表示本周是今年的第几个礼拜。以周日为第一天開始计算(0-53),如 "41"
%w -- 表示礼拜几。(0-6, 礼拜天为0),如 "6"
%W -- 表示本周是今年的第几个礼拜,以周一为第一天開始计算(0-53),如 "41"
%x -- 标准的日期格式,如 "10/16/04"
%X -- 标准的时间格式,如 "19:02:34"
%y -- 两位数的年份(0-99),如 "04"
%Y -- 四位数的年份,如 "2004"
%Z -- 时区名,比方 "GMT"
(4)"%F",输出当前记录器所在的文件名。比方std::string pattern ="%F" 时输出: "main.cpp"。
(5)"%L"。输出当前记录器所在的文件行号,比方std::string pattern ="%L" 时输出: "51"
(6)"%l",输出当前记录器所在的文件名和行号。比方std::string pattern ="%l" 时输出"main.cpp:51"。
(7)"%m",输出原始信息,比方std::string pattern ="%m" 时输出: "teststr",即上述代码中LOG4CPLUS_DEBUG的第二个參数,这样的实现机制能够确保原始信息被嵌入到带格式的信息中。
(8)"%n"。换行符,没什么好解释的。
(9)"%p"。输出LogLevel,比方std::string pattern ="%p" 时输出: "DEBUG"。
(10)"%t",输出记录器所在的线程ID,比方std::string pattern ="%t" 时输出: "1075298944"。
(11)"%x",嵌套诊断上下文NDC (nested diagnostic context) 输出,从堆栈中弹出上下文信息,NDC能够用对不同源的log信息(同一时候地)交叉输出进行区分,关于NDC方面的具体介绍会在下文中提到。
(12)格式对齐。比方std::string pattern ="%-10m"时表示左对齐,宽度是10,此时会输出"teststr ",当然其他的控制字符也能够同样的方式来使用。比方"%-12d","%-5p"等等。
5.3. TTCCLayout
是在PatternLayout基础上发展的一种缺省的带格式输出的布局器,其格式由时间。线程ID,Logger和NDC 组成(consists of time, thread, Logger and nested diagnostic context information, hence the name)。因而得名,关于NDC请參见6.4小节。
下面代码片段演示了怎样使用TTCCLayout。
...... /*step1:Instantiateanappenderobject*/ SharedObjectPtr_append(new ConsoleAppender()); _append->setName("appendfortest");
/*step2:Instantiatealayoutobject*/ std::auto_ptr_layout(new TTCCLayout());
/*step3:Attachthelayoutobjecttotheappender*/ _append->setLayout(_layout);
/*step4:Instantiatealoggerobject*/ Logger_logger=Logger::getInstance("test_logger");
/*step5:Attachtheappenderobjecttothelogger*/ _logger.addAppender(_append);
/*logactivity*/ LOG4CPLUS_DEBUG(_logger,"teststr") ...... |
输出结果:
10-16-04 19:08:27,501 [1075298944] DEBUG test_logger <> - teststr |
TTCCLayout在构造时,有机会选择显示本地时间或GMT时间,缺省是依照本地时间显示:TTCCLayout::TTCCLayout(bool use_gmtime = false)。
假设须要构造TTCCLayout对象时选择GMT时间格式,则使用方式例如以下代码片断所看到的。
... ... /* step 2: Instantiate a layout object */ std::auto_ptr _layout(new TTCCLayout(true)); ... ... |
输出结果:
10-16-04 11:12:47,678 [1075298944] DEBUG test_logger <> - teststr |
6. 输出重定向(appender)
6.1. 重定向到控制台
log4cplus默认将输出到控制台,提供ConsoleAppender用于操作。
演示样例代码请參见4.2.1、4.2.2或4.2.3小节,这里不再赘述。
6.2. 重定向到文件
log4cplus提供了三个类用于文件操作,它们是FileAppender类、RollingFileAppender类、DailyRollingFileAppender类。
6.2.1. FileAppender
实现了主要的文件操作功能,构造函数例如以下:
FileAppender ::FileAppender(const log4cplus::tstring& filename,
LOG4CPLUS_OPEN_MODE_TYPE mode =
LOG4CPLUS_FSTREAM_NAMESPACE::ios::trunc,
bool immediateFlush = true);
filename : 文件名称
mode : 文件类型。可选择的文件类型包含app、ate、binary、in、out、trunc,由于实际上仅仅是对stl的一个简单包装,这里就不多讲了。缺省是trunc,表示将先前文件删除。
immediateFlush : 缓冲刷新标志,假设为true表示每向文件写一条记录就刷新一次缓存,否则直到FileAppender被关闭或文件缓存已满才更新文件。通常是要设置true的。比方你往文件写的过程中出现了错误(如程序非正常退出),即使文件没有正常关闭也能够保证程序终止时刻之前的全部记录都会被正常保存。
FileAppender类的使用情况请參考4.2.5小节。这里不再赘述。
6.2.2. RollingFileAppender
实现能够滚动转储的文件操作功能。构造函数例如以下:
RollingFileAppender::RollingFileAppender(const log4cplus::tstring& filename,
long maxFileSize,
int maxBackupIndex,
bool immediateFlush)
filename : 文件名称
maxFileSize : 文件的最大尺寸
maxBackupIndex : 最大记录文件数
immediateFlush : 缓冲刷新标志
RollingFileAppender类能够依据你预先设定的大小来决定是否转储,当超过该大小,兴许log信息会另存到新文件里,除了定义每一个记录文件的大小之外,你还要确定在RollingFileAppender类对象构造时最多须要多少个这种记录文件(maxBackupIndex+1),当存储的文件数目超过maxBackupIndex+1时。会删除最早生成的文件,保证整个文件数目等于maxBackupIndex+1。然后继续记录,比方下面代码片段:
... ... #define LOOP_COUNT 200000 SharedAppenderPtr _append(new RollingFileAppender("Test.log", 5*1024, 5)); _append->setName("file test"); _append->setLayout( std::auto_ptr(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 < LOOP_COUNT; ++i) { NDCContextCreator _context("loop"); LOG4CPLUS_DEBUG(subTest, "Entering loop #" << i) } ... ... |
输出结果:
执行后会产生6个输出文件,Test.log、Test.log.1、Test.log.2、Test.log.3、Test.log.4、Test.log.5当中Test.log存放着最新写入的信息。而最后一个文件里并不包括第一个写入信息,说明已经被不断更新了。
须要指出的是,这里除了Test.log之外,每一个文件的大小都是200K,而不是我们想像中的5K。这是由于log4cplus中隐含定义了文件的最小尺寸是200K,仅仅有大于200K的设置才生效,<= 200k的设置都会被觉得是200K。
6.2.3. DailyRollingFileAppender
实现依据频度来决定是否转储的文件转储功能,构造函数例如以下:
DailyRollingFileAppender::DailyRollingFileAppender(const log4cplus::tstring& filename,
DailyRollingFileSchedule schedule,
bool immediateFlush,
int maxBackupIndex)
filename : 文件名称
schedule : 存储频度
immediateFlush : 缓冲刷新标志
maxBackupIndex : 最大记录文件数
DailyRollingFileAppender类能够依据你预先设定的频度来决定是否转储,当超过该频度。兴许log信息会另存到新文件里,这里的频度包含:MONTHLY(每月)、WEEKLY(每周)、DAILY(每日)、TWICE_DAILY(每两天)、HOURLY(每时)、MINUTELY(每分)。
maxBackupIndex的含义同上所述,比方下面代码片段:
... ... SharedAppenderPtr _append(new DailyRollingFileAppender("Test.log", MINUTELY, true, 5)); _append->setName("file test"); _append->setLayout( std::auto_ptr(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 < LOOP_COUNT; ++i) { NDCContextCreator _context("loop"); LOG4CPLUS_DEBUG(subTest, "Entering loop #" << i) } ... ... |
输出结果:
执行后会以分钟为单位,分别生成名为Test.log.2004-10-17-03-03、Test.log.2004-10-17-03-04和Test.log.2004-10-17-03-05这种文件。
须要指出的是这里的"频度"并非你写入文件的速度,事实上是否转储的标准并不依赖你写入文件的速度,而是依赖于写入的那一时刻是否满足了频度条件。即是否超过了以分钟、小时、周、月为单位的时间刻度,假设超过了就另存。
6.3. 重定向到远程server
log4cplus提供了SocketAppender。实现了C/S方式的日志记录,用于支持重定向到远程server。
6.3.1. client程序须要做的工作
(1) 定义一个SocketAppender类型的挂接器
SharedAppenderPtr _append(new SocketAppender(host, 8888, "ServerName"));
(2) 把该挂接器增加到logger中
Logger::getRoot().addAppender(_append);
(3) SocketAppender类型不须要Layout, 直接调用宏就能够将信息发往loggerServer了LOG4CPLUS_INFO(Logger::getRoot(), "This is a test: ")
注意这里对宏的调用事实上是调用了SocketAppender::append(),里面有一个传输数据约定。即先发送一个兴许数据的总长度,然后再发送实际的数据:
... ... SocketBuffer buffer = convertToBuffer(event, serverName); SocketBuffer msgBuffer(LOG4CPLUS_MAX_MESSAGE_SIZE); msgBuffer.appendSize_t(buffer.getSize()); msgBuffer.appendBuffer(buffer); ... ... |
6.3.2. server端程序须要做的工作
(1) 定义一个ServerSocket
ServerSocket serverSocket(port);
(2) 调用accept函数创建一个新的socket与client连接
Socket sock = serverSocket.accept();
(3) 此后就可以用该sock进行数据read/write了,形如(完整代码见6.3.3小节):
SocketBuffer msgSizeBuffer(sizeof(unsigned int));
if(!clientsock.read(msgSizeBuffer)){
return;
}
unsigned int msgSize = msgSizeBuffer.readInt();
SocketBuffer buffer(msgSize);
if(!clientsock.read(buffer)){
return;
}
(4) 为了将读到的数据正常显示出来,须要将SocketBuffer存放的内容转换成InternalLoggingEvent格式:
log4cplus::spi::InternalLoggingEvent event = readFromBuffer(buffer);
然后输出:
Logger logger = Logger::getInstance(event.getLoggerName());
logger.callAppenders(event);
注意read/write是依照堵塞方式实现的,意味着对其调用直到满足了所接收或发送的个数才返回。
6.3.3. 例6-重定向到远程server
下面是server端代码。
#include <log4cplus/config.h> #include <log4cplus/configurator.h> #include <log4cplus/consoleappender.h> #include <log4cplus/socketappender.h> #include <log4cplus/helpers/loglog.h> #include <log4cplus/helpers/socket.h> #include <log4cplus/helpers/threads.h> #include <log4cplus/spi/loggerimpl.h> #include <log4cplus/spi/loggingevent.h> #include <iostream> using namespace std; using namespace log4cplus; using namespace log4cplus::helpers; using namespace log4cplus::thread;
namespace loggingserver { class ClientThread : public AbstractThread { public: ClientThread(Socket clientsock) : clientsock(clientsock) { cout << "Received a client connection!!!!" << endl; }
~ClientThread() { cout << "Client connection closed." << endl; }
virtual void run();
private: Socket clientsock; };
}
int main(int argc, char** argv) { if (argc < 3) { cout << "Usage: port config_file" << endl; return 1; } int port = atoi(argv[1]); tstring configFile = LOG4CPLUS_C_STR_TO_TSTRING(argv[2]);
PropertyConfigurator config(configFile); config.configure();
ServerSocket serverSocket(port); while (1) { loggingserver::ClientThread* thr = newloggingserver::ClientThread(serverSocket.accept()); thr->start(); }
return 0; }
////////////////////////////////////////////////////////////////////////////// // loggingserver::ClientThread implementation ////////////////////////////////////////////////////////////////////////////// void loggingserver::ClientThread::run() { while (1) { if (!clientsock.isOpen()) { return; } 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); } } |
下面是client代码。
#include <log4cplus/logger.h> #include <log4cplus/socketappender.h> #include <log4cplus/loglevel.h> #include <log4cplus/tstring.h> #include <log4cplus/helpers/threads.h> #include <iomanip>
using namespace std; using namespace log4cplus;
int main(int argc, char **argv) { log4cplus::helpers::sleep(1); tstring serverName = (argc > 1 ? LOG4CPLUS_C_STR_TO_TSTRING(argv[1]) : tstring()); //tstring host = LOG4CPLUS_TEXT("192.168.2.10"); tstring host = LOG4CPLUS_TEXT("127.0.0.1"); SharedAppenderPtr append_1(new SocketAppender(host, 9998, serverName)); append_1->setName( LOG4CPLUS_TEXT("First") ); Logger::getRoot().addAppender(append_1);
Logger root = Logger::getRoot(); Logger test = Logger::getInstance( LOG4CPLUS_TEXT("socket.test") );
LOG4CPLUS_DEBUG(root, "This is" << " a reall" << "y long message." << endl << "Just testing it out" << endl << "What do you think? ") test.setLogLevel(NOT_SET_LOG_LEVEL); LOG4CPLUS_DEBUG(test, "This is a bool: " << true) LOG4CPLUS_INFO(test, "This is a char: " << 'x') LOG4CPLUS_INFO(test, "This is a short: " << (short)-100) LOG4CPLUS_INFO(test, "This is a unsigned short: " << (unsignedshort)100) log4cplus::helpers::sleep(0, 500000); LOG4CPLUS_INFO(test, "This is a int: " << (int)1000) LOG4CPLUS_INFO(test, "This is a unsigned int: " << (unsignedint)1000) LOG4CPLUS_INFO(test, "This is a long(hex): " << hex << (long)100000000) LOG4CPLUS_INFO(test, "This is a unsigned long: " << (unsignedlong)100000000) LOG4CPLUS_WARN(test, "This is a float: " << (float)1.2345) LOG4CPLUS_ERROR(test, "This is a double: " << setprecision(15) << (double)1.2345234234) LOG4CPLUS_FATAL(test, "This is a long double: " << setprecision(15) << (long double)123452342342.342)
return 0; } |
6.4. 嵌入诊断上下文NDC
log4cplus中的嵌入诊断上下文(Nested Diagnostic Context),即NDC。对log系统而言,当输入源可能不止一个,而仅仅有一个输出时。往往须要分辩所要输出消息的来源,比方server处理来自不同client的消息时就须要作此推断。NDC能够为交错显示的信息打上一个标记(stamp)。使得辨认工作看起来比較easy些。
这个标记是线程特有的,利用了线程局部存储机制,称为线程私有数据(Thread-Specific Data,或TSD)。相关定义例如以下,包含定义、初始化、获取、设置和清除操作:
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) #defineLOG4CPLUS_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(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(); |
输出结果(当设定输出格式为TTCCLayout时):
10-21-04 21:32:58, [3392] DEBUG test - this is a NDC test 10-21-04 21:32:58, [3392] DEBUG test <> - There should be no NDC... |
也能够在自己定义的输出格式中使用NDC(用%x) ,比方:
... ... std::string pattern = "NDC:[%x] - %m %n"; std::auto_ptr _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(); ... ... |
输出结果:
NDC:[] - This is the FIRST log message... NDC:[ur ndc string] - This is the SECOND log message... |
第二种更简单的用法是在线程中直接用NDCContextCreator:
NDCContextCreator _first_ndc("ur ndc string");
G4CPLUS_DEBUG(logger, "this is a NDC test")
不必显式地调用push/pop了,并且当出现异常时,可以确保push与pop的调用是匹配的。
7. 输出过滤(filter)
7.1. 利用日志级别进行输出过滤
7.1.1. 日志级别管理
log4cplus将输出的log信息依照LogLevel(从低到高)分为:
级别 |
说明 |
NOT_SET_LOG_LEVEL ( -1) |
接受缺省的LogLevel,假设有父logger则继承它的LogLevel |
ALL_LOG_LEVEL (0) |
开放全部log信息输出 |
TRACE_LOG_LEVEL (0) |
开放trace信息输出(即ALL_LOG_LEVEL) |
DEBUG_LOG_LEVEL(10000) |
开放debug信息输出 |
INFO_LOG_LEVEL (20000) |
开放info信息输出 |
WARN_LOG_LEVEL (30000) |
开放warning信息输出 |
ERROR_LOG_LEVEL(40000) |
开放error信息输出 |
FATAL_LOG_LEVEL (50000) |
开放fatal信息输出 |
OFF_LOG_LEVEL (60000) |
关闭全部log信息输出 |
在log4cplus中。全部logger都通过一个层次化的结构(事实上内部是hash表)来组织的,有一个Root级别的logger,能够通过下面方法获取: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能够通过setLogLevel设置自己的优先级。当某个logger的LogLevel设置成NOT_SET_LOG_LEVEL时,该logger会继承父logger的优先级,另外,假设定义了重名的多个logger, 对当中不论什么一个的改动都会同一时候改变其他logger。
7.1.2. 利用日志级别进行输出过滤
log4cplus支持编译时候和执行时刻利用日志级别进行输出过滤。编译时刻通过例如以下的提前定义变量进行过滤:
#define LOG4CPLUS_DISABLE_FATAL #define LOG4CPLUS_DISABLE_WARN #define LOG4CPLUS_DISABLE_ERROR #define LOG4CPLUS_DISABLE_INFO #define LOG4CPLUS_DISABLE_DEBUG #define LOG4CPLUS_DISABLE_TRACE |
执行时刻的过滤则通过使用Logger的setLogLevel设置日志级别进行过滤。
7.1.3. 例7-日志的优先级
#include "log4cplus/logger.h" #include "log4cplus/consoleappender.h" #include "log4cplus/loglevel.h" #include <iostream> 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 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()) << ' ');
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; } |
输出结果:
Before Setting, Default LogLevel FATAL - root: DEBUG FATAL - test: DEBUG FATAL - test.subtest: DEBUG
Setting test.subtest to WARN FATAL - root: DEBUG FATAL - test: DEBUG FATAL - test.subtest: WARN
Setting test to TRACE FATAL - root: DEBUG FATAL - test: TRACE FATAL - test.subtest: WARN
Setting test.subtest to NO_LEVEL FATAL - root: DEBUG FATAL - test: TRACE FATAL - test.subtest: TRACE
create a logger test_bak, named "test_", too. Setting test to INFO, so test_bak also be set to INFO FATAL - test: INFO FATAL - test_bak: INFO |
7.1.4. 例8-执行时利用日志级别进行输出过滤
#include "log4cplus/logger.h" #include "log4cplus/consoleappender.h" #include "log4cplus/loglevel.h" #include <iostream> 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(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; } |
输出结果:
all-log allowed 10-17-04 10:11:40,587 [1075298944] TRACE root <> - info 10-17-04 10:11:40,590 [1075298944] DEBUG root <> - info 10-17-04 10:11:40,591 [1075298944] INFO root <> - info 10-17-04 10:11:40,591 [1075298944] WARN root <> - info 10-17-04 10:11:40,592 [1075298944] ERROR root <> - info 10-17-04 10:11:40,592 [1075298944] FATAL root <> - info
trace-log and above allowed 10-17-04 10:11:40,593 [1075298944] TRACE root <> - info 10-17-04 10:11:40,593 [1075298944] DEBUG root <> - info 10-17-04 10:11:40,594 [1075298944] INFO root <> - info 10-17-04 10:11:40,594 [1075298944] WARN root <> - info 10-17-04 10:11:40,594 [1075298944] ERROR root <> - info 10-17-04 10:11:40,594 [1075298944] FATAL root <> - info
debug-log and above allowed 10-17-04 10:11:40,595 [1075298944] DEBUG root <> - info 10-17-04 10:11:40,595 [1075298944] INFO root <> - info 10-17-04 10:11:40,596 [1075298944] WARN root <> - info 10-17-04 10:11:40,596 [1075298944] ERROR root <> - info 10-17-04 10:11:40,596 [1075298944] FATAL root <> - info
info-log and above allowed 10-17-04 10:11:40,597 [1075298944] INFO root <> - info 10-17-04 10:11:40,597 [1075298944] WARN root <> - info 10-17-04 10:11:40,597 [1075298944] ERROR root <> - info 10-17-04 10:11:40,598 [1075298944] FATAL root <> - info
warn-log and above allowed 10-17-04 10:11:40,598 [1075298944] WARN root <> - info 10-17-04 10:11:40,598 [1075298944] ERROR root <> - info 10-17-04 10:11:40,599 [1075298944] FATAL root <> - info
error-log and above allowed 10-17-04 10:11:40,599 [1075298944] ERROR root <> - info 10-17-04 10:11:40,600 [1075298944] FATAL root <> - info
fatal-log and above allowed 10-17-04 10:11:40,600 [1075298944] FATAL root <> - info
log disabled |
7.2. 利用脚本配置进行输出过滤
因为log4cplus脚本配置中能够设置日志的级别、过滤器Filter,因此它也是进行输出过滤的一种非常好的选择。脚本配置的使用详细參见第8节。
7.3. LogLog的输出过滤
Loglog能够使用setInternalDebugging()方法用来控制是否屏蔽输出信息中的调试信息。当输入參数为false则屏蔽,缺省设置为false。 另外方法setQuietMode()方法用来控制是否屏蔽全部输出信息。当输入參数为true则屏蔽。缺省设置为false。详细使用方法请參见4.2.5小节。
8. 脚本配置
除了通过程序实现对log环境的配置之外,log4cplus通过PropertyConfigurator类实现了基于脚本配置的功能。通过脚本能够完毕对logger、appender和layout的配置,因此能够解决如何输出,输出到哪里的问题。
以下将简介一下脚本的语法规则,包含基本配置语法和高级配置语法。
8.1. 基本配置
基本配置语法主要针对包含rootLogger和non-root logger。
8.1.1. 根Logger的配置
语法:log4cplus.rootLogger=[LogLevel], appenderName, appenderName, ...
8.1.2. 非根Logger的配置
语法:log4cplus.logger.logger_name=[LogLevel|INHERITED], appenderName, appenderName, ...
说明:INHERITED表示继承父Logger的日志级别。
8.2. 高级配置
8.2.1. Appender配置
语法:
log4cplus.appender.appenderName=fully.qualified.name.of.appender.class
fully.qualified.name.of.appeneder.class可用值:
log4cplus::ConsoleAppender |
终端输出 |
log4cplus::FileAppender |
一般文件输出 |
log4cplus::RollingFileAppender |
日志大小输出 |
log4cplus::DailyRollingFileAppender |
日期输出 |
log4cplus::SocketAppender |
网络port输出 |
文件通用选项:
选项 |
作用 |
ImmediateFlush |
是否马上刷新(默觉得true) |
log4cplus.appender.ALL_MSGS.ImmediateFlush=true |
|
File |
使用的文件名称 |
log4cplus.appender.ALL_MSGS.File=all_msgs.log |
|
Append |
是否追加到之前的文件 |
log4cplus.appender.ALL_MSGS.Append=true |
|
ReopenDelay |
先将日志缓存起来,等指定时间之后再往文件里插入 降低文件的保存次数 |
log4cplus.appender.ALL_MSGS.ReopenDelay=10【单位为秒】 |
|
UseLockFile |
是否使用加锁的方式去写文件,默认是false |
log4cplus.appender.ALL_MSGS.UseLockFile=true |
|
LockFile |
使用的加锁文件名称 |
log4cplus.appender.ALL_MSGS.LockFile=fuck_are_you.lock[文件名称没有详细要求] |
|
Locale |
使用的字符集 |
log4cplus.appender.ALL_MSGS.Locale=chs【en,其它參数详细见imbue參数】 |
|
Threshold |
指定日志消息的输出最低层次 |
log4cplus.appender.ALL_MSGS.Threshold=DEBUG |
DailyRollingFileAppender相关配置:
选项 |
作用 |
Schedule |
文件保存频率 可选值:MONTHLY, WEEKLY, DAILY, TWICE_DAILY, HOURLY, MINUTELY |
log4cplus.appender.ALL_MSGS.Schedule=MINUTELY |
|
MaxBackupIndex |
最多文件个数 |
log4cplus.appender.ALL_MSGS. MaxBackupIndex=10 |
|
DatePattern |
指定文件名称的日期格式 1)'.'yyyy-MM: 每月 2)'.'yyyy-ww: 每周 3)'.'yyyy-MM-dd: 每天 4)'.'yyyy-MM-dd-a: 每天两次 5)'.'yyyy-MM-dd-HH: 每小时 6)'.'yyyy-MM-dd-HH-mm: 每分钟 |
log4cplus.appender.ALL_MSGS.DatePattern='.'yyyy-ww |
RollingFileAppender相关配置:
选项 |
作用 |
MaxFileSize |
最大文件大小,当小于200kb的时候。默觉得200kb,单位有(MB、KB) |
log4cplus.appender.ALL_MSGS. MaxFileSize=10 |
|
MaxBackupIndex |
最多文件个数 |
log4cplus.appender.ALL_MSGS. MaxBackupIndex=10 |
8.2.2. Filter配置
Appender能够附加Filter组成的链表,假设Filter链中存在过滤器Filter, log4cplus在输出日志之前将调用链表中Filter的过滤方法decide(),依据该方法的返回值决定是否过滤该输出日志。
语法:
log4cplus.appender.appenderName.Filters.FilterNumber=fully.qualified.name.of.Filter.class
log4cplus.appender.appenderName.Filters.FilterNumber.FilterCondition=value.of.FilterCondition
log4cplus.appender.appenderName.Filters.AcceptOnMatch=true|false
举例:
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
眼下log4plus提供的过滤器包含DenyAllFilter 、LogLevelMatchFilter、LogLevelRangeFilter、和StringMatchFilter。
l LogLevelMatchFilter依据特定的日志级别进行过滤。
过滤条件包含LogLevelToMatch和AcceptOnMatch(true|false), 仅仅有当日志的LogLevel值与LogLevelToMatch同样,且AcceptOnMatch为true时才会匹配。
l LogLevelRangeFilter依据依据日志级别的范围进行过滤。
过滤条件包含LogLevelMin、LogLevelMax和AcceptOnMatch,仅仅有当日志的LogLevel在LogLevelMin、LogLevelMax之间同一时候AcceptOnMatch为true时才会匹配。
l StringMatchFilter依据日志内容是否包括特定字符串进行过滤。
过滤条件包括StringToMatch和AcceptOnMatch。仅仅有当日志包括StringToMatch字符串 且AcceptOnMatch为true时会匹配。
l DenyAllFilter则过滤掉全部消息。
过滤条件处理机制类似于Linux中IPTABLE的Responsibility chain机制。(即先deny、再allow)只是运行顺序刚好相反,后写的条件会被先运行。比方:
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信息。
8.2.3. Layout配置
能够选择不设置、TTCCLayout、或PatternLayout,假设不设置。会输出SimpleLayout格式的日志。
设置TTCCLayout的语法: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%n
8.2.4. 例9-脚本配置
脚本方式使用起来很easy。仅仅要首先载入配置就可以(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 |
下面是演示样例代码。
#include <log4cplus/logger.h> #include <log4cplus/configurator.h> #include <log4cplus/helpers/stringhelper.h> #include <log4cplus/loggingmacros.h> #include <iostream> #include <string>
using namespace std; using namespace log4cplus; using namespace log4cplus::helpers; using namespace log4cplus::thread;
static Logger logger = Logger::getInstance(LOG4CPLUS_TEXT("log")); void printDebug() { LOG4CPLUS_TRACE_METHOD(logger, LOG4CPLUS_TEXT("::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(LOG4CPLUS_TEXT("urconfig.properties")); printDebug();
return 0; } |
输出结果:
1. all_msgs.log 10-17-04 14:55:25,858 [1075298944] TRACE log <> - ENTER: ::printDebug() 10-17-04 14:55:25,871 [1075298944] DEBUG log <> - This is a DEBUG message 10-17-04 14:55:25,873 [1075298944] INFO log <> - This is a INFO message 10-17-04 14:55:25,873 [1075298944] WARN log <> - This is a WARN message 10-17-04 14:55:25,874 [1075298944] ERROR log <> - This is a ERROR message 10-17-04 14:55:25,874 [1075298944] FATAL log <> - This is a FATAL message 10-17-04 14:55:25,875 [1075298944] TRACE log <> - EXIT:::printDebug()
2. trace_msgs.log 10-17-04 14:55:25,858 [1075298944] TRACE log <> - ENTER: ::printDebug() 10-17-04 14:55:25,875 [1075298944] TRACE log <> - EXIT:::printDebug()
3. debug_info_msgs.log 10-17-04 14:55:25,871 [1075298944] DEBUG log <> - This is a DEBUG message 10-17-04 14:55:25,873 [1075298944] INFO log <> - This is a INFO message
4. fatal_msgs.log 10-17-04 14:55:25,874 [1075298944] FATAL log <> - This is a FATAL message |
8.3. 脚本配置的动态载入
多线程版本号的log4cplus提供了有用类ConfigureAndWatchThread,该类启动线程对配置脚本进行监控,一旦发现配置脚本被更新则立马又一次载入配置。
类ConfigureAndWatchThread的构造函数定义为:
ConfigureAndWatchThread(const log4cplus::tstring& propertyFile,
unsigned int millis = 60 * 1000);
第一个參数propertyFile为配置脚本的路径名。第二个參数为监控时两次更新检查相隔的时间。单位为耗秒ms。
8.3.1. 例10-使用线程监控脚本的更新
#include <log4cplus/logger.h> #include <log4cplus/configurator.h> #include <log4cplus/helpers/loglog.h> #include <log4cplus/helpers/stringhelper.h> using namespace std; using namespace log4cplus; using namespace log4cplus::helpers;
Logger log_1 = Logger::getInstance("test.log_1"); Logger log_2 = Logger::getInstance("test.log_2"); Logger log_3 = Logger::getInstance("test.log_3");
void printMsgs(Logger& logger) { LOG4CPLUS_TRACE_METHOD(logger, "printMsgs()"); LOG4CPLUS_DEBUG(logger, "printMsgs()"); LOG4CPLUS_INFO(logger, "printMsgs()"); LOG4CPLUS_WARN(logger, "printMsgs()"); LOG4CPLUS_ERROR(logger, "printMsgs()"); }
int main() { cout << "Entering main()..." << endl; LogLog::getLogLog()->setInternalDebugging(true); Logger root = Logger::getRoot(); try { ConfigureAndWatchThread configureThread("log4cplus.properties", 5 * 1000); LOG4CPLUS_WARN(root, "Testing...."); for(int i=0; i<100; ++i) { printMsgs(log_1); printMsgs(log_2); printMsgs(log_3); log4cplus::helpers::sleep(1); } } catch(...) { cout << "Exception..." << endl; LOG4CPLUS_FATAL(root, "Exception occured...") } cout << "Exiting main()..." << endl; return 0; } |
下面是配置脚本log4cplus.properties的内容。
log4cplus.rootLogger=INFO, STDOUT, R log4cplus.logger.test=WARN log4cplus.logger.test.log_1=FATAL log4cplus.logger.test.log_2=FATAL log4cplus.logger.test.log_3=WARN
log4cplus.appender.STDOUT=log4cplus::ConsoleAppender log4cplus.appender.STDOUT.layout=log4cplus::PatternLayout log4cplus.appender.STDOUT.layout.ConversionPattern=%d{%m/%d/%y %H:%M:%S} [%t] %-5p %c{2} %%%x%% - %m [%l]%n
log4cplus.appender.R=log4cplus::RollingFileAppender log4cplus.appender.R.File=output.log #log4cplus.appender.R.MaxFileSize=5MB log4cplus.appender.R.MaxFileSize=500KB log4cplus.appender.R.MaxBackupIndex=5 log4cplus.appender.R.layout=log4cplus::TTCCLayout |
9. 定制Log4cplus
9.1. 定制日志级别
log4cplus支持日志级别的定制。
假设须要定义自己的优先级,则能够按下面步骤进行定制。
l 定义新日志级别相应的常量整数和输出宏。
/* * customloglevel.h */ #include <log4cplus/logger.h> #include <log4cplus/helpers/loglog.h>
using namespace log4cplus; using namespace log4cplus::helpers;
const LogLevel CRITICAL_LOG_LEVEL = 45000;
#define LOG4CPLUS_CRITICAL(logger, logEvent) if(logger.isEnabledFor(CRITICAL_LOG_LEVEL)) { log4cplus::tostringstream _log4cplus_buf; _log4cplus_buf << logEvent; logger.forcedLog(CRITICAL_LOG_LEVEL, _log4cplus_buf.str(), __FILE__,__LINE__); } |
l 定义新日志级别相应的字符串、常量整数与字符串之间的转换函数,定义自己的初始化器将转换函数注冊到LogLevelManage。
#include "customloglevel.h" #define _CRITICAL_STRING "CRITICAL"
tstring criticalToStringMethod(LogLevel ll) { if(ll == CRITICAL_LOG_LEVEL) { return _CRITICAL_STRING; } else { return tstring(); } }
LogLevel criticalFromStringMethod(const tstring& s) { if(s == _CRITICAL_STRING) return CRITICAL_LOG_LEVEL; return NOT_SET_LOG_LEVEL; }
class CriticalLogLevelInitializer { public: CriticalLogLevelInitializer() { getLogLevelManager().pushToStringMethod(criticalToStringMethod); getLogLevelManager().pushFromStringMethod(criticalFromStringMethod); } };
CriticalLogLevelInitializer criticalLogLevelInitializer_; |
l 使用新定义的日志级别
#include "customloglevel.h" #include <log4cplus/consoleappender.h> #include <iomanip> #include <iostream> using namespace std; using namespace log4cplus;
int main() { SharedAppenderPtr append_1(new ConsoleAppender()); append_1->setName("First"); Logger::getRoot().addAppender(append_1);
Logger root = Logger::getRoot(); LOG4CPLUS_CRITICAL(root, "This is a new logginglevel"); return 0; } |
9.2. 定制LogLog
LogLog输出信息中总是包括"log4cplus:"前缀,这是由于LogLog在实现时候在构造函数中进行了硬编码:
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 ") ) { } |