首页 > 代码库 > log4cplus学习笔记(二 )
log4cplus学习笔记(二 )
log4cplus在很多方面做的都很出色,但是使用过程有些地方感觉不爽。在继续吹捧之前我先把不爽之处
稍微提一提,然后继续介绍关于线程和套接字的知识。
### 一些可以改进之处 ###
1. 用户自定义LogLevel的实现机制不够开放
在第五篇中曾经介绍过如何实现用户自行定义LogLevel,为了实现比较理想的效果,甚至还需要改log4cplus
的源代码。:(
2. 生成Logger对象的机制可以改进
我在使用时候,经常需要在不同的文件、函数中操作同一个logger,虽然log4cplus实现了树状存储以及根据
名称生成Logger,却没有充分利用这样的特点确保同一个名称对应的logger对象的唯一性,比如以下代码:
... ...
Logger logger1 = Logger::getInstance("test");
Logger logger2 = Logger::getInstance("test");
Logger * plogger1 = &logger1;
Logger * plogger2 = &logger2;
std::cout << "plogger1: " << plogger1 << std::endl << "plogger2: " << plogger2 << std::endl;
... ...
运行结果:
plogger1: 0xbfffe5a0
plogger2: 0xbfffe580
从结果可以看出,明明是同一个Logger,但每次调用都会产生一个Logger副本,虽然结果是正确的(因为将存
储和操作分开了),但是资源有些浪费,我看了一下log4cplus的代码,其实可以按照如下方式实现(示意性
的):
#include <iostream></iostream>
#include <string></string>
#include
/* forward declaration */
class Logger;
class LoggerContainer
{
public:
~LoggerContainer();
Logger * getinstance(const std::string & strLogger);
private:
typedef std::map<:string,> LoggerMap;
LoggerMap loggerPtrs;
};
class Logger
{
public:
Logger() {std::cout << "ctor of Logger " << std::endl; }
~Logger() {std::cout << "dtor of Logger " << std::endl; }
static Logger * getInstance( const std::string & strLogger)
{
static LoggerContainer defaultLoggerContainer;
return defaultLoggerContainer.getinstance(strLogger);
}
};
LoggerContainer::~LoggerContainer()
{
/* release all ptr in LoggerMap */
LoggerMap::iterator itr = loggerPtrs.begin();
for( ; itr != loggerPtrs.end(); ++itr )
{
delete (*itr).second;
}
}
Logger * LoggerContainer::getinstance(const std::string & strLogger)
{
LoggerMap::iterator itr = loggerPtrs.find(strLogger);
if(itr != loggerPtrs.end())
{
/* logger exist, just return it */
return (*itr).second;
}
else
{
/* return a new logger */
Logger * plogger = new Logger();
loggerPtrs.insert(std::make_pair(strLogger, plogger));
return plogger;
}
}
int main()
{
Logger * plogger1 = Logger::getInstance("test");
Logger * plogger2 = Logger::getInstance("test");
std::cout << "plogger1: " << plogger1 << std::endl << "plogger2: " << plogger2 << std::endl;
return 0;
}
运行结果:
ctor of Logger
plogger1: 0x804fc30
plogger2: 0x804fc30
dtor of Logger
这里的LoggerContainer相当于log4cplus中的Hierarchy类,结果可以看出,通过同一个名称可以获取相同的
Logger实例。
还有一些小毛病比如RollingFileAppender和DailyRollingFileAppender的参数输入顺序可以调整成统一方式
等等,就不细说了。
本部分提到了使用log4cplus时候感觉不爽的地方,最后一部分将介绍一下log4cplus中线程和套接字实现情况
(七)
经过短暂的熟悉过程,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> _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是按照阻塞方式实现的,意味着对其调用直到满足了所接收或发送的个数才返回。
log4cplus的三个例程
http://log4cplus.sourceforge.net/codeexamples.html
里面自带的三个例程
Hello World Example
ostream Example (Show how to write logging messages.)
LogLevel Example (Shows how log messages can be filtered at runtime by adjusting the LogLevel.)
里面自带的三个例程
Hello World Example
#include <log4cplus/logger.h>
#include <log4cplus/configurator.h>
#include <iomanip>
using namespace log4cplus;
int
main()
{
BasicConfigurator config;
config.configure();
Logger logger = Logger::getInstance("main");
LOG4CPLUS_WARN(logger, "Hello, World!");
return 0;
}
#include <log4cplus/configurator.h>
#include <iomanip>
using namespace log4cplus;
int
main()
{
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;
int
main()
{
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;
}
#include <log4cplus/configurator.h>
#include <iomanip>
using namespace std;
using namespace log4cplus;
int
main()
{
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");
}
int
main()
{
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;
}
#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");
}
int
main()
{
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;
}
log4cplus学习笔记(二 )
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。