- 浏览: 89571 次
最新评论
-
守望者:
我也是出现了同样的错误,应该不是楼上这位仁兄说的Tomcat的 ...
appfuse 1.9.4 错误 -
huanghero0663:
不是tomcat的问题,而是你在电脑上的环境变量写错了,app ...
appfuse 1.9.4 错误
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;
}
发表评论
-
出现java.lang.UnsupportedClassVersionError 错误的原因
2007-04-18 13:55 2281出现java.lang.UnsupportedClassVer ... -
log4cplus学习笔记(一)
2007-03-27 13:00 59067(一)log4cplus是C++编写的开源的日志系统,功能非常 ... -
Java基础知识总结
2007-02-08 16:32 181、对象的初始化 ... -
Java及相关字符集编码问题研究
2007-01-09 10:45 12051. ... -
编写安全的Java代码
2007-01-09 09:54 1254本文是来自Sun官方站点的一篇关于如何编写安全的Java代码的 ... -
Filter学习2
2006-12-14 16:40 1706Servlet2.3 Filter 1、Se ... -
Filter的作用
2006-12-14 15:27 10101你可以在两种情况下使用本文: ·学习过滤器的功用, ·作为你写 ... -
java 日期的处理2
2006-12-12 11:22 63cal1.add(Calendar.WEEK_OF_ ... -
关于Oracle9i日期格式几点要说明的问题
2006-12-12 11:02 751. 在 Oracle9i 之前, 日期格式的数据类型默认格式 ... -
java 日期的处理1
2006-12-12 11:22 40java中常见的日期时间类 Date 类 最基础的日期时间 ... -
SQL语句集
2006-12-12 10:35 62--语 句 功 能 --数据操作 S ... -
JDBC
2006-12-11 11:47 54java 代码 String strDri ...
相关推荐
log4cplus是C++编写的开源的日志系统,功能非常全面,用到自己开发的工程中会比较专业的,:),本文介绍了log4cplus基本概念,以及如何安装,配置。 ### 简介 ### log4cplus是C++编写的开源的日志系统,前身是java...
log4cplus是C++编写的开源的日志系统,前身是java编写的log4j系统.受Apache Software License 保护。作者是Tad E. Smith。log4cplus具有线程安全、灵活、以及多粒度控制的特点,通过将信息划分 优先级使其可以面向...
Qt中第三方日志库log4cplus的基本配置和使用详解案例,仅供参考,不可盲目相信,因为我的编译环境和您的不相同,所以建议参考文章自己编译,文章可参考:https://blog.csdn.net/didi_ya/article/details/123148479
log4cplus-1.2.1库使用说明:编译log4cplus-1.2.1库与调用
visual studio 2017(vc15) 编译的log4cplus 1.2.1,32位,下载即用
log4cplus封装
log4cplus的帮助文档。最新版本1.1.0
log4cplus,一个强大的日志开源代码!
visual studio 2017(vc15) 编译的log4cplus 2.0.2,32位,下载即用
log4cplus使用说明
封装的log4cplus日志库,软件采用vs2008编写,在实际项目中使用,方便高效。
log4cplus是C++编写的开源的日志系统,log为对其的封装类,支持类似Format方式输出日志,方便开发中对日志的编写以及维护
Log4cplus使用指南 Log4cplus使用说明 ,里面有详细的使用用例可以借鉴,想要用Log4cplus输出日志的可以借鉴下。
Log4qt 1.4.2版本 qt专用
log4cplus开源库的Ver1.1.1
Log4cplus使用指南,有利于代码的调试
log4cplus是C++编写的开源的日志系统。log4cplus具有线程安全、灵活、以及多粒度控制的特点。
log4cplus-1.1.3
log4cplus文档chm版本,我已经用doxygen编译完毕,这个使用手册对开发非常有帮助。
C++版的log4j log4cplus