一、簡(jiǎn)介:
log4cplus是C++編寫(xiě)的開(kāi)源的日志系統(tǒng). 具有線程安全、靈活、以及多粒度控制的特點(diǎn),通過(guò)將信息劃分優(yōu)先級(jí)使其可以面向程序調(diào)試、運(yùn)行、測(cè)試、和維護(hù)等全生命周期; 你可以選擇將信息輸出到屏幕、文件、NT event log、甚至是遠(yuǎn)程服務(wù)器;通過(guò)指定策略對(duì)日志進(jìn)行定期備份等等。
二、基本要素
將log4cplus文件夾下的msvc10文件夾拷貝出來(lái),使用VS10打開(kāi)工程,編譯源代碼和用例。
Layouts:布局器,控制輸出消息的格式。
Appenders:掛接器,與布局器緊密配合,將特定格式的消息輸出到所掛接
的設(shè)備終端如屏幕,文件等等)。
Logger:記錄器,保存并跟蹤對(duì)象日志信息變更的實(shí)體,當(dāng)你需要對(duì)一個(gè)對(duì)
象進(jìn)行記錄時(shí),就需要生成一個(gè)logger。
Categories:分類器,層次化(hierarchy)的結(jié)構(gòu),用于對(duì)被記錄信息的
分類,層次中每一個(gè)節(jié)點(diǎn)維護(hù)一個(gè)logger的所有信息。
PRiorities:優(yōu)先權(quán),包括TRACE, DEBUG, INFO, WARNING, ERROR, FATAL。
三、基本使用使用log4cplus的六個(gè)步驟:
1. 實(shí)例化一個(gè)appender對(duì)象: new ConsoleAppender();
2. 實(shí)例化一個(gè)layout對(duì)象:new PatternLayout(格式);
3. 將layout對(duì)象綁定(attach)到appender對(duì)象:
appender->setLayout(layout);
4. 實(shí)例化一個(gè)logger對(duì)象,調(diào)用靜態(tài)函數(shù):
log4cplus ::Logger ::getInstance("logger_name");
5. 將appender對(duì)象綁定(attach)到logger對(duì)象:
logger.addAppender(appender),如省略此步驟,標(biāo)準(zhǔn)輸出(屏幕)appender
對(duì)象會(huì)綁定到logger;
6. 設(shè)置logger的優(yōu)先級(jí): logger.setLogLevel(ALL_LOG_LEVEL),如
省略此步驟,各種有限級(jí)的消息都將被記錄。
例如:
1、appender輸出到屏幕:
LOG4CPLUS_DEBUG(logger,"This is the FIRST log message...");
sleep(1);
LOG4CPLUS_WARN(logger,"This is the SECOND log message...");
2、iostream模式,appender輸出到屏幕:
LOG4CPLUS_INFO(logger,"This is a char: " << 'x');
LOG4CPLUS_ERROR(logger, "This is a long( hex ): " << std::hex <<
100000000);
LOG4CPLUS_FATAL(logger, "This is a double: " <<
std::setprecision(15) << 1.2345234234);
3、調(diào)試模式,通過(guò)loglog來(lái)控制輸出調(diào)試、警告或錯(cuò)誤信息,appender輸出到屏幕:
LogLog::getLogLog()->debug("This is a Debug statement...");
LogLog::getLogLog()->warn("This is a Warning...");
LogLog::getLogLog()->error("This is a Error...");
LogLog::getLogLog()->setInternalDebugging(true);
LogLog::getLogLog()->setQuietMode(true);
LogLog類實(shí)現(xiàn)了debug, warn, error 函數(shù)用于輸出調(diào)試、警告或錯(cuò)誤信息,同時(shí)提供了兩個(gè)方法來(lái)進(jìn)一步控制所輸出的信息,其中:setInternalDebugging方法用來(lái)控制是否屏蔽輸出信息中的調(diào)試信息,當(dāng)輸入?yún)?shù)為false則屏蔽,缺省設(shè)置為false;setQuietMode方法用來(lái)控制是否屏蔽所有輸出信息,當(dāng)輸入?yún)?shù)為true則屏蔽,缺省設(shè)置為false。
注:LogLog在實(shí)現(xiàn)時(shí),寫(xiě)死了輸出信息前綴:
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 "))
{
}
4、文件模式,appender輸出到文件:
SharedAppenderPtr _append(new FileAppender("Test.log"));
_append->setName("filelog");
Logger _logger = Logger::getInstance("test.subtestof_filelog");
_logger.addAppender(_append);
LOG4CPLUS_DEBUG(_logger,"Entering text");
四、輸出格式
1、SimpleLayout是一種簡(jiǎn)單格式的布局器,在輸出的原始信息之前加上LogLevel和一個(gè)"-"。
std::auto_ptr _layout(new log4cplus::SimpleLayout());
2、PatternLayout是一種有詞法分析功能的模式布局器,一提起模式就會(huì)想起正則表達(dá)式,這里的模式和正則表達(dá)式類似,但是遠(yuǎn)比后者簡(jiǎn)單,能夠?qū)︻A(yù)定義的標(biāo)識(shí)符進(jìn)行解析,轉(zhuǎn)換成特定格式輸出。
std::string pattern = "%d{%m/%d/%y %H:%M:%S} - %m [%l] %n";
std::auto_ptr _layout(new PatternLayout(pattern));
注:
1、"pattern"可以包含普通字符串和預(yù)定義的標(biāo)識(shí)符,其中:
(1)普通字符串,能夠被直接顯示的信息;
(2)預(yù)定義標(biāo)識(shí)符,通過(guò)"%"與一個(gè)或多個(gè)字符共同構(gòu)成預(yù)定義的標(biāo)識(shí)符,
能夠產(chǎn)生出特定格式信息。
2、各種消息格式:
(1)"%%",轉(zhuǎn)義為%;
(2)"%c{n}",輸出logger名稱,也可以控制logger名稱的顯示層次其中數(shù)字表示層次。
(3)"%D",顯示本地時(shí)間,%d顯示標(biāo)準(zhǔn)時(shí)間,可以通過(guò)%d{...}定義更詳細(xì)的顯示格式,比如%d{%H:%M:%s}表示要顯示小時(shí):分鐘:秒。大括號(hào)中可顯示的預(yù)定義標(biāo)識(shí)符如下:
%a -- 表示礼拜幾,英文縮寫(xiě)形式,比如"Fri"
%A -- 表示礼拜幾,比如"Friday"
%b -- 表示幾月份,英文縮寫(xiě)形式,比如"Oct"
%B -- 表示幾月份,"October"
%c -- 標(biāo)準(zhǔn)的日期+時(shí)間格式,如 "Sat Oct 16 18:56:19 2004"
%d -- 表示今天是這個(gè)月的幾號(hào)(1-31)"16"
%H -- 表示當(dāng)前時(shí)刻是幾時(shí)(0-23),如 "18"
%I -- 表示當(dāng)前時(shí)刻是幾時(shí)(1-12),如 "6"
%j -- 表示今天是哪一天(1-366),如 "290"
%m -- 表示本月是哪一月(1-12),如 "10"
%M -- 表示當(dāng)前時(shí)刻是哪一分鐘(0-59),如 "59"
%p -- 表示現(xiàn)在是上午還是下午, AM or PM
%q -- 表示當(dāng)前時(shí)刻中毫秒部分(0-999),如 "237"
%Q -- 表示當(dāng)前時(shí)刻中帶小數(shù)的毫秒部分(0-999.999),如 "430.732"
%S -- 表示當(dāng)前時(shí)刻的多少秒(0-59),如 "32"
%U -- 表示本周是今年的第幾個(gè)礼拜,以周日為第一天開(kāi)始計(jì)算(0-53)。
%w -- 表示礼拜幾,(0-6, 礼拜天為0),如 "6"
%W -- 表示本周是今年的第幾個(gè)礼拜,以周一為第一天開(kāi)始計(jì)算(0-53)。
%x -- 標(biāo)準(zhǔn)的日期格式,如 "10/16/04"
%X -- 標(biāo)準(zhǔn)的時(shí)間格式,如 "19:02:34"
%y -- 兩位數(shù)的年份(0-99),如 "04"
%Y -- 四位數(shù)的年份,如 "2004"
%Z -- 時(shí)區(qū)名,比如 "GMT"
(4)"%F",輸出當(dāng)前記錄器所在的文件名稱;
(5)"%L",輸出當(dāng)前記錄器所在的文件行號(hào);
(6)"%l",輸出當(dāng)前記錄器所在的文件名稱和行號(hào);
(7)"%m",輸出原始信息,這種實(shí)現(xiàn)機(jī)制可以確保原始信息被嵌入到帶格式的信息中。
(8)"%n",換行符;
(9)"%p",輸出LogLevel;
(10)"%t",輸出記錄器所在的線程ID
(11)"%x",嵌套診斷上下文NDC輸出,從堆棧中彈出上下文信息,NDC可以用對(duì)不同源的log信息(同時(shí)地)交叉輸出進(jìn)行區(qū)分。
(12)格式對(duì)齊:"%-10m"時(shí)表示左對(duì)齊,寬度是10。
3、TTCCLayout是在PatternLayout基礎(chǔ)上發(fā)展的一種缺省的帶格式輸出的布局器,其格式由時(shí)間,線程ID,Logger和NDC 組成,其在構(gòu)造時(shí)選擇顯示本地時(shí)間(默認(rèn)false)或GMT時(shí)間
五、文件操作類
log4cplus提供了FileAppender類、DailyRollingFileAppender類和RollingFileAppender類用于文件操作:
1、FileAppender類實(shí)現(xiàn)了基本的文件操作功能,構(gòu)造函數(shù)如下:
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,缺省是trunc,表示將先前文件刪除。
immediateFlush :緩沖刷新標(biāo)志,如果為true表示每向文件寫(xiě)一條記錄就刷新一次緩存,否則直到FileAppender被關(guān)閉或文件緩存已滿才更新文件,默認(rèn)true。
2、RollingFileAppender類可以根據(jù)預(yù)先設(shè)定的大小來(lái)決定是否轉(zhuǎn)儲(chǔ),當(dāng)超過(guò)該大小,后續(xù)log信息會(huì)另存到新文件中,除了定義每個(gè)記錄文件的大小之外,還要確定在RollingFileAppender類對(duì)象構(gòu)造時(shí)最多需要多少個(gè)這樣的記錄文件(maxBackupIndex+1),當(dāng)存儲(chǔ)的文件數(shù)目超過(guò)maxBackupIndex+1時(shí),會(huì)刪除最早生成的文件,保證整個(gè)文件數(shù)目等于maxBackupIndex+1,然后繼續(xù)記錄。
構(gòu)造函數(shù)如下:
log4cplus::RollingFileAppender::RollingFileAppender(
const log4cplus::tstring& filename, long maxFileSize,
int maxBackupIndex, bool immediateFlush);
filename : 文件名maxFileSize : 文件的最大尺寸,尺寸小于200k時(shí)等于200k;
maxBackupIndex : 最大記錄文件數(shù)
immediateFlush : 緩沖刷新標(biāo)志
SharedAppenderPtr _append(new RollingFileAppender("Test.log", size
* n, size));
3、DailyRollingFileAppender類可以根據(jù)你預(yù)先設(shè)定的頻度來(lái)決定是否轉(zhuǎn)儲(chǔ),當(dāng)超過(guò)該頻度,后續(xù)log信息會(huì)另存到新文件中,這里的頻度包括:MONTHLY (每月)、WEEKLY(每周)、DAILY(每日)、TWICE_DAILY(每?jī)商欤OURLY(每時(shí))、MINUTELY(每分)。構(gòu)造函數(shù)如下:
DailyRollingFileAppender::DailyRollingFileAppender(
const log4cplus::tstring& filename,
DailyRollingFileSchedule schedule,
bool immediateFlush,
int maxBackupIndex);
filename : 文件名
schedule : 存儲(chǔ)頻度
immediateFlush : 緩沖刷新標(biāo)志
maxBackupIndex : 最大記錄文件數(shù)
SharedAppenderPtr _append(new DailyRollingFileAppender("Test.log",
MINUTELY, true, 5));
五、進(jìn)階
通過(guò)LogLevelManager、LogLog、Filter三種方式,實(shí)現(xiàn)任意時(shí)刻輸出的LogLevel的信息。
1、背景知識(shí):
log4cplus中l(wèi)ogger的存儲(chǔ)機(jī)制是通過(guò)一個(gè)層次化的結(jié)構(gòu)(如:hash表)來(lái)組織的。
獲取Root級(jí)別的logger: Logger::getRoot();
自定義的logger:Logger::getInstance("test");
定義其子logger: Logger::getInstance("test.subtest");
設(shè)置其LogLevel: Test.setLogLevel( ... );
subTest.setLogLevel( ... );
log4cplus將輸出的log信息按照LogLevel(從低到高)分為:
NOT_SET_LOG_LEVEL(-1) :接受缺省的LogLevel,如果有父logger則繼承它的LogLevel;
ALL_LOG_LEVEL(0) :開(kāi)放所有l(wèi)og信息輸出;
TRACE_LOG_LEVEL(0) :開(kāi)放trace信息輸出(即ALL_LOG_LEVEL);
DEBUG_LOG_LEVEL(10000) :開(kāi)放debug信息輸出;
INFO_LOG_LEVEL(20000) :開(kāi)放info信息輸出;
WARN_LOG_LEVEL(30000) :開(kāi)放warning信息輸出;
ERROR_LOG_LEVEL(40000) :開(kāi)放error信息輸出;
FATAL_LOG_LEVEL(50000) :開(kāi)放fatal信息輸出;
OFF_LOG_LEVEL(60000) :關(guān)閉所有l(wèi)og信息輸出;
2、LogLevelManager負(fù)責(zé)設(shè)置logger的優(yōu)先級(jí),各logger通過(guò)setLogLevel設(shè)置自己的優(yōu)先級(jí),當(dāng)某個(gè)logger的LogLevel設(shè)置成NOT_SET_LOG_LEVEL時(shí),該logger會(huì)繼承父logger的優(yōu)先級(jí),另外,如果定義了重名的多個(gè)logger, 對(duì)其中任何一個(gè)的修改都會(huì)同時(shí)改變其它logger。
例1:
SharedAppenderPtr _append(new ConsoleAppender());
_append->setName("test");
Logger root = Logger::getRoot();
root.addAppender(_append);
Logger test = Logger::getInstance("test");
Logger subTest = Logger::getInstance("test.subtest");
LogLevelManager& llm = getLogLevelManager();
test.setLogLevel(INFO_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()));
例2:通過(guò)設(shè)置LogLevel來(lái)控制用戶的log信息輸出:
一下級(jí)別逐漸升高:
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")
Logger root = Logger::getRoot();
root.setLogLevel(ALL_LOG_LEVEL); //全部顯示
root.setLogLevel(TRACE_LOG_LEVEL); //全部顯示
root.setLogLevel(ERROR_LOG_LEVEL); //只顯示ERROR、FATAL
root.setLogLevel(OFF_LOG_LEVEL); //顯示log disabled
3、自行定義LogLevel:
定義NEW_LOG_LEVEL:
//DEBUG_LOG_LEVEL < NEW_LOG_LEVEL < INFO_LOG_LEVEL
const LogLevel NEW_LOG_LEVEL = 15000;
定義宏:
#define LOG4CPLUS_NEW(logger, logEvent) /
if(logger.isEnabledFor(NEW_LOG_LEVEL)) { /
log4cplus::tostringstream _log4cplus_buf; /
_log4cplus_buf << logEvent; /
logger.forcedLog(NEW_LOG_LEVEL, _log4cplus_buf.str(),
__FILE__, __LINE__);}
在loglevel.cxx中加入#define _HELLO_STRING LOG4CPLUS_TEXT("HELLO") 然后修改log4cplus::tstring defaultLogLevelToStringMethod(LogLevel ll)函數(shù),增加一個(gè)判斷:case HELLO_LOG_LEVEL:return _HELLO_STRING;重新編譯log4cplus源代碼。
4、基于腳本配置來(lái)過(guò)濾log信息:
log4cplus通過(guò)PropertyConfigurator類實(shí)現(xiàn)基于腳本配置實(shí)現(xiàn)對(duì)logger、appender和layout的配置及l(fā)og環(huán)境的配置(通過(guò)程序也可以)。從而過(guò)濾log信息及利用腳本配置來(lái)配合實(shí)現(xiàn)性能測(cè)試,
腳本語(yǔ)法規(guī)則:
1、Appender的配置語(yǔ)法:
(1)設(shè)置名稱:
//設(shè)置方法
log4cplus.appender.appenderName=fully.qualified.name.of.appender.class
例如:
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)設(shè)置Filter:
過(guò)濾器:LogLevelMatchFilter、LogLevelRangeFilter、StringMatchFilter。
LogLevelMatchFilter:過(guò)濾條件包括LogLevelToMatch和
AcceptOnMatch(true|false), 只有當(dāng)log信息的LogLevel值與
LogLevelToMatch相同,且AcceptOnMatch為true時(shí)才會(huì)匹配。
LogLevelRangeFilter:過(guò)濾條件包括LogLevelMin、LogLevelMax和
AcceptOnMatch,只有當(dāng)log信息的LogLevel在LogLevelMin、LogLevelMax之間同時(shí)AcceptOnMatch為true時(shí)才會(huì)匹配。
StringMatchFilter:過(guò)濾條件包括StringToMatch和AcceptOnMatch,
只有當(dāng)log信息的LogLevel值與StringToMatch對(duì)應(yīng)的LogLevel值相同,
且AcceptOnMatch為true時(shí)會(huì)匹配。
過(guò)濾條件處理機(jī)制:先deny再allow,后寫(xiě)的條件會(huì)被先執(zhí)行。比如:
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
首先執(zhí)行filters.2的過(guò)濾條件,關(guān)閉所有過(guò)濾器,然后執(zhí)行filters.1,僅匹配TRACE信息。
(3)設(shè)置Layout:不設(shè)置(默認(rèn))、TTCCLayout、或PatternLayout
設(shè)置TTCCLayout:
log4cplus.appender.ALL_MSGS.layout=log4cplus::TTCCLayout
設(shè)置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的配置語(yǔ)法: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.rootLogger=TRACE, ALL_MSGS, TRACE_MSGS, DEBUG_INFO_MSGS, FATAL_MSGS
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
//加載
Logger root = Logger::getRoot();
PropertyConfigurator::doConfigure("urconfig.properties");
六、嵌入診斷上下文
NDC是線程特有的,利用了線程局部存儲(chǔ)機(jī)制,稱為線程私有數(shù)據(jù)(Thread-specificData,或TSD)。
(1)默認(rèn)格式輸出NDC:
NDC& ndc = log4cplus::getNDC();
ndc.push("ur ndc string");
ndc.pop();
ndc.remove();
(2)自定義的輸出格式中使用NDC(用%x):
std::string pattern = "NDC:[%x] - %m %n";
std::auto_ptr<layout></layout> _layout(new PatternLayout(pattern));
NDC& ndc = log4cplus::getNDC();ndc.push("ur ndc string");
ndc.pop();
ndc.remove();
(3)線程中直接用NDCContextCreator,不必顯式地調(diào)用push/pop了,而且當(dāng)出現(xiàn)異常時(shí),能夠確保push與pop的調(diào)用是匹配的。
NDCContextCreator _first_ndc("ur ndc string");
七、線程
log4cplus的線程沒(méi)有考慮同步、死鎖,有互斥,使用時(shí)派生類中直接重載run函數(shù)。如下:
class TestThread : public AbstractThread
{
public:
virtual void run();
};
void TestThread::run()
{
}
八、套接字
定義在namespace log4cplus::helpers中,實(shí)現(xiàn)了C/S方式的日志記錄。
1、 客戶端程序需要做的工作:
//定義一個(gè)SocketAppender類型的掛接器:
SharedAppenderPtr _append(new SocketAppender(host, 8888, "ServerName"));
//把_append加入到logger中:
Logger::getRoot().addAppender(_append);
注:SocketAppender類型不需要Layout,直接調(diào)用宏將信息發(fā)往loggerServer。
對(duì)宏的調(diào)用其實(shí)是調(diào)用了SocketAppender::append,里面有一個(gè)數(shù)據(jù)傳輸約定,即先發(fā)送一個(gè)后續(xù)數(shù)據(jù)的總長(zhǎng)度,然后再發(fā)送實(shí)際的數(shù)據(jù)。如下:
SocketBuffer buffer = convertToBuffer(event, serverName);
SocketBuffer msgBuffer(LOG4CPLUS_MAX_MESSAGE_SIZE);
msgBuffer.appendSize_t(buffer.getSize());
msgBuffer.appendBuffer(buffer);
2、服務(wù)器端程序需要做的工作:
//定義一個(gè)ServerSocket
ServerSocket serverSocket(port);
//調(diào)用accept函數(shù)創(chuàng)建一個(gè)新的socket與客戶端連接
Socket sock = serverSocket.accept();
//進(jìn)行數(shù)據(jù)read/write
SocketBuffer msgSizeBuffer(sizeof(unsigned int));
if(!clientsock.read(msgSizeBuffer))
{
return;
}
unsigned int msgSize = msgSizeBuffer.readInt();
SocketBuffer buffer(msgSize);
if(!clientsock.read(buffer))
{
return;
}
注:為了將讀到的數(shù)據(jù)正常顯示出來(lái),需要將SocketBuffer存放的內(nèi)容轉(zhuǎn)換成InternalLoggingEvent格式:spi::InternalLoggingEvent event = readFromBuffer(buffer);然后輸出:Logger logger = Logger::getInstance(event.getLoggerName());logger.callAppenders(event); read/write是按照阻塞方式實(shí)現(xiàn)的,對(duì)其調(diào)用直到滿足了所接收或發(fā)送的個(gè)數(shù)才返回。
原文地址:http://m.survivalescaperooms.com/shenchao/archive/2013/08/12/3253606.html
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注