一. 優先級控制
在研究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文件寫入日志!
其他文件為空