開源日志系統log4cplus(五)
日志系統的另一個基本功能就是能夠讓使用者按照自己的意愿來控制什么時候,哪些log信息可以輸出。
如果能夠讓用戶在任意時刻設置允許輸出的LogLevel的信息就好了,log4cplus通過LogLevelManager、
LogLog、Filter三種方式實現了上述功能。
### 優先級控制 ###
在研究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,我們舉例說明:
〖例6〗
#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.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;
}
輸出結果:
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.subtest 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
下面的例子演示了如何通過設置LogLevel來控制用戶的log信息輸出:
〖例7〗
#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
?
用戶也可以自行定義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源代碼后生成庫文件,再使用時即可實現滿意效果。
### 調試模式 ###
即通過loglog來控制輸出調試、警告或錯誤信息,見例4,這里不再贅述。
?
### 基于腳本配置來過濾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強大的基于腳本過濾log信息的功能。
〖例8〗
/*
?*??? 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>
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;
}
運行結果:
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
?
本部分詳細介紹了如何有選擇地控制log信息的輸出,最后一部分我們將介紹一下多線程、
和C/S模式下該如何操作,順便提一下NDC的概念。
posted on 2006-08-26 04:50 楊粼波 閱讀(8267) 評論(0) 編輯 收藏 引用 所屬分類: Windows編程 、Linux編程