經過短暫的熟悉過程,log4cplus已經被成功應用到了我的項目中去了,效果還不錯,:)除了上文提及的
功能之外,下面將介紹log4cplus提供的線程和套接字的使用情況。
### NDC ###
首先我們先了解一下log4cplus中嵌入診斷上下文(Nested Diagnostic Context),即NDC。對log系統而言,
當輸入源可能不止一個,而只有一個輸出時,往往需要分辯所要輸出消息的來源,比如服務器處理來自不同
客戶端的消息時就需要作此判斷,NDC可以為交錯顯示的信息打上一個標記(stamp), 使得辨認工作看起來
比較容易些,呵呵。這個標記是線程特有的,利用了線程局部存儲機制,稱為線程私有數據(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)
# define LOG4CPLUS_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();

當設定輸出格式(Layout)為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");
LOG4CPLUS_DEBUG(logger, "this is a NDC test")
不必顯式地調用push/pop了,而且當出現異常時,能夠確保push與pop的調用是匹配的。
### 線程 ###
線程是log4cplus中的副產品, 而且僅作了最基本的實現,使用起來也異常簡單,只要且必須要
在派生類中重載run函數即可:
class TestThread : public AbstractThread


{
public:
virtual void run();
};
void TestThread::run()


{

/**//* do sth. */

}

log4cplus的線程沒有考慮同步、死鎖,有互斥,實現線程切換的小函數挺別致的:
void log4cplus::thread::yield()


{
#if defined(LOG4CPLUS_USE_PTHREADS)
::sched_yield();
#elif defined(LOG4CPLUS_USE_WIN32_THREADS)
::Sleep(0);
#endif
}
### 套接字 ###
套接字也是log4cplus中的副產品,在namespace log4cplus::helpers中,實現了C/S方式的日志記錄。
1. 客戶端程序需要做的工作:

/**//* 定義一個SocketAppender類型的掛接器 */SharedAppenderPtr _append(new SocketAppender(host, 8888, "ServerName"));

/**//* 把_append加入到logger中 */Logger::getRoot().addAppender(_append);

/**//* 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);


2. 服務器端程序需要做的工作:

/**//* 定義一個ServerSocket */ServerSocket serverSocket(port);

/**//* 調用accept函數創建一個新的socket與客戶端連接 */Socket sock = serverSocket.accept();

此后即可用該sock進行數據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;
}

為了將讀到的數據正常顯示出來,需要將SocketBuffer存放的內容轉換成InternalLoggingEvent格式:
spi::InternalLoggingEvent event = readFromBuffer(buffer);
然后輸出:
Logger logger = Logger::getInstance(event.getLoggerName());
logger.callAppenders(event);
【注】 read/write是按照阻塞方式實現的,意味著對其調用直到滿足了所接收或發送的個數才返回。