(七)

经过短暂的熟悉过程,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<lpvoid></lpvoid>(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 <ur string="" ndc=""></ur> - 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>_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了,而且当出现异常时,能够确保pushpop的调用是匹配的。

### 线程 ###

线程是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是按照阻塞方式实现的,意味着对其调用直到满足了所接收或发送的个数才返回。

log4cplus的三个例程 http://log4cplus.sourceforge.net/codeexamples.html 里面自带的三个例程

Hello World Example

#include <log4cplus\logger.h>

#include <log4cplus\configurator.h>

#include <iomanip>

using namespace log4cplus;

intmain()

{

BasicConfigurator config;

config.configure();

Logger logger = Logger::getInstance("main");

LOG4CPLUS_WARN(logger, "Hello, World!");

return 0;

}

ostream Example (Show how to write logging messages.)

#include <log4cplus\logger.h>

#include <log4cplus\configurator.h>

#include <iomanip>

using namespace std;

using namespace log4cplus;

intmain()

{

BasicConfigurator config;

config.configure();

Logger logger = Logger::getInstance("logger");

LOG4CPLUS_WARN(logger, "This is"

<< " a reall"

<< "y long message." << endl

<< "Just testing it out" << endl

<< "What do you think?")

LOG4CPLUS_WARN(logger, "This is a bool: " << true)

LOG4CPLUS_WARN(logger, "This is a char: " << 'x')

LOG4CPLUS_WARN(logger, "This is a short: " << (short)-100)

LOG4CPLUS_WARN(logger, "This is a unsigned short: " << (unsigned short)100)

LOG4CPLUS_WARN(logger, "This is a int: " << (int)1000)

LOG4CPLUS_WARN(logger, "This is a unsigned int: " << (unsigned int)1000)

LOG4CPLUS_WARN(logger, "This is a long(hex): " << hex << (long)100000000)

LOG4CPLUS_WARN(logger, "This is a unsigned long: "

<< (unsigned long)100000000)

LOG4CPLUS_WARN(logger, "This is a float: " << (float)1.2345)

LOG4CPLUS_WARN(logger, "This is a double: "

<< setprecision(15)

<< (double)1.2345234234)

LOG4CPLUS_WARN(logger, "This is a long double: "

<< setprecision(15)

<< (long double)123452342342.342)

return 0;

}

LogLevel Example (Shows how log messages can be filtered at runtime by adjusting the LogLevel.)

#include <log4cplus\logger.h>

#include <log4cplus\configurator.h>

#include <iostream>

using namespace std;

using namespace log4cplus;

Logger logger = Logger::getInstance("main");

void printMessages()

{

LOG4CPLUS_TRACE(logger, "printMessages()");

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");}

intmain()

{

BasicConfigurator config;

config.configure();

logger.setLogLevel(TRACE_LOG_LEVEL);

cout << "*** calling printMessages() with TRACE set: ***" << endl;

printMessages();

logger.setLogLevel(DEBUG_LOG_LEVEL);

cout << "\n*** calling printMessages() with DEBUG set: ***" << endl;

printMessages();

logger.setLogLevel(INFO_LOG_LEVEL);

cout << "\n*** calling printMessages() with INFO set: ***" << endl;

printMessages();

logger.setLogLevel(WARN_LOG_LEVEL);

cout << "\n*** calling printMessages() with WARN set: ***" << endl;

printMessages();

logger.setLogLevel(ERROR_LOG_LEVEL);

cout << "\n*** calling printMessages() with ERROR set: ***" << endl;

printMessages();

logger.setLogLevel(FATAL_LOG_LEVEL);

cout << "\n*** calling printMessages() with FATAL set: ***" << endl;

printMessages();

return 0;

}