一. 优先级控制
在研究LogLevelManager之前,首先介绍一下log4cplus中logger的存储机制,在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的这种父子关联性会体现在优先级控制方面,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信息输出
LogLevelManager负责设置logger的优先级,各个logger可以通过setLogLevel设置自己的优先级,
当某个logger的LogLevel设置成NOT_SET_LOG_LEVEL时,该logger会继承父logger的优先级,另外,
如果定义了重名的多个logger, 对其中任何一个的修改都会同时改变其它logger,我们举例说明:
/**//*
设置日志等级
*/
#include "stdafx.h"
#pragma comment(lib,"../bin/log4cplusD.lib")
#include <log4cplus/logger.h>
#include <log4cplus/fileappender.h>
#include <log4cplus/consoleappender.h>
#include <conio.h>
#include <iostream>
using namespace std;
using namespace log4cplus;
int _tmain(int argc, _TCHAR* argv[]){
SharedAppenderPtr _append(new ConsoleAppender());
_append->setName(LOG4CPLUS_TEXT("test"));
//新建test logger以及test的子subtest logger
Logger::getRoot().addAppender(_append);
Logger root = Logger::getRoot();
Logger test = Logger::getInstance(LOG4CPLUS_TEXT("test"));
Logger subTest = Logger::getInstance(LOG4CPLUS_TEXT("test.subtest"));
LogLevelManager& llm = getLogLevelManager();
//设置优先级之前
//默认都是debug级别
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()));
//subTest被设置警告优先级
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()));
//设置TRACE
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;
//NOT_SET_LOG_LEVEL
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') ;
//重新获取test实例
cout << "create a logger test_bak, named \"test_\", too. " << endl;
Logger test_bak = Logger::getInstance(LOG4CPLUS_TEXT("test"));
//设置等级INFO_LOG_LEVEL
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()));
_getch();
return 0;
}
/**//*
设置日志等级,输出对应等级的日志
*/
#include "stdafx.h"
#pragma comment(lib,"../bin/log4cplusD.lib")
#include <log4cplus/logger.h>
#include <log4cplus/fileappender.h>
#include <log4cplus/consoleappender.h>
#include <conio.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 _tmain(int argc, _TCHAR* argv[]){
SharedAppenderPtr _append(new ConsoleAppender());
_append->setName(LOG4CPLUS_TEXT("test"));
//std::auto_ptr<Layout> _layout(new TTCCLayout());
//_append->setLayout(_layout);
_append->setLayout(std::auto_ptr<TTCCLayout>(new TTCCLayout()));
Logger root = Logger::getRoot();
root.addAppender(_append);
//无等级,显示所有的日志
cout << endl << "all-log allowed" << endl;
root.setLogLevel(ALL_LOG_LEVEL);
ShowMsg();
//显示>=TRACE等级日志
cout << endl << "trace-log and above allowed" << endl;
root.setLogLevel(TRACE_LOG_LEVEL);
ShowMsg();
//显示>=DEBUG等级日志
cout << endl << "debug-log and above allowed" << endl;
root.setLogLevel(DEBUG_LOG_LEVEL);
ShowMsg();
//显示>=INFO等级日志
cout << endl << "info-log and above allowed" << endl;
root.setLogLevel(INFO_LOG_LEVEL);
ShowMsg();
//显示>=WARN等级日志
cout << endl << "warn-log and above allowed" << endl;
root.setLogLevel(WARN_LOG_LEVEL);
ShowMsg();
//显示>=ERROR等级日志
cout << endl << "error-log and above allowed" << endl;
root.setLogLevel(ERROR_LOG_LEVEL);
ShowMsg();
//显示>=FATAL等级日志
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();
_getch();
return 0;
}
用户也可以自行定义LogLevel,操作比较简单,首先要定义LEVEL值,比如HELLO_LOG_LEVEL定义如下:
//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控制输出的结果可能会如下所示:
10-17-04 11:17:51,124 [1075298944] UNKNOWN root <> - info
而不是期望的以下结果:
10-17-04 11:17:51,124 [1075298944] HELLO root <> - info
要想实现第二种结果,按照log4cplus现有的接口机制,只能改其源代码后重新编译,方法是在loglevel.cxx中加入:
#define _HELLO_STRING LOG4CPLUS_TEXT("HELLO")
然后修改log4cplus::tstring defaultLogLevelToStringMethod(LogLevel ll)函数,增加一个判断:
case HELLO_LOG_LEVEL:
return _HELLO_STRING;
重新编译log4cplus源代码后生成库文件,再使用时即可实现满意效果。
二. 基于脚本配置来过滤log信息
除了通过程序实现对log环境的配置之外,log4cplus通过PropertyConfigurator类实现了基于脚本配置的功能。
通过脚本可以完成对logger、appender和layout的配置,因此可以解决怎样输出,输出到哪里的问题,我将在
全文的最后一部分中提到多线程环境中如何利用脚本配置来配合实现性能测试,本节将重点介绍基脚本实现过
滤log信息的功能。
首先简单介绍一下脚本的语法规则:
包括Appender的配置语法和logger的配置语法,其中:
1.Appender的配置语法:
(1)设置名称:
//设置方法
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:
包括选择过滤器和设置过滤条件,可选择的过滤器包括:LogLevelMatchFilter、LogLevelRangeFilter、和StringMatchFilter:
对LogLevelMatchFilter来说,过滤条件包括LogLevelToMatch和AcceptOnMatch(true|false),
只有当log信息的LogLevel值与LogLevelToMatch相同,且AcceptOnMatch为true时才会匹配。
LogLevelRangeFilter来说,过滤条件包括LogLevelMin、LogLevelMax和AcceptOnMatch,
只有当log信息的LogLevel在LogLevelMin、LogLevelMax之间同时AcceptOnMatch为true时才会匹配。
对StringMatchFilter来说,过滤条件包括StringToMatch和AcceptOnMatch,只有当log信息的LogLevel值
与StringToMatch对应的LogLevel值与相同, 且AcceptOnMatch为true时会匹配。
过滤条件处理机制类似于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信息。
(3) 设置Layout可以选择不设置、TTCCLayout、或PatternLayout如果不设置,
会输出简单格式的log信息。设置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
2.logger的配置语法
包括rootLogger和non-root logger。
对于rootLogger来说:log4cplus.rootLogger=[LogLevel], appenderName, appenderName, ...
对于non-root logger来说:log4cplus.logger.logger_name=[LogLevel|INHERITED], appenderName, appenderName, ...
脚本方式使用起来非常简单,只要首先加载配置即可(urconfig.properties是自行定义的配置文件):
PropertyConfigurator::doConfigure("urconfig.properties");
补充说明:log4cplus.additivity.logger_name表示表示是否继承父类的配置
3. 示例:
配置文件如下:
log4cplus.rootLogger=TRACE, ALL_MSGS, TRACE_MSGS, DEBUG_INFO_MSGS, FATAL_MSGS
#trace_msgs将可以通过Logger::getInstance(LOG4CPLUS_TEXT("trace_msgs"))获取
log4cplus.logger.trace_msgs = TRACE,TRACE_MSGS
log4cplus.logger.debug_info_msgs = TRACE,DEBUG_INFO_MSGS
log4cplus.logger.fatal_msgs = TRACE,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 "stdafx.h"
#pragma comment(lib,"../bin/log4cplusD.lib")
#include <log4cplus/logger.h>
#include <log4cplus/fileappender.h>
#include <log4cplus/consoleappender.h>
#include <conio.h>
#include <iostream>
#include <log4cplus/configurator.h>
using namespace std;
using namespace log4cplus;
static Logger logger1 = Logger::getInstance(LOG4CPLUS_TEXT("TRACE_MSGS"));
static Logger logger2 = Logger::getInstance(LOG4CPLUS_TEXT("log"));
void printDebug(){
/**//* LOG4CPLUS_TRACE(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");
*/
LOG4CPLUS_TRACE(logger1, "::printDebug()");
}
int _tmain(int argc, _TCHAR* argv[]){
//
PropertyConfigurator::doConfigure(LOG4CPLUS_TEXT("urconfig.properties"));
//入root的logger写入记录
Logger root = Logger::getRoot();
LOG4CPLUS_FATAL(root,"向all_msgs.log文件中写入信息,因为向root,自动往所有的字文件写入!");
//只向log4cplus.logger.trace_msgs = TRACE,TRACE_MSGS对应的
//log4cplus.appender.TRACE_MSGS文件写入日志
Logger logger1 = Logger::getInstance(LOG4CPLUS_TEXT("TRACE_MSGS"));
LOG4CPLUS_TRACE(logger1,"只向all_msgs.log或trace_msgs.log文件写入日志!");
return 0;
}
结果如下:
all_msgs.log:
10 [5840] FATAL root <> - 向所有的文件中写入信息,因为向root,自动往所有的字文件写入!
10 [5840] TRACE TRACE_MSGS <> - 只向trace_msgs.log文件写入日志!
trace_msgs.log:
10 [5840] TRACE TRACE_MSGS <> - 只向trace_msgs.log文件写入日志!
其他文件为空