﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-牵着老婆满街逛-文章分类-Linux编程</title><link>http://www.cppblog.com/tx7do/category/1516.html</link><description>危机感是一种强大前进的动力！&lt;/br&gt;
GMail/GTalk: yanglinbo#google.com;&lt;/br&gt;
MSN/Email: tx7do#yahoo.com.cn;&lt;/br&gt;
QQ: 3 0 3 3 9 6 9 2 0 .</description><language>zh-cn</language><lastBuildDate>Mon, 19 May 2008 13:14:50 GMT</lastBuildDate><pubDate>Mon, 19 May 2008 13:14:50 GMT</pubDate><ttl>60</ttl><item><title>开源日志系统log4cplus(七)</title><link>http://www.cppblog.com/tx7do/articles/11721.html</link><dc:creator>杨粼波</dc:creator><author>杨粼波</author><pubDate>Fri, 25 Aug 2006 20:51:00 GMT</pubDate><guid>http://www.cppblog.com/tx7do/articles/11721.html</guid><wfw:comment>http://www.cppblog.com/tx7do/comments/11721.html</wfw:comment><comments>http://www.cppblog.com/tx7do/articles/11721.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/tx7do/comments/commentRss/11721.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tx7do/services/trackbacks/11721.html</trackback:ping><description><![CDATA[
		<p>经过短暂的熟悉过程，log4cplus已经被成功应用到了我的项目中去了，效果还不错，：）除了上文提及的<br />功能之外，下面将介绍log4cplus提供的线程和套接字的使用情况。<br /></p>
		<pre>### NDC ###</pre>
		<pre>首先我们先了解一下log4cplus中嵌入诊断上下文（Nested Diagnostic Context），即NDC。对log系统而言，<br />当输入源可能不止一个，而只有一个输出时，往往需要分辩所要输出消息的来源，比如服务器处理来自不同<br />客户端的消息时就需要作此判断，NDC可以为交错显示的信息打上一个标记(stamp)， 使得辨认工作看起来<br />比较容易些，呵呵。这个标记是线程特有的，利用了线程局部存储机制，称为线程私有数据（Thread-specific<br /> Data，或TSD）。 看了一下源代码，相关定义如下，包括定义、初始化、获取、设置和清除操作：</pre>
		<pre>linux pthread</pre>
		<pre>
				<br />#   define LOG4CPLUS_THREAD_LOCAL_TYPE pthread_key_t*<br />#   define LOG4CPLUS_THREAD_LOCAL_INIT ::log4cplus::thread::createPthreadKey()<br />#   define LOG4CPLUS_GET_THREAD_LOCAL_VALUE( key ) pthread_getspecific(*key)<br />#   define LOG4CPLUS_SET_THREAD_LOCAL_VALUE( key, value ) pthread_setspecific(*key, value)<br />#   define LOG4CPLUS_THREAD_LOCAL_CLEANUP( key ) pthread_key_delete(*key)</pre>
		<pre>win32</pre>
		<pre>#   define LOG4CPLUS_THREAD_LOCAL_TYPE DWORD<br />#   define LOG4CPLUS_THREAD_LOCAL_INIT TlsAlloc()<br />#   define LOG4CPLUS_GET_THREAD_LOCAL_VALUE( key ) TlsGetValue(key)<br />#   define LOG4CPLUS_SET_THREAD_LOCAL_VALUE( key, value ) \<br />       TlsSetValue(key, static_cast<lpvoid />(value))<br />#   define LOG4CPLUS_THREAD_LOCAL_CLEANUP( key ) TlsFree(key)</pre>
		<pre>
				<br />使用起来比较简单，在某个线程中：</pre>
		<pre>    NDC&amp; ndc = log4cplus::getNDC();<br />    ndc.push("ur ndc string");<br />    LOG4CPLUS_DEBUG(logger, "this is a NDC test");</pre>
		<pre>    ... ...<br />    <br />    ndc.pop();<br />    <br />    ... ...<br />    <br />    LOG4CPLUS_DEBUG(logger, "There should be no NDC...");<br />    ndc.remove();<br />    <br />当设定输出格式(Layout)为TTCCLayout时，输出如下：</pre>
		<pre>10-21-04 21:32:58, [3392] DEBUG test <ur string="" ndc="" /> - this is a NDC test<br />10-21-04 21:32:58, [3392] DEBUG test &lt;&gt; - There should be no NDC...</pre>
		<pre>也可以在自定义的输出格式中使用NDC(用%x) ，比如：</pre>
		<pre>    ... ...<br />    <br />    std::string pattern = "NDC:[%x]  - %m %n";<br />    std::auto_ptr<layout /> _layout(new PatternLayout(pattern));</pre>
		<pre>    ... ...<br />    <br />    LOG4CPLUS_DEBUG(_logger, "This is the FIRST log message...")<br />    NDC&amp; ndc = log4cplus::getNDC();<br />    ndc.push("ur ndc string");<br />    LOG4CPLUS_WARN(_logger, "This is the SECOND log message...")<br />    ndc.pop();<br />    ndc.remove();<br />    <br />    ... ...<br />    <br />输出如下：</pre>
		<pre>NDC:[]  - This is the FIRST log message...<br />NDC:[ur ndc string]  - This is the SECOND log message...</pre>
		<pre>
				<br />另外一种更简单的使用方法是在线程中直接用NDCContextCreator：</pre>
		<pre>    NDCContextCreator _first_ndc("ur ndc string");<br />    LOG4CPLUS_DEBUG(logger, "this is a NDC test")<br />    <br />不必显式地调用push/pop了，而且当出现异常时，能够确保push与pop的调用是匹配的。</pre>
		<pre>    <br />### 线程 ###</pre>
		<pre>线程是log4cplus中的副产品， 而且仅作了最基本的实现，使用起来也异常简单，只要且必须要<br />在派生类中重载run函数即可：</pre>
		<pre>class TestThread : public AbstractThread<br />{<br />public:</pre>
		<pre>    virtual void run();</pre>
		<pre>};</pre>
		<pre>
				<br />void TestThread::run()<br />{<br />    /* do sth. */<br />    ... ...</pre>
		<pre>}</pre>
		<pre>log4cplus的线程没有考虑同步、死锁，有互斥，实现线程切换的小函数挺别致的：</pre>
		<pre>void log4cplus::thread::yield()<br />{<br />#if defined(LOG4CPLUS_USE_PTHREADS)<br />    ::sched_yield();<br />#elif defined(LOG4CPLUS_USE_WIN32_THREADS)<br />    ::Sleep(0);<br />#endif<br />}</pre>
		<pre>
				<br />### 套接字 ###</pre>
		<pre>套接字也是log4cplus中的副产品，在namespace log4cplus::helpers中，实现了C/S方式的日志记录。</pre>
		<pre>1. 客户端程序需要做的工作：</pre>
		<pre>/* 定义一个SocketAppender类型的挂接器 */<br />SharedAppenderPtr _append(new SocketAppender(host, 8888, "ServerName"));</pre>
		<pre>/* 把_append加入到logger中 */<br />Logger::getRoot().addAppender(_append);</pre>
		<pre>/*  SocketAppender类型不需要Layout, 直接调用宏就可以将信息发往loggerServer了 */<br />LOG4CPLUS_INFO(Logger::getRoot(), "This is a test: ")</pre>
		<pre>
				<br />【注】 这里对宏的调用其实是调用了SocketAppender::append，里面有一个数据传输约定，即先发送<br />一个后续数据的总长度，然后再发送实际的数据：</pre>
		<pre>    ... ...</pre>
		<pre>    SocketBuffer buffer = convertToBuffer(event, serverName);<br />    SocketBuffer msgBuffer(LOG4CPLUS_MAX_MESSAGE_SIZE);</pre>
		<pre>    msgBuffer.appendSize_t(buffer.getSize());<br />    msgBuffer.appendBuffer(buffer);<br />   <br />    ... ...</pre>
		<pre>
				<br />2. 服务器端程序需要做的工作：</pre>
		<pre>/* 定义一个ServerSocket */<br />ServerSocket serverSocket(port);</pre>
		<pre> </pre>
		<pre>/* 调用accept函数创建一个新的socket与客户端连接 */<br />Socket sock = serverSocket.accept();</pre>
		<pre>
				<br />此后即可用该sock进行数据read/write了,形如：</pre>
		<pre>SocketBuffer msgSizeBuffer(sizeof(unsigned int));</pre>
		<pre>if(!clientsock.read(msgSizeBuffer))<br />{<br />    return;<br />}</pre>
		<pre>unsigned int msgSize = msgSizeBuffer.readInt();</pre>
		<pre>SocketBuffer buffer(msgSize);</pre>
		<pre>if(!clientsock.read(buffer))<br />{<br />    return;<br />}</pre>
		<pre>为了将读到的数据正常显示出来，需要将SocketBuffer存放的内容转换成InternalLoggingEvent格式：</pre>
		<pre>spi::InternalLoggingEvent event = readFromBuffer(buffer);</pre>
		<pre>然后输出：<br />Logger logger = Logger::getInstance(event.getLoggerName());<br />logger.callAppenders(event);</pre>
		<pre>
				<br />【注】 read/write是按照阻塞方式实现的，意味着对其调用直到满足了所接收或发送的个数才返回。</pre>
<img src ="http://www.cppblog.com/tx7do/aggbug/11721.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tx7do/" target="_blank">杨粼波</a> 2006-08-26 04:51 <a href="http://www.cppblog.com/tx7do/articles/11721.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开源日志系统log4cplus(四)</title><link>http://www.cppblog.com/tx7do/articles/11718.html</link><dc:creator>杨粼波</dc:creator><author>杨粼波</author><pubDate>Fri, 25 Aug 2006 20:50:00 GMT</pubDate><guid>http://www.cppblog.com/tx7do/articles/11718.html</guid><wfw:comment>http://www.cppblog.com/tx7do/comments/11718.html</wfw:comment><comments>http://www.cppblog.com/tx7do/articles/11718.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tx7do/comments/commentRss/11718.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tx7do/services/trackbacks/11718.html</trackback:ping><description><![CDATA[
		<p>将log信息记录到文件应该说是日志系统的一个基本功能，log4cplus在此基础上，提供了更多的功能，可以按照你预先设定的大小来决定是否转储，当超过该大小，后续log信息会另存到新文件中，依次类推；或者按照日期来决定是否转储。本文将详细介绍这些用法。</p>
		<br />
		<pre>
				<br />### 如何将log记录到文件 ###</pre>
		<pre>我们在例5中给出了一个将log记录到文件的例子，用的是FileAppender类实现的，log4cplus提供了三个类用于<br />文件操作，它们是FileAppender类、RollingFileAppender类、DailyRollingFileAppender类。</pre>
		<pre>1. FileAppender类</pre>
		<pre>实现了基本的文件操作功能，构造函数如下：</pre>
		<pre>FileAppender(const log4cplus::tstring&amp; filename,<br />                     LOG4CPLUS_OPEN_MODE_TYPE mode = LOG4CPLUS_FSTREAM_NAMESPACE::ios::trunc,<br />                     bool immediateFlush = true);<br />                     <br />filename       : 文件名<br />mode           : 文件类型，可选择的文件类型包括app、ate、binary、in、out、trunc，因为实际上只是对<br />                 stl的一个简单包装，呵呵，这里就不多讲了。缺省是trunc，表示将先前文件删除。<br />immediateFlush ：缓冲刷新标志，如果为true表示每向文件写一条记录就刷新一次缓存，否则直到FileAppender<br />                 被关闭或文件缓存已满才更新文件，一般是要设置true的，比如你往文件写的过程中出现<br />                 了错误（如程序非正常退出），即使文件没有正常关闭也可以保证程序终止时刻之前的所有<br />                 记录都会被正常保存。</pre>
		<pre>FileAppender类的使用情况请参考例5，这里不再赘述。</pre>
		<pre>
				<br />2. RollingFileAppender类</pre>
		<pre>构造函数如下：<br />log4cplus::RollingFileAppender::RollingFileAppender(const log4cplus::tstring&amp; filename,<br />                                                    long maxFileSize,<br />                                                    int maxBackupIndex,<br />                                                    bool immediateFlush)</pre>
		<pre>filename       : 文件名<br />maxFileSize    : 文件的最大尺寸<br />maxBackupIndex : 最大记录文件数<br />immediateFlush : 缓冲刷新标志<br />                                                    <br />RollingFileAppender类可以根据你预先设定的大小来决定是否转储，当超过该大小，后续log信息会另存到新<br />文件中，除了定义每个记录文件的大小之外，你还要确定在RollingFileAppender类对象构造时最多需要多少个<br />这样的记录文件(maxBackupIndex+1)，当存储的文件数目超过maxBackupIndex+1时，会删除最早生成的文件，<br />保证整个文件数目等于maxBackupIndex+1。然后继续记录，比如以下代码片段：</pre>
		<pre>    ... ...<br />    <br />    #define LOOP_COUNT 200000<br />    <br />    SharedAppenderPtr _append(new RollingFileAppender("Test.log", 5*1024, 5));<br />    _append-&gt;setName("file test");<br />    _append-&gt;setLayout( std::auto_ptr<layout />(new TTCCLayout()) );<br />    Logger::getRoot().addAppender(_append);</pre>
		<pre>    Logger root = Logger::getRoot();<br />    Logger test = Logger::getInstance("test");<br />    Logger subTest = Logger::getInstance("test.subtest");</pre>
		<pre>    for(int i=0; i<loop_count; />    {<br />        NDCContextCreator _context("loop");<br />        LOG4CPLUS_DEBUG(subTest, "Entering loop #" &lt;&lt; i)<br />    }<br />    <br />    ... ...<br />    </pre>
		<pre>运行结果：</pre>
		<pre>运行后会产生6个输出文件，Test.log、Test.log.1、Test.log.2、Test.log.3、Test.log.4、Test.log.5<br />其中Test.log存放着最新写入的信息，而最后一个文件中并不包含第一个写入信息，说明已经被不断更新了。<br />需要指出的是，这里除了Test.log之外，每个文件的大小都是200K,而不是我们想像中的5K，这是因为<br />log4cplus中隐含定义了文件的最小尺寸是200K，只有大于200K的设置才生效，&lt;= 200k的设置都会被认为是<br />200K.</pre>
		<pre>
				<br />3. DailyRollingFileAppender类</pre>
		<pre>构造函数如下：<br />DailyRollingFileAppender::DailyRollingFileAppender(const log4cplus::tstring&amp; filename,<br />                                                   DailyRollingFileSchedule schedule,<br />                                                   bool immediateFlush,<br />                                                   int maxBackupIndex)<br />                                                   <br />filename       : 文件名<br />schedule       : 存储频度<br />immediateFlush : 缓冲刷新标志<br />maxBackupIndex : 最大记录文件数</pre>
		<pre>DailyRollingFileAppender类可以根据你预先设定的频度来决定是否转储，当超过该频度，后续log信息会另存<br />到新文件中，这里的频度包括：MONTHLY（每月）、WEEKLY（每周）、DAILY（每日）、TWICE_DAILY（每两天）、<br />HOURLY（每时）、MINUTELY（每分）。maxBackupIndex的含义同上所述，比如以下代码片段：</pre>
		<pre>    ... ...<br />    <br />    SharedAppenderPtr _append(new DailyRollingFileAppender("Test.log", MINUTELY, true, 5));<br />    _append-&gt;setName("file test");<br />    _append-&gt;setLayout( std::auto_ptr<layout />(new TTCCLayout()) );<br />    Logger::getRoot().addAppender(_append);</pre>
		<pre>    Logger root = Logger::getRoot();<br />    Logger test = Logger::getInstance("test");<br />    Logger subTest = Logger::getInstance("test.subtest");</pre>
		<pre>    for(int i=0; i<loop_count; />    {<br />        NDCContextCreator _context("loop");<br />        LOG4CPLUS_DEBUG(subTest, "Entering loop #" &lt;&lt; i)<br />    }<br />    <br />    ... ...</pre>
		<pre>
				<br />运行结果：</pre>
		<pre>运行后会以分钟为单位，分别生成名为Test.log.2004-10-17-03-03、Test.log.2004-10-17-03-04和<br />Test.log.2004-10-17-03-05这样的文件。</pre>
		<pre>需要指出的是，刚看到按照频度（如HOURLY、MINUTELY）转储这样的概念，以为log4cplus提供了内部定时器，<br />感觉很奇怪，因为日志系统不应该主动记录，而loging事件总是应该被动触发的啊。仔细看了源代码后才知道<br />这里的"频度"并不是你写入文件的速度，其实是否转储的标准并不依赖你写入文件的速度，而是依赖于写入<br />的那一时刻是否满足了频度条件，即是否超过了以分钟、小时、周、月为单位的时间刻度，如果超过了就另存。</pre>
		<pre>本部分详细介绍log信息的几种文件操作方式，下面将重点介绍一下如何有选择地控制log信息的输出。</pre>
<img src ="http://www.cppblog.com/tx7do/aggbug/11718.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tx7do/" target="_blank">杨粼波</a> 2006-08-26 04:50 <a href="http://www.cppblog.com/tx7do/articles/11718.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开源日志系统log4cplus(五)</title><link>http://www.cppblog.com/tx7do/articles/11719.html</link><dc:creator>杨粼波</dc:creator><author>杨粼波</author><pubDate>Fri, 25 Aug 2006 20:50:00 GMT</pubDate><guid>http://www.cppblog.com/tx7do/articles/11719.html</guid><wfw:comment>http://www.cppblog.com/tx7do/comments/11719.html</wfw:comment><comments>http://www.cppblog.com/tx7do/articles/11719.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tx7do/comments/commentRss/11719.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tx7do/services/trackbacks/11719.html</trackback:ping><description><![CDATA[
		<pre>日志系统的另一个基本功能就是能够让使用者按照自己的意愿来控制什么时候，哪些log信息可以输出。<br />如果能够让用户在任意时刻设置允许输出的LogLevel的信息就好了，log4cplus通过LogLevelManager、<br />LogLog、Filter三种方式实现了上述功能。</pre>
		<br />
		<pre>
				<br />### 优先级控制 ###</pre>
		<pre>在研究LogLevelManager之前，首先介绍一下log4cplus中logger的存储机制，在log4cplus中，所有<br />logger都通过一个层次化的结构（其实内部是hash表）来组织的，有一个Root级别的logger,可以通<br />过以下方法获取：</pre>
		<pre>    Logger root = Logger::getRoot();<br />    <br />用户定义的logger都有一个名字与之对应，比如：</pre>
		<pre>    Logger test = Logger::getInstance("test");<br />    <br />可以定义该logger的子logger:</pre>
		<pre>    Logger subTest = Logger::getInstance("test.subtest");<br />    <br />注意Root级别的logger只有通过getRoot方法获取，Logger::getInstance("root")获得的是它的<br />子对象而已。有了这些具有父子关系的logger之后可分别设置其LogLevel,比如：</pre>
		<pre>root.setLogLevel( ... );<br />Test.setLogLevel( ... );<br />subTest.setLogLevel( ... );</pre>
		<pre>
				<br />logger的这种父子关联性会体现在优先级控制方面，log4cplus将输出的log信息按照LogLevel<br />（从低到高）分为：</pre>
		<pre>NOT_SET_LOG_LEVEL (   -1) ：接受缺省的LogLevel，如果有父logger则继承它的LogLevel<br />ALL_LOG_LEVEL     (    0) ：开放所有log信息输出<br />TRACE_LOG_LEVEL   (    0) ：开放trace信息输出(即ALL_LOG_LEVEL)<br />DEBUG_LOG_LEVEL   (10000) ：开放debug信息输出<br />INFO_LOG_LEVEL    (20000) ：开放info信息输出<br />WARN_LOG_LEVEL    (30000) ：开放warning信息输出<br />ERROR_LOG_LEVEL   (40000) ：开放error信息输出<br />FATAL_LOG_LEVEL   (50000) ：开放fatal信息输出<br />OFF_LOG_LEVEL     (60000) ：关闭所有log信息输出</pre>
		<pre>LogLevelManager负责设置logger的优先级，各个logger可以通过setLogLevel设置自己的优先级，<br />当某个logger的LogLevel设置成NOT_SET_LOG_LEVEL时，该logger会继承父logger的优先级，另外，<br />如果定义了重名的多个logger, 对其中任何一个的修改都会同时改变其它logger,我们举例说明：</pre>
		<pre>〖例6〗</pre>
		<pre>#include "log4cplus/logger.h"<br />#include "log4cplus/consoleappender.h"<br />#include "log4cplus/loglevel.h"<br />#include &lt;iostream&gt;<iostream /></pre>
		<pre>using namespace std;<br />using namespace log4cplus;</pre>
		<pre>int main()<br />{<br />    SharedAppenderPtr _append(new ConsoleAppender());<br />    _append-&gt;setName("test");<br />    Logger::getRoot().addAppender(_append);<br />    Logger root = Logger::getRoot();</pre>
		<pre>    Logger test = Logger::getInstance("test");<br />    Logger subTest = Logger::getInstance("test.subtest");<br />    LogLevelManager&amp; llm = getLogLevelManager();</pre>
		<pre>    cout &lt;&lt; endl &lt;&lt; "Before Setting, Default LogLevel" &lt;&lt; endl;<br />    LOG4CPLUS_FATAL(root, "root: " &lt;&lt; llm.toString(root.getChainedLogLevel()))<br />    LOG4CPLUS_FATAL(root, "test: " &lt;&lt; llm.toString(test.getChainedLogLevel()))<br />    LOG4CPLUS_FATAL(root, "test.subtest: " &lt;&lt; llm.toString(subTest.getChainedLogLevel()))</pre>
		<pre>    cout &lt;&lt; endl &lt;&lt; "Setting test.subtest to WARN" &lt;&lt; endl;<br />    subTest.setLogLevel(WARN_LOG_LEVEL);<br />    LOG4CPLUS_FATAL(root, "root: " &lt;&lt; llm.toString(root.getChainedLogLevel()))<br />    LOG4CPLUS_FATAL(root, "test: " &lt;&lt; llm.toString(test.getChainedLogLevel()))<br />    LOG4CPLUS_FATAL(root, "test.subtest: " &lt;&lt; llm.toString(subTest.getChainedLogLevel()))</pre>
		<pre>    cout &lt;&lt; endl &lt;&lt; "Setting test.subtest to TRACE" &lt;&lt; endl;<br />    test.setLogLevel(TRACE_LOG_LEVEL);<br />    LOG4CPLUS_FATAL(root, "root: " &lt;&lt; llm.toString(root.getChainedLogLevel()))<br />    LOG4CPLUS_FATAL(root, "test: " &lt;&lt; llm.toString(test.getChainedLogLevel()))<br />    LOG4CPLUS_FATAL(root, "test.subtest: " &lt;&lt; llm.toString(subTest.getChainedLogLevel()))</pre>
		<pre>    cout &lt;&lt; endl &lt;&lt; "Setting test.subtest to NO_LEVEL" &lt;&lt; endl;<br />    subTest.setLogLevel(NOT_SET_LOG_LEVEL);<br />    LOG4CPLUS_FATAL(root, "root: " &lt;&lt; llm.toString(root.getChainedLogLevel()))<br />    LOG4CPLUS_FATAL(root, "test: " &lt;&lt; llm.toString(test.getChainedLogLevel()))<br />    LOG4CPLUS_FATAL(root, "test.subtest: " &lt;&lt; llm.toString(subTest.getChainedLogLevel()) &lt;&lt; '\n')</pre>
		<pre>    cout &lt;&lt; "create a logger test_bak, named \"test_\", too. " &lt;&lt; endl;<br />    Logger test_bak = Logger::getInstance("test");<br />    cout &lt;&lt; "Setting test to INFO, so test_bak also be set to INFO" &lt;&lt; endl;<br />    test.setLogLevel(INFO_LOG_LEVEL);<br />    LOG4CPLUS_FATAL(root, "test: " &lt;&lt; llm.toString(test.getChainedLogLevel()))<br />    LOG4CPLUS_FATAL(root, "test_bak: " &lt;&lt; llm.toString(test_bak.getChainedLogLevel()))</pre>
		<pre>    return 0;<br />}</pre>
		<pre>输出结果：</pre>
		<pre>Before Setting, Default LogLevel<br />FATAL - root: DEBUG<br />FATAL - test: DEBUG<br />FATAL - test.subtest: DEBUG</pre>
		<pre>Setting test.subtest to WARN<br />FATAL - root: DEBUG<br />FATAL - test: DEBUG<br />FATAL - test.subtest: WARN</pre>
		<pre>Setting test.subtest to TRACE<br />FATAL - root: DEBUG<br />FATAL - test: TRACE<br />FATAL - test.subtest: WARN</pre>
		<pre>Setting test.subtest to NO_LEVEL<br />FATAL - root: DEBUG<br />FATAL - test: TRACE<br />FATAL - test.subtest: TRACE</pre>
		<pre>create a logger test_bak, named "test_", too.<br />Setting test to INFO, so test_bak also be set to INFO<br />FATAL - test: INFO<br />FATAL - test_bak: INFO</pre>
		<pre>
				<br />下面的例子演示了如何通过设置LogLevel来控制用户的log信息输出：</pre>
		<pre>〖例7〗</pre>
		<pre>#include "log4cplus/logger.h"<br />#include "log4cplus/consoleappender.h"<br />#include "log4cplus/loglevel.h"<br />#include &lt;iostream&gt;<iostream /></pre>
		<pre>using namespace std;<br />using namespace log4cplus;</pre>
		<pre>void ShowMsg(void)<br />{<br />    LOG4CPLUS_TRACE(Logger::getRoot(),"info")<br />    LOG4CPLUS_DEBUG(Logger::getRoot(),"info")<br />    LOG4CPLUS_INFO(Logger::getRoot(),"info")<br />    LOG4CPLUS_WARN(Logger::getRoot(),"info")<br />    LOG4CPLUS_ERROR(Logger::getRoot(),"info")<br />    LOG4CPLUS_FATAL(Logger::getRoot(),"info")<br />}</pre>
		<pre>int main()<br />{<br />    SharedAppenderPtr _append(new ConsoleAppender());<br />    _append-&gt;setName("test");<br />    _append-&gt;setLayout(std::auto_ptr<layout />(new TTCCLayout()));<br />    Logger root = Logger::getRoot();<br />    root.addAppender(_append);</pre>
		<pre>    cout &lt;&lt; endl &lt;&lt; "all-log allowed" &lt;&lt; endl;<br />    root.setLogLevel(ALL_LOG_LEVEL);<br />    ShowMsg();</pre>
		<pre>    cout &lt;&lt; endl &lt;&lt; "trace-log and above allowed" &lt;&lt; endl;<br />    root.setLogLevel(TRACE_LOG_LEVEL);<br />    ShowMsg();</pre>
		<pre>    cout &lt;&lt; endl &lt;&lt; "debug-log and above allowed" &lt;&lt; endl;<br />    root.setLogLevel(DEBUG_LOG_LEVEL);<br />    ShowMsg();</pre>
		<pre>    cout &lt;&lt; endl &lt;&lt; "info-log and above allowed" &lt;&lt; endl;<br />    root.setLogLevel(INFO_LOG_LEVEL);<br />    ShowMsg();</pre>
		<pre>    cout &lt;&lt; endl &lt;&lt; "warn-log and above allowed" &lt;&lt; endl;<br />    root.setLogLevel(WARN_LOG_LEVEL);<br />    ShowMsg();</pre>
		<pre>    cout &lt;&lt; endl &lt;&lt; "error-log and above allowed" &lt;&lt; endl;<br />    root.setLogLevel(ERROR_LOG_LEVEL);<br />    ShowMsg();</pre>
		<pre>    cout &lt;&lt; endl &lt;&lt; "fatal-log and above allowed" &lt;&lt; endl;<br />    root.setLogLevel(FATAL_LOG_LEVEL);<br />    ShowMsg();</pre>
		<pre>    cout &lt;&lt; endl &lt;&lt; "log disabled" &lt;&lt; endl;<br />    root.setLogLevel(OFF_LOG_LEVEL);<br />    ShowMsg();</pre>
		<pre>    return 0;<br />}</pre>
		<pre>输出结果：</pre>
		<pre>all-log allowed<br />10-17-04 10:11:40,587 [1075298944] TRACE root &lt;&gt; - info<br />10-17-04 10:11:40,590 [1075298944] DEBUG root &lt;&gt; - info<br />10-17-04 10:11:40,591 [1075298944] INFO root &lt;&gt; - info<br />10-17-04 10:11:40,591 [1075298944] WARN root &lt;&gt; - info<br />10-17-04 10:11:40,592 [1075298944] ERROR root &lt;&gt; - info<br />10-17-04 10:11:40,592 [1075298944] FATAL root &lt;&gt; - info</pre>
		<pre>trace-log and above allowed<br />10-17-04 10:11:40,593 [1075298944] TRACE root &lt;&gt; - info<br />10-17-04 10:11:40,593 [1075298944] DEBUG root &lt;&gt; - info<br />10-17-04 10:11:40,594 [1075298944] INFO root &lt;&gt; - info<br />10-17-04 10:11:40,594 [1075298944] WARN root &lt;&gt; - info<br />10-17-04 10:11:40,594 [1075298944] ERROR root &lt;&gt; - info<br />10-17-04 10:11:40,594 [1075298944] FATAL root &lt;&gt; - info</pre>
		<pre>debug-log and above allowed<br />10-17-04 10:11:40,595 [1075298944] DEBUG root &lt;&gt; - info<br />10-17-04 10:11:40,595 [1075298944] INFO root &lt;&gt; - info<br />10-17-04 10:11:40,596 [1075298944] WARN root &lt;&gt; - info<br />10-17-04 10:11:40,596 [1075298944] ERROR root &lt;&gt; - info<br />10-17-04 10:11:40,596 [1075298944] FATAL root &lt;&gt; - info</pre>
		<pre>info-log and above allowed<br />10-17-04 10:11:40,597 [1075298944] INFO root &lt;&gt; - info<br />10-17-04 10:11:40,597 [1075298944] WARN root &lt;&gt; - info<br />10-17-04 10:11:40,597 [1075298944] ERROR root &lt;&gt; - info<br />10-17-04 10:11:40,598 [1075298944] FATAL root &lt;&gt; - info</pre>
		<pre>warn-log and above allowed<br />10-17-04 10:11:40,598 [1075298944] WARN root &lt;&gt; - info<br />10-17-04 10:11:40,598 [1075298944] ERROR root &lt;&gt; - info<br />10-17-04 10:11:40,599 [1075298944] FATAL root &lt;&gt; - info</pre>
		<pre>error-log and above allowed<br />10-17-04 10:11:40,599 [1075298944] ERROR root &lt;&gt; - info<br />10-17-04 10:11:40,600 [1075298944] FATAL root &lt;&gt; - info</pre>
		<pre>fatal-log and above allowed<br />10-17-04 10:11:40,600 [1075298944] FATAL root &lt;&gt; - info</pre>
		<pre>log disabled</pre>
		<pre> </pre>
		<pre>用户也可以自行定义LogLevel，操作比较简单，首先要定义LEVEL值，比如HELLO_LOG_LEVEL定义如下：</pre>
		<pre>/* DEBUG_LOG_LEVEL  &lt; HELLO_LOG_LEVEL &lt; INFO_LOG_LEVEL */<br />const LogLevel HELLO_LOG_LEVEL = 15000;</pre>
		<pre>然后定义以下宏即可：</pre>
		<pre>/* define MACRO LOG4CPLUS_HELLO */<br />#define LOG4CPLUS_HELLO(logger, logEvent) \<br />    if(logger.isEnabledFor(HELLO_LOG_LEVEL)) { \<br />        log4cplus::tostringstream _log4cplus_buf; \<br />        _log4cplus_buf &lt;&lt; logEvent; \<br /> logger.forcedLog(HELLO_LOG_LEVEL, _log4cplus_buf.str(), __FILE__, __LINE__); \<br />    }</pre>
		<pre>不过log4cplus没有提供给用户一个接口来实现LEVEL值与字符串的转换，所以当带格式输出LogLevel字符<br />串时候会显示"UNKNOWN"， 不够理想。比如用TTCCLayout控制输出的结果可能会如下所示：</pre>
		<pre>10-17-04 11:17:51,124 [1075298944] UNKNOWN root &lt;&gt; - info</pre>
		<pre>而不是期望的以下结果：<br />10-17-04 11:17:51,124 [1075298944] HELLO root &lt;&gt; - info</pre>
		<pre>要想实现第二种结果，按照log4cplus现有的接口机制，只能改其源代码后重新编译，方法是在loglevel.cxx<br />中加入：</pre>
		<pre>#define _HELLO_STRING LOG4CPLUS_TEXT("HELLO")</pre>
		<pre>然后修改log4cplus::tstring  defaultLogLevelToStringMethod(LogLevel ll)函数，增加一个判断：</pre>
		<pre>case HELLO_LOG_LEVEL:    return _HELLO_STRING;</pre>
		<pre>重新编译log4cplus源代码后生成库文件，再使用时即可实现满意效果。</pre>
		<pre>
				<br />### 调试模式 ###</pre>
		<pre>即通过loglog来控制输出调试、警告或错误信息，见例4，这里不再赘述。</pre>
		<pre> </pre>
		<pre>### 基于脚本配置来过滤log信息 ###</pre>
		<pre>除了通过程序实现对log环境的配置之外，log4cplus通过PropertyConfigurator类实现了基于脚本配置的功能。<br />通过脚本可以完成对logger、appender和layout的配置，因此可以解决怎样输出，输出到哪里的问题，我将在<br />全文的最后一部分中提到多线程环境中如何利用脚本配置来配合实现性能测试，本节将重点介绍基脚本实现过<br />滤log信息的功能。</pre>
		<pre>首先简单介绍一下脚本的语法规则：</pre>
		<pre>包括Appender的配置语法和logger的配置语法，其中：</pre>
		<pre>1.Appender的配置语法:</pre>
		<pre>（1）设置名称：</pre>
		<pre>/*设置方法*/<br />log4cplus.appender.appenderName=fully.qualified.name.of.appender.class</pre>
		<pre>例如（列举了所有可能的Appender，其中SocketAppender后面会讲到）：<br />log4cplus.appender.append_1=log4cplus::ConsoleAppender<br />log4cplus.appender.append_2=log4cplus::FileAppender<br />log4cplus.appender.append_3=log4cplus::RollingFileAppender<br />log4cplus.appender.append_4=log4cplus::DailyRollingFileAppender<br />log4cplus.appender.append_4=log4cplus::SocketAppender</pre>
		<pre>（2）设置Filter：</pre>
		<pre>包括选择过滤器和设置过滤条件，可选择的过滤器包括：LogLevelMatchFilter、LogLevelRangeFilter、<br />和StringMatchFilter：</pre>
		<pre>对LogLevelMatchFilter来说，过滤条件包括LogLevelToMatch和AcceptOnMatch（true|false）， 只有<br />当log信息的LogLevel值与LogLevelToMatch相同，且AcceptOnMatch为true时才会匹配。</pre>
		<pre>LogLevelRangeFilter来说，过滤条件包括LogLevelMin、LogLevelMax和AcceptOnMatch，只有当log信息<br />的LogLevel在LogLevelMin、LogLevelMax之间同时AcceptOnMatch为true时才会匹配。</pre>
		<pre>对StringMatchFilter来说，过滤条件包括StringToMatch和AcceptOnMatch，只有当log信息的LogLevel值<br />与StringToMatch对应的LogLevel值与相同， 且AcceptOnMatch为true时会匹配。</pre>
		<pre>
				<br />过滤条件处理机制类似于IPTABLE的Responsibility chain，（即先deny、再allow）不过执行顺序刚好相反，<br />后写的条件会被先执行，比如：</pre>
		<pre>log4cplus.appender.append_1.filters.1=log4cplus::spi::LogLevelMatchFilter<br />log4cplus.appender.append_1.filters.1.LogLevelToMatch=TRACE<br />log4cplus.appender.append_1.filters.1.AcceptOnMatch=true<br />#log4cplus.appender.append_1.filters.2=log4cplus::spi::DenyAllFilter</pre>
		<pre>会首先执行filters.2的过滤条件，关闭所有过滤器，然后执行filters.1，仅匹配TRACE信息。</pre>
		<pre>（3）设置Layout</pre>
		<pre>可以选择不设置、TTCCLayout、或PatternLayout</pre>
		<pre>如果不设置，会输出简单格式的log信息。</pre>
		<pre>设置TTCCLayout如下所示：<br />log4cplus.appender.ALL_MSGS.layout=log4cplus::TTCCLayout</pre>
		<pre>设置PatternLayout如下所示：<br />log4cplus.appender.append_1.layout=log4cplus::PatternLayout<br />log4cplus.appender.append_1.layout.ConversionPattern=%d{%m/%d/%y %H:%M:%S,%Q} [%t] %-5p - %m%n</pre>
		<pre>
				<br />2.logger的配置语法</pre>
		<pre>包括rootLogger和non-root logger。</pre>
		<pre>对于rootLogger来说：<br />log4cplus.rootLogger=[LogLevel], appenderName, appenderName, ...</pre>
		<pre>对于non-root logger来说：<br />log4cplus.logger.logger_name=[LogLevel|INHERITED], appenderName, appenderName, ...</pre>
		<pre>
				<br />脚本方式使用起来非常简单，只要首先加载配置即可（urconfig.properties是自行定义的配置文件）：</pre>
		<pre>PropertyConfigurator::doConfigure("urconfig.properties");</pre>
		<pre>
				<br />下面我们通过例子体会一下log4cplus强大的基于脚本过滤log信息的功能。</pre>
		<pre>
				<br />〖例8〗</pre>
		<pre>/*<br /> *    urconfig.properties<br /> */<br />log4cplus.rootLogger=TRACE, ALL_MSGS, TRACE_MSGS, DEBUG_INFO_MSGS, FATAL_MSGS</pre>
		<pre>log4cplus.appender.ALL_MSGS=log4cplus::RollingFileAppender<br />log4cplus.appender.ALL_MSGS.File=all_msgs.log<br />log4cplus.appender.ALL_MSGS.layout=log4cplus::TTCCLayout</pre>
		<pre>log4cplus.appender.TRACE_MSGS=log4cplus::RollingFileAppender<br />log4cplus.appender.TRACE_MSGS.File=trace_msgs.log<br />log4cplus.appender.TRACE_MSGS.layout=log4cplus::TTCCLayout<br />log4cplus.appender.TRACE_MSGS.filters.1=log4cplus::spi::LogLevelMatchFilter<br />log4cplus.appender.TRACE_MSGS.filters.1.LogLevelToMatch=TRACE<br />log4cplus.appender.TRACE_MSGS.filters.1.AcceptOnMatch=true<br />log4cplus.appender.TRACE_MSGS.filters.2=log4cplus::spi::DenyAllFilter</pre>
		<pre>log4cplus.appender.DEBUG_INFO_MSGS=log4cplus::RollingFileAppender<br />log4cplus.appender.DEBUG_INFO_MSGS.File=debug_info_msgs.log<br />log4cplus.appender.DEBUG_INFO_MSGS.layout=log4cplus::TTCCLayout<br />log4cplus.appender.DEBUG_INFO_MSGS.filters.1=log4cplus::spi::LogLevelRangeFilter<br />log4cplus.appender.DEBUG_INFO_MSGS.filters.1.LogLevelMin=DEBUG<br />log4cplus.appender.DEBUG_INFO_MSGS.filters.1.LogLevelMax=INFO<br />log4cplus.appender.DEBUG_INFO_MSGS.filters.1.AcceptOnMatch=true<br />log4cplus.appender.DEBUG_INFO_MSGS.filters.2=log4cplus::spi::DenyAllFilter</pre>
		<pre>log4cplus.appender.FATAL_MSGS=log4cplus::RollingFileAppender<br />log4cplus.appender.FATAL_MSGS.File=fatal_msgs.log<br />log4cplus.appender.FATAL_MSGS.layout=log4cplus::TTCCLayout<br />log4cplus.appender.FATAL_MSGS.filters.1=log4cplus::spi::StringMatchFilter<br />log4cplus.appender.FATAL_MSGS.filters.1.StringToMatch=FATAL<br />log4cplus.appender.FATAL_MSGS.filters.1.AcceptOnMatch=true<br />log4cplus.appender.FATAL_MSGS.filters.2=log4cplus::spi::DenyAllFilter</pre>
		<pre>
				<br />/*<br /> *    main.cpp<br /> */<br />#include &lt;log4cplus/logger.h&gt;<br />#include &lt;log4cplus/configurator.h&gt;<br />#include &lt;log4cplus/helpers/stringhelper.h&gt;<log4cplus /></pre>
		<pre>using namespace log4cplus;</pre>
		<pre>static Logger logger = Logger::getInstance("log");</pre>
		<pre>void printDebug()<br />{<br />    LOG4CPLUS_TRACE_METHOD(logger, "::printDebug()");<br />    LOG4CPLUS_DEBUG(logger, "This is a DEBUG message");<br />    LOG4CPLUS_INFO(logger, "This is a INFO message");<br />    LOG4CPLUS_WARN(logger, "This is a WARN message");<br />    LOG4CPLUS_ERROR(logger, "This is a ERROR message");<br />    LOG4CPLUS_FATAL(logger, "This is a FATAL message");<br />}<br />int main()<br />{<br />    Logger root = Logger::getRoot();<br />    PropertyConfigurator::doConfigure("urconfig.properties");<br />    printDebug();</pre>
		<pre>    return 0;<br />}</pre>
		<pre>运行结果：</pre>
		<pre>1. all_msgs.log<br />10-17-04 14:55:25,858 [1075298944] TRACE log &lt;&gt; - ENTER: ::printDebug()<br />10-17-04 14:55:25,871 [1075298944] DEBUG log &lt;&gt; - This is a DEBUG message<br />10-17-04 14:55:25,873 [1075298944] INFO log &lt;&gt; - This is a INFO message<br />10-17-04 14:55:25,873 [1075298944] WARN log &lt;&gt; - This is a WARN message<br />10-17-04 14:55:25,874 [1075298944] ERROR log &lt;&gt; - This is a ERROR message<br />10-17-04 14:55:25,874 [1075298944] FATAL log &lt;&gt; - This is a FATAL message<br />10-17-04 14:55:25,875 [1075298944] TRACE log &lt;&gt; - EXIT:  ::printDebug()</pre>
		<pre>2. trace_msgs.log<br />10-17-04 14:55:25,858 [1075298944] TRACE log &lt;&gt; - ENTER: ::printDebug()<br />10-17-04 14:55:25,875 [1075298944] TRACE log &lt;&gt; - EXIT:  ::printDebug()</pre>
		<pre>3. debug_info_msgs.log<br />10-17-04 14:55:25,871 [1075298944] DEBUG log &lt;&gt; - This is a DEBUG message<br />10-17-04 14:55:25,873 [1075298944] INFO log &lt;&gt; - This is a INFO message</pre>
		<pre>4. fatal_msgs.log<br />10-17-04 14:55:25,874 [1075298944] FATAL log &lt;&gt; - This is a FATAL message</pre>
		<pre> </pre>
		<pre>本部分详细介绍了如何有选择地控制log信息的输出，最后一部分我们将介绍一下多线程、<br />和C/S模式下该如何操作，顺便提一下NDC的概念。</pre>
<img src ="http://www.cppblog.com/tx7do/aggbug/11719.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tx7do/" target="_blank">杨粼波</a> 2006-08-26 04:50 <a href="http://www.cppblog.com/tx7do/articles/11719.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开源日志系统log4cplus(六)</title><link>http://www.cppblog.com/tx7do/articles/11720.html</link><dc:creator>杨粼波</dc:creator><author>杨粼波</author><pubDate>Fri, 25 Aug 2006 20:50:00 GMT</pubDate><guid>http://www.cppblog.com/tx7do/articles/11720.html</guid><wfw:comment>http://www.cppblog.com/tx7do/comments/11720.html</wfw:comment><comments>http://www.cppblog.com/tx7do/articles/11720.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tx7do/comments/commentRss/11720.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tx7do/services/trackbacks/11720.html</trackback:ping><description><![CDATA[
		<p>log4cplus在很多方面做的都很出色，但是使用过程有些地方感觉不爽。在继续吹捧之前我先把不爽之处<br />稍微提一提，然后继续介绍关于线程和套接字的知识。<br /></p>
		<pre>### 一些可以改进之处 ###</pre>
		<pre>1. 用户自定义LogLevel的实现机制不够开放</pre>
		<pre>在第五篇中曾经介绍过如何实现用户自行定义LogLevel，为了实现比较理想的效果，甚至还需要改log4cplus<br />的源代码。：（</pre>
		<pre>2. 生成Logger对象的机制可以改进</pre>
		<pre>我在使用时候，经常需要在不同的文件、函数中操作同一个logger，虽然log4cplus实现了树状存储以及根据<br />名称生成Logger，却没有充分利用这样的特点确保同一个名称对应的logger对象的唯一性，比如以下代码：</pre>
		<pre>    ... ...<br />    <br />    Logger logger1 = Logger::getInstance("test");<br />    Logger logger2 = Logger::getInstance("test");</pre>
		<pre>    Logger * plogger1 = &amp;logger1;<br />    Logger * plogger2 = &amp;logger2;</pre>
		<pre>    std::cout &lt;&lt; "plogger1: " &lt;&lt; plogger1 &lt;&lt; std::endl &lt;&lt; "plogger2: " &lt;&lt; plogger2 &lt;&lt; std::endl;<br />    <br />    ... ...<br />    <br />    <br />运行结果：</pre>
		<pre>plogger1: 0xbfffe5a0<br />plogger2: 0xbfffe580</pre>
		<pre>
				<br />从结果可以看出，明明是同一个Logger，但每次调用都会产生一个Logger副本，虽然结果是正确的（因为将存<br />储和操作分开了），但是资源有些浪费，我看了一下log4cplus的代码，其实可以按照如下方式实现（示意性<br />的）：</pre>
		<pre>#include &lt;iostream&gt;<br />#include &lt;string&gt;<br />#include &lt;map&gt;<map></map></pre>
		<pre>/* forward declaration */<br />class Logger;</pre>
		<pre>class LoggerContainer<br />{<br />public:</pre>
		<pre>    ~LoggerContainer();</pre>
		<pre>    Logger * getinstance(const std::string &amp; strLogger);</pre>
		<pre>private:</pre>
		<pre>    typedef std::map&lt;:string,&gt; LoggerMap;<br />    LoggerMap loggerPtrs;<br />};</pre>
		<pre>class Logger<br />{<br />public:<br />     Logger() {std::cout &lt;&lt; "ctor of Logger " &lt;&lt; std::endl; }<br />    ~Logger() {std::cout &lt;&lt; "dtor of Logger " &lt;&lt; std::endl; }</pre>
		<pre>    static Logger * getInstance( const std::string &amp; strLogger)<br />    {<br />        static LoggerContainer defaultLoggerContainer;<br />        return defaultLoggerContainer.getinstance(strLogger);<br />    }<br />};</pre>
		<pre>LoggerContainer::~LoggerContainer()<br />{<br />    /* release all ptr in LoggerMap */<br />    LoggerMap::iterator itr = loggerPtrs.begin();</pre>
		<pre>    for( ; itr != loggerPtrs.end(); ++itr )<br /> {<br />     delete (*itr).second;<br /> }</pre>
		<pre>}</pre>
		<pre>Logger * LoggerContainer::getinstance(const std::string &amp; strLogger)<br />{<br />   LoggerMap::iterator itr = loggerPtrs.find(strLogger);</pre>
		<pre>   if(itr != loggerPtrs.end())<br />   {<br />       /* logger exist, just return it */<br />       return (*itr).second;<br />   }<br />   else<br />   {<br />       /* return a new logger */<br />       Logger * plogger = new Logger();<br />       loggerPtrs.insert(std::make_pair(strLogger, plogger));</pre>
		<pre>       return plogger;<br />   }<br />}</pre>
		<pre>int main()<br />{<br />    Logger * plogger1 = Logger::getInstance("test");<br />    Logger * plogger2 = Logger::getInstance("test");</pre>
		<pre>    std::cout &lt;&lt; "plogger1: " &lt;&lt; plogger1 &lt;&lt; std::endl &lt;&lt; "plogger2: " &lt;&lt; plogger2 &lt;&lt; std::endl;</pre>
		<pre>    return 0;<br />}</pre>
		<pre>
				<br />运行结果：</pre>
		<pre>ctor of Logger<br />plogger1: 0x804fc30<br />plogger2: 0x804fc30<br />dtor of Logger</pre>
		<pre>这里的LoggerContainer相当于log4cplus中的Hierarchy类，结果可以看出，通过同一个名称可以获取相同的<br />Logger实例。</pre>
		<pre>
				<br />还有一些小毛病比如RollingFileAppender和DailyRollingFileAppender的参数输入顺序可以调整成统一方式<br />等等，就不细说了。</pre>
		<pre>本部分提到了使用log4cplus时候感觉不爽的地方，最后一部分将介绍一下log4cplus中线程和套接字实现情况<br /></pre>
<img src ="http://www.cppblog.com/tx7do/aggbug/11720.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tx7do/" target="_blank">杨粼波</a> 2006-08-26 04:50 <a href="http://www.cppblog.com/tx7do/articles/11720.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开源日志系统log4cplus(二)</title><link>http://www.cppblog.com/tx7do/articles/11716.html</link><dc:creator>杨粼波</dc:creator><author>杨粼波</author><pubDate>Fri, 25 Aug 2006 20:49:00 GMT</pubDate><guid>http://www.cppblog.com/tx7do/articles/11716.html</guid><wfw:comment>http://www.cppblog.com/tx7do/comments/11716.html</wfw:comment><comments>http://www.cppblog.com/tx7do/articles/11716.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tx7do/comments/commentRss/11716.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tx7do/services/trackbacks/11716.html</trackback:ping><description><![CDATA[
		<pre>本文介绍了使用log4cplus有六个步骤，并提供了一些例子引导你了解log4cplus的基本使用。</pre>
		<br />
		<pre>
				<br />### 基本使用 ###</pre>
		<pre>使用log4cplus有六个基本步骤：</pre>
		<pre>1. 实例化一个appender对象<br />2. 实例化一个layout对象<br />3. 将layout对象绑定(attach)到appender对象<br />4. 实例化一个logger对象,调用静态函数：log4cplus::Logger::getInstance("logger_name")<br />5. 将appender对象绑定(attach)到logger对象，如省略此步骤，标准输出（屏幕）appender对象会绑定到logger<br />6. 设置logger的优先级，如省略此步骤，各种有限级的消息都将被记录</pre>
		<pre>下面通过一些例子来了解log4cplus的基本使用。</pre>
		<pre>〖例1〗<br />/*<br />    严格实现步骤1-6，appender输出到屏幕, 其中的布局格式和LogLevel后面会详细解释。<br />*/<br />#include &lt;log4cplus/logger.h&gt;<br />#include &lt;log4cplus/consoleappender.h&gt;<br />#include &lt;log4cplus/layout.h&gt;<log4cplus /></pre>
		<pre>using namespace log4cplus;<br />using namespace log4cplus::helpers;</pre>
		<pre>int main()<br />{<br />    /* step 1: Instantiate an appender object */<br />    SharedObjectPtr<appender /> _append (new ConsoleAppender());<br />    _append-&gt;setName("append for test");</pre>
		<pre>    /* step 2: Instantiate a layout object */<br />    std::string pattern = "%d{%m/%d/%y %H:%M:%S}  - %m [%l]%n";<br />    std::auto_ptr<layout /> _layout(new PatternLayout(pattern));</pre>
		<pre>    /* step 3: Attach the layout object to the appender */<br />    _append-&gt;setLayout( _layout );</pre>
		<pre>    /* step 4: Instantiate a logger object */<br />    Logger _logger = Logger::getInstance("test");</pre>
		<pre>    /* step 5: Attach the appender object to the logger  */<br />    _logger.addAppender(_append);</pre>
		<pre>    /* step 6: Set a priority for the logger  */<br />    _logger.setLogLevel(ALL_LOG_LEVEL);</pre>
		<pre>     /* log activity */<br />    LOG4CPLUS_DEBUG(_logger, "This is the FIRST log message...")<br />    sleep(1);<br />    LOG4CPLUS_WARN(_logger, "This is the SECOND log message...")</pre>
		<pre>    return 0;<br />}</pre>
		<pre>输出结果：<br />10/14/04 09:06:24  - This is the FIRST log message... [main.cpp:31]<br />10/14/04 09:06:25  - This is the SECOND log message... [main.cpp:33]</pre>
		<pre>
				<br />〖例2〗<br />/*<br />    简洁使用模式，appender输出到屏幕。<br />*/<br />#include &lt;log4cplus/logger.h&gt;<br />#include &lt;log4cplus/consoleappender.h&gt;<log4cplus /></pre>
		<pre>using namespace log4cplus;<br />using namespace log4cplus::helpers;</pre>
		<pre>int main()<br />{<br />    /* step 1: Instantiate an appender object */<br />    SharedAppenderPtr _append(new ConsoleAppender());<br />    _append-&gt;setName("append test");</pre>
		<pre>    /* step 4: Instantiate a logger object */<br />    Logger _logger = Logger::getInstance("test");</pre>
		<pre>    /* step 5: Attach the appender object to the logger  */<br />    _logger.addAppender(_append);</pre>
		<pre>    /* log activity */<br />    LOG4CPLUS_DEBUG(_logger, "This is the FIRST log message...")<br />    sleep(1);<br />    LOG4CPLUS_WARN(_logger, "This is the SECOND log message...")</pre>
		<pre>    return 0;<br />}</pre>
		<pre>输出结果：<br />DEBUG - This is the FIRST log message...<br />WARN - This is the SECOND log message...</pre>
		<pre>
				<br />〖例3〗<br />/*<br />    iostream模式，appender输出到屏幕。<br />*/<br />#include &lt;log4cplus/logger.h&gt;<br />#include &lt;log4cplus/consoleappender.h&gt;<br />#include &lt;iomanip&gt; /* 其实这个东东还是放到log4cplus头文件中比较合适些，个人意见：） */using namespace log4cplus;</pre>
		<pre>int main()<br />{<br />    /* step 1: Instantiate an appender object */<br />    SharedAppenderPtr _append(new ConsoleAppender());<br />    _append-&gt;setName("append test");</pre>
		<pre>    /* step 4: Instantiate a logger object */<br />    Logger _logger = Logger::getInstance("test");</pre>
		<pre>    /* step 5: Attach the appender object to the logger  */<br />    _logger.addAppender(_append);</pre>
		<pre>    /* log activity */<br />    LOG4CPLUS_TRACE(_logger, "This is"  &lt;&lt; " just a t" &lt;&lt; "est." &lt;&lt; std::endl)<br />    LOG4CPLUS_DEBUG(_logger, "This is a bool: " &lt;&lt; true)<br />    LOG4CPLUS_INFO(_logger, "This is a char: " &lt;&lt; 'x')<br />    LOG4CPLUS_WARN(_logger, "This is a int: " &lt;&lt; 1000)<br />    LOG4CPLUS_ERROR(_logger, "This is a long(hex): " &lt;&lt; std::hex &lt;&lt; 100000000)<br />    LOG4CPLUS_FATAL(_logger, "This is a double: "  &lt;&lt; std::setprecision(15)  &lt;&lt; 1.2345234234)</pre>
		<pre>    return 0;<br />}</pre>
		<pre>输出结果：<br />DEBUG - This is a bool: 1<br />INFO - This is a char: x<br />WARN - This is a int: 1000<br />ERROR - This is a long(hex): 5f5e100<br />FATAL - This is a double: 1.2345234234</pre>
		<pre>
				<br />〖例4〗<br />/*<br />    调试模式，通过loglog来控制输出调试、警告或错误信息，appender输出到屏幕。<br />*/<br />#include &lt;iostream&gt;<br />#include &lt;log4cplus/helpers/loglog.h&gt;</pre>
		<pre>using namespace log4cplus::helpers;</pre>
		<pre>void printMsgs(void)<br />{<br />    std::cout &lt;&lt; "Entering printMsgs()..." &lt;&lt; std::endl;<br />    LogLog::getLogLog()-&gt;debug("This is a Debug statement...");<br />    LogLog::getLogLog()-&gt;warn("This is a Warning...");<br />    LogLog::getLogLog()-&gt;error("This is a Error...");<br />    std::cout &lt;&lt; "Exiting printMsgs()..." &lt;&lt; std::endl &lt;&lt; std::endl;<br />}</pre>
		<pre>int main()<br />{<br />    /*<br />       LogLog类实现了debug, warn, error 函数用于输出调试、警告或错误信息，<br />       同时提供了两个方法来进一步控制所输出的信息，其中：</pre>
		<pre>       setInternalDebugging方法用来控制是否屏蔽输出信息中的调试信息，当输入<br />       参数为false则屏蔽，缺省设置为false。</pre>
		<pre>       setQuietMode方法用来控制是否屏蔽所有输出信息，当输入参数为true则屏蔽，<br />       缺省设置为false。</pre>
		<pre>       LogLog::getLogLog()-&gt;setInternalDebugging(false);<br />    */</pre>
		<pre>    printMsgs();</pre>
		<pre>    std::cout &lt;&lt; "Turning on debug..." &lt;&lt; std::endl;<br />    LogLog::getLogLog()-&gt;setInternalDebugging(true);<br />    printMsgs();</pre>
		<pre>    std::cout &lt;&lt; "Turning on quiet mode..." &lt;&lt; std::endl;<br />    LogLog::getLogLog()-&gt;setQuietMode(true);<br />    printMsgs();</pre>
		<pre>    return 0;<br />}</pre>
		<pre>输出结果：<br />Entering printMsgs()...<br />log4cplus:WARN This is a Warning...<br />log4cplus:ERROR This is a Error...<br />Exiting printMsgs()...</pre>
		<pre>Turning on debug...<br />Entering printMsgs()...<br />log4cplus: This is a Debug statement...<br />log4cplus:WARN This is a Warning...<br />log4cplus:ERROR This is a Error...<br />Exiting printMsgs()...</pre>
		<pre>Turning on quiet mode...<br />Entering printMsgs()...<br />Exiting printMsgs()...</pre>
		<pre>需要指出的是，输出信息中总是包含"log4cplus:"前缀，有时候会感觉不爽，这是因为LogLog在实现时候死定了要这么写：</pre>
		<pre>LogLog::LogLog()<br /> : mutex(LOG4CPLUS_MUTEX_CREATE),<br />   debugEnabled(false),<br />   quietMode(false),<br />   PREFIX( LOG4CPLUS_TEXT("log4cplus: ") ),<br />   WARN_PREFIX( LOG4CPLUS_TEXT("log4cplus:WARN ") ),<br />   ERR_PREFIX( LOG4CPLUS_TEXT("log4cplus:ERROR ") )<br />{<br />}</pre>
		<pre>你可以把这些前缀换成自己看着爽的提示符号，然后重新编译，hihi。除非万不得已或者实在郁闷的不行，否则还是不要这样干。</pre>
		<pre>
				<br />〖例5〗<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008000">/*</span><span style="COLOR: #008000">    文件模式，appender输出到文件。</span><span style="COLOR: #008000">*/<br /></span><span style="COLOR: #000000">#include </span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">log4cplus</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">logger.h</span><span style="COLOR: #000000">&gt;<br /></span><span style="COLOR: #000000">#include </span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000">log4cplus</span><span style="COLOR: #000000">/</span><span style="COLOR: #000000">fileappender.h</span><span style="COLOR: #000000">&gt;</span><span style="COLOR: #000000"><br /></span><span style="COLOR: #0000ff">using</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">namespace</span><span style="COLOR: #000000"> log4cplus;<br /></span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000"> main()<br />{    <br /></span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000"> step 1: Instantiate an appender object </span><span style="COLOR: #008000">*/<br /></span><span style="COLOR: #000000">    SharedAppenderPtr _append(</span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> FileAppender(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Test.log</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">));<br />    _append</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">setName(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">file log test</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br />    </span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000"> step 4: Instantiate a logger object </span><span style="COLOR: #008000">*/<br /></span><span style="COLOR: #000000">    Logger _logger </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> Logger::getInstance(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">test.subtestof_filelog</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br />    </span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000"> step 5: Attach the appender object to the logger  </span><span style="COLOR: #008000">*/<br /></span><span style="COLOR: #000000">    _logger.addAppender(_append);<br />    </span><span style="COLOR: #008000">/*</span><span style="COLOR: #008000"> log activity </span><span style="COLOR: #008000">*/<br /></span><span style="COLOR: #000000">    </span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000"> i;<br />    </span><span style="COLOR: #0000ff">for</span><span style="COLOR: #000000">( i </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">; i </span><span style="COLOR: #000000">&lt;</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">5</span><span style="COLOR: #000000">; </span><span style="COLOR: #000000">++</span><span style="COLOR: #000000">i )<br />    {<br />        LOG4CPLUS_DEBUG(_logger, </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">Entering loop #</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000"> i </span><span style="COLOR: #000000">&lt;&lt;</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">End line #</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">)<br />    }<br />    </span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">;<br />}<br /></span></div></pre>
		<pre>输出结果（Test.log文件）：</pre>
		<pre>DEBUG - Entering loop #0End line #<br />DEBUG - Entering loop #1End line #<br />DEBUG - Entering loop #2End line #<br />DEBUG - Entering loop #3End line #<br />DEBUG - Entering loop #4End line #</pre>
<img src ="http://www.cppblog.com/tx7do/aggbug/11716.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tx7do/" target="_blank">杨粼波</a> 2006-08-26 04:49 <a href="http://www.cppblog.com/tx7do/articles/11716.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开源日志系统log4cplus(三)</title><link>http://www.cppblog.com/tx7do/articles/11717.html</link><dc:creator>杨粼波</dc:creator><author>杨粼波</author><pubDate>Fri, 25 Aug 2006 20:49:00 GMT</pubDate><guid>http://www.cppblog.com/tx7do/articles/11717.html</guid><wfw:comment>http://www.cppblog.com/tx7do/comments/11717.html</wfw:comment><comments>http://www.cppblog.com/tx7do/articles/11717.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tx7do/comments/commentRss/11717.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tx7do/services/trackbacks/11717.html</trackback:ping><description><![CDATA[
		<p>本文介绍了三种控制输出格式的布局管理器的概念和使用情况，通过掌握这些知识，可以更有效地控制log系统输出尽可能贴近你需求的信息来。<br /></p>
		<pre>
				<br />### 如何控制输出消息的格式 ###</pre>
		<pre>前面已经讲过，log4cplus通过布局器（Layouts）来控制输出的格式，log4cplus提供了三种类型的Layouts，<br />分别是SimpleLayout、PatternLayout、和TTCCLayout。其中：</pre>
		<pre>1. SimpleLayout<br />是一种简单格式的布局器，在输出的原始信息之前加上LogLevel和一个"-"。</pre>
		<pre>比如以下代码片段：</pre>
		<pre>    ... ...</pre>
		<pre>    /* step 1: Instantiate an appender object */<br />    SharedObjectPtr _append (new ConsoleAppender());<br />    _append-&gt;setName("append for test");</pre>
		<pre>    /* step 2: Instantiate a layout object */<br />    std::auto_ptr  _layout(new log4cplus::SimpleLayout());</pre>
		<pre>    /* step 3: Attach the layout object to the appender */<br />    _append-&gt;setLayout( _layout );</pre>
		<pre>    /* step 4: Instantiate a logger object */<br />    Logger _logger = Logger::getInstance("test");</pre>
		<pre>    /* step 5: Attach the appender object to the logger  */<br />    _logger.addAppender(_append);</pre>
		<pre>     /* log activity */<br />    LOG4CPLUS_DEBUG(_logger, "This is the simple formatted log message...")<br />    <br />    ... ...<br />    <br />    <br />将打印结果：<br />DEBUG - This is the simple formatted log message...</pre>
		<pre>2. PatternLayout<br />是一种有词法分析功能的模式布局器，一提起模式就会想起正则表达式，这里的模式和正则表达式类似，但是<br />远比后者简单，能够对预定义的标识符（称为conversion specifiers）进行解析，转换成特定格式输出。以下<br />代码片段演示了如何使用PatternLayout：</pre>
		<pre>    ... ...</pre>
		<pre>    /* step 1: Instantiate an appender object */<br />    SharedObjectPtr _append (new ConsoleAppender());<br />    _append-&gt;setName("append for test");<br />   <br />    /* step 2: Instantiate a layout object */<br />    std::string pattern = "%d{%m/%d/%y %H:%M:%S}  - %m [%l]%n";<br />    std::auto_ptr _layout(new PatternLayout(pattern));<br />    <br />    /* step 3: Attach the layout object to the appender */<br />    _append-&gt;setLayout( _layout );</pre>
		<pre>    /* step 4: Instantiate a logger object */<br />    Logger _logger = Logger::getInstance("test_logger.subtest");</pre>
		<pre>    /* step 5: Attach the appender object to the logger  */<br />    _logger.addAppender(_append);</pre>
		<pre>     /* log activity */<br />    LOG4CPLUS_DEBUG(_logger, "teststr")<br />    <br />    ... ...<br />    <br />输出结果：<br />10/16/04 18:51:25  - teststr [main.cpp:51]</pre>
		<pre>可以看出通过填写特定格式的模式字符串"pattern"，原始信息被包含到一堆有格式的信息当中了，这就使得<br />用户可以根据自身需要来定制显示内容。"pattern"可以包含普通字符串和预定义的标识符，其中：</pre>
		<pre>（1）普通字符串，能够被直接显示的信息。<br />（2）预定义标识符，通过"%"与一个或多个字符共同构成预定义的标识符，能够产生出特定格式信息。</pre>
		<pre>关于预定义标识符，log4cplus文档中提供了详细的格式说明，我每种都试了一下，以上述代码为例，根据不同<br />的pattern，各种消息格式使用情况列举如下：</pre>
		<pre>（1）"%%"，转义为%, 即，std::string pattern = "%%" 时输出: "%"<br />（2）"%c"，输出logger名称，比如std::string pattern ="%c" 时输出: "test_logger.subtest"，<br />     也可以控制logger名称的显示层次，比如"%c{1}"时输出"test_logger"，其中数字表示层次。<br />（3）"%D"，显示本地时间，当std::string pattern ="%D" 时输出:"2004-10-16 18:55:45"，%d显示标准时间，<br />     所以当std::string pattern ="%d" 时输出 "2004-10-16 10:55:45" （因为我们是东8区，差8个小时啊）。<br />     可以通过%d{...}定义更详细的显示格式，比如%d{%H:%M:%s}表示要显示小时:分钟：秒。大括号中可显示的<br />     预定义标识符如下：<br />     <br />%a -- 表示礼拜几，英文缩写形式，比如"Fri"<br />%A -- 表示礼拜几，比如"Friday"<br />%b -- 表示几月份，英文缩写形式，比如"Oct"<br />%B -- 表示几月份，"October"<br />%c -- 标准的日期＋时间格式，如 "Sat Oct 16 18:56:19 2004"<br />%d -- 表示今天是这个月的几号(1-31)"16"<br />%H -- 表示当前时刻是几时(0-23)，如 "18"<br />%I -- 表示当前时刻是几时(1-12)，如 "6"<br />%j -- 表示今天是哪一天(1-366)，如 "290"<br />%m -- 表示本月是哪一月(1-12)，如 "10"<br />%M -- 表示当前时刻是哪一分钟(0-59)，如 "59"<br />%p -- 表示现在是上午还是下午， AM or PM<br />%q -- 表示当前时刻中毫秒部分(0-999)，如 "237"<br />%Q -- 表示当前时刻中带小数的毫秒部分(0-999.999)，如 "430.732"<br />%S -- 表示当前时刻的多少秒(0-59)，如 "32"<br />%U -- 表示本周是今年的第几个礼拜，以周日为第一天开始计算(0-53)，如 "41"<br />%w -- 表示礼拜几，(0-6, 礼拜天为0)，如 "6"<br />%W -- 表示本周是今年的第几个礼拜，以周一为第一天开始计算(0-53)，如 "41"<br />%x -- 标准的日期格式，如 "10/16/04"<br />%X -- 标准的时间格式，如 "19:02:34"<br />%y -- 两位数的年份(0-99)，如 "04"<br />%Y -- 四位数的年份，如 "2004"<br />%Z -- 时区名，比如 "GMT"</pre>
		<pre>（4）"%F"，输出当前记录器所在的文件名称，比如std::string pattern ="%F" 时输出: "main.cpp"<br />（5）"%L"，输出当前记录器所在的文件行号，比如std::string pattern ="%L" 时输出: "51"<br />（6）"%l"，输出当前记录器所在的文件名称和行号，比如std::string pattern ="%L" 时输出:<br />     "main.cpp:51"<br />（7）"%m"，输出原始信息，比如std::string pattern ="%m" 时输出: "teststr"，即上述代码中<br />     LOG4CPLUS_DEBUG的第二个参数，这种实现机制可以确保原始信息被嵌入到带格式的信息中。<br />（8）"%n"，换行符，没什么好解释的<br />（9）"%p"，输出LogLevel，比如std::string pattern ="%p" 时输出: "DEBUG"<br />（10）"%t"，输出记录器所在的线程ID，比如std::string pattern ="%t" 时输出: "1075298944"<br />（11）"%x"，嵌套诊断上下文NDC (nested diagnostic context) 输出，从堆栈中弹出上下文信息，NDC可以用对<br />      不同源的log信息（同时地）交叉输出进行区分，关于NDC方面的详细介绍会在下文中提到。<br />（12）格式对齐，比如std::string pattern ="%-10m"时表示左对齐，宽度是10，此时会输出"teststr   "，当<br />      然其它的控制字符也可以相同的方式来使用，比如"%-12d"，"%-5p"等等（刚接触log4cplus文档时还以为<br />      "%-5p"整个字符串代表LogLevel呢，呵呵）。</pre>
		<pre>      <br />3. TTCCLayout<br />是在PatternLayout基础上发展的一种缺省的带格式输出的布局器， 其格式由时间，线程ID，Logger和NDC 组<br />成（consists of time, thread, Logger and nested diagnostic context information, hence the name），<br />因而得名（怎么得名的？Logger里哪里有那个"C"的缩写啊！名字起得真够烂的，想扁人）。提供给那些想显示<br />典型的信息（一般情况下够用了）又懒得配置pattern的同志们。</pre>
		<pre>TTCCLayout在构造时有机会选择显示本地时间或GMT时间，缺省是按照本地时间显示：<br />TTCCLayout::TTCCLayout(bool use_gmtime  = false)</pre>
		<pre>以下代码片段演示了如何使用TTCCLayout：</pre>
		<pre>    ... ...</pre>
		<pre>    /* step 1: Instantiate an appender object */<br />    SharedObjectPtr _append (new ConsoleAppender());<br />    _append-&gt;setName("append for test");</pre>
		<pre>    /* step 2: Instantiate a layout object */<br />    std::auto_ptr _layout(new TTCCLayout());</pre>
		<pre>    /* step 3: Attach the layout object to the appender */<br />    _append-&gt;setLayout( _layout );</pre>
		<pre>    /* step 4: Instantiate a logger object */<br />    Logger _logger = Logger::getInstance("test_logger");</pre>
		<pre>    /* step 5: Attach the appender object to the logger  */<br />    _logger.addAppender(_append);</pre>
		<pre>     /* log activity */<br />    LOG4CPLUS_DEBUG(_logger, "teststr")<br />    <br />    ... ...<br />    <br />输出结果：<br />10-16-04 19:08:27,501 [1075298944] DEBUG test_logger &lt;&gt; - teststr</pre>
		<pre>
				<br />当构造TTCCLayout对象时选择GMT时间格式时：</pre>
		<pre>    ... ...<br />    <br />    /* step 2: Instantiate a layout object */<br />    std::auto_ptr _layout(new TTCCLayout(true));<br />    <br />    ... ...<br />    <br />输出结果：<br />10-16-04 11:12:47,678 [1075298944] DEBUG test_logger &lt;&gt; - teststr</pre>
		<pre>
				<br />本文介绍了控制log信息格式的相关知识，下一部分将详细介绍log信息的几种文件操作方式。</pre>
<img src ="http://www.cppblog.com/tx7do/aggbug/11717.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tx7do/" target="_blank">杨粼波</a> 2006-08-26 04:49 <a href="http://www.cppblog.com/tx7do/articles/11717.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开源日志系统log4cplus(一)</title><link>http://www.cppblog.com/tx7do/articles/11715.html</link><dc:creator>杨粼波</dc:creator><author>杨粼波</author><pubDate>Fri, 25 Aug 2006 20:45:00 GMT</pubDate><guid>http://www.cppblog.com/tx7do/articles/11715.html</guid><wfw:comment>http://www.cppblog.com/tx7do/comments/11715.html</wfw:comment><comments>http://www.cppblog.com/tx7do/articles/11715.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tx7do/comments/commentRss/11715.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tx7do/services/trackbacks/11715.html</trackback:ping><description><![CDATA[
		<p>log4cplus是C++编写的开源的日志系统，功能非常全面，用到自己开发的工程中会比较专业的，：），本文介绍了log4cplus基本概念，以及如何安装，配置。 <br /></p>
		<pre>
				<br />### 简介 ###</pre>
		<pre>log4cplus是C++编写的开源的日志系统，前身是java编写的log4j系统.受Apache Software License<br />保护。作者是Tad E. Smith。log4cplus具有线程安全、灵活、以及多粒度控制的特点，通过将信息划分<br />优先级使其可以面向程序调试、运行、测试、和维护等全生命周期； 你可以选择将信息输出到屏幕、文件、<br />NT event log、甚至是远程服务器；通过指定策略对日志进行定期备份等等。</pre>
		<pre> </pre>
		<pre>### 下载 ###</pre>
		<pre>最新的log4cplus可以从以下网址下载 <a href="http://log4cplus.sourceforge.net/"><font color="#002c99">http://log4cplus.sourceforge.net</font></a><br />本文使用的版本为：1.0.2</pre>
		<pre> </pre>
		<pre>### 安装 ###</pre>
		<pre> </pre>
		<pre>1. linux下安装</pre>
		<pre>tar xvzf log4cplus-x.x.x.tar.gz<br />cd log4cplus-x.x.x<br />./configure --prefix=/where/to/install<br />make<br />make install</pre>
		<pre>这里我采用缺省安装路径：/usr/local，下文如无特别说明，均以此路径为准。</pre>
		<pre> </pre>
		<pre>2. windows下安装</pre>
		<pre>不需要安装，有一个msvc6存放包括源代码和用例在内的开发工程（for VC6 only），使用之前请先编译<br />"log4cplus_dll class"工程生成dll，或者编译"log4cplus_static class"工程生成lib.</pre>
		<pre> </pre>
		<pre>### 使用前的配置 ###</pre>
		<pre>1. linux下的配置</pre>
		<pre>确保你的Makefile中包含 /usr/local/lib/liblog4cplus.a（静态库）或  -llog4cplus（动态库）即可，<br />头文件在/usr/local/include/log4cplus目录下。对于动态库，要想正常使用，还得将库安装路径加入到<br />LD_LIBRARY_PATH 中，我一般是这样做的：以管理员身份登录，在/etc/ld.so.conf中加入安装路径，这里<br />是/usr/local/lib，然后执行ldconfig使设置生效即可。</pre>
		<pre>2. windows下的配置</pre>
		<pre>将"log4cplus_dll class"工程或"log4cplus_static class"工程的dsp 文件插入到你的工程中，或者直接<br />把两个工程编译生成的库以及头文件所在目录放到你的工程的搜索路径中，如果你使用静态库，请在你的工程中<br />"project/setting/C++"的preprocessor definitions中加入LOG4CPLUS_STATIC。</pre>
		<pre> </pre>
		<pre>### 构成要素介绍 ###</pre>
		<pre>虽然功能强大，应该说log4cplus用起来还是比较复杂的，为了更好地使用它，先介绍一下它的基本要素。</pre>
		<pre>Layouts      ：布局器，控制输出消息的格式.<br />Appenders    ：挂接器，与布局器紧密配合，将特定格式的消息输出到所挂接的设备终端<br />               （如屏幕，文件等等)。<br />Logger       ：记录器，保存并跟踪对象日志信息变更的实体，当你需要对一个对象进行<br />               记录时，就需要生成一个logger。<br />Categories   ：分类器，层次化（hierarchy）的结构，用于对被记录信息的分类，层次中<br />               每一个节点维护一个logger的所有信息。<br />Priorities   ：优先权，包括TRACE, DEBUG, INFO, WARNING, ERROR, FATAL。</pre>
		<pre>
				<br />本文介绍了log4cplus基本概念，以及如何安装，配置，下一篇将通过例子介绍如何使用log4cplus。</pre>
<img src ="http://www.cppblog.com/tx7do/aggbug/11715.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tx7do/" target="_blank">杨粼波</a> 2006-08-26 04:45 <a href="http://www.cppblog.com/tx7do/articles/11715.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>  Linux Socket编程实例(一个Hello World程序) </title><link>http://www.cppblog.com/tx7do/articles/5966.html</link><dc:creator>杨粼波</dc:creator><author>杨粼波</author><pubDate>Thu, 20 Apr 2006 09:49:00 GMT</pubDate><guid>http://www.cppblog.com/tx7do/articles/5966.html</guid><wfw:comment>http://www.cppblog.com/tx7do/comments/5966.html</wfw:comment><comments>http://www.cppblog.com/tx7do/articles/5966.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tx7do/comments/commentRss/5966.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tx7do/services/trackbacks/5966.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 在Linux下写了个小的socket程序，分为客户端和服务器端,服务端开一个端口(2000),做为一个daemon,等待客户的连接请求.一旦有客户连接,服务器端打印出客户端的IP地址和端口,并且向服务器端发送欢迎信息和时间.下面是服务端的代码(tcpserver.c).由于这只是个简单的程序，所以只用了单线程实现!														/**/										/*...&nbsp;&nbsp;<a href='http://www.cppblog.com/tx7do/articles/5966.html'>阅读全文</a><img src ="http://www.cppblog.com/tx7do/aggbug/5966.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tx7do/" target="_blank">杨粼波</a> 2006-04-20 17:49 <a href="http://www.cppblog.com/tx7do/articles/5966.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何在linux下检测内存泄漏</title><link>http://www.cppblog.com/tx7do/articles/5964.html</link><dc:creator>杨粼波</dc:creator><author>杨粼波</author><pubDate>Thu, 20 Apr 2006 09:45:00 GMT</pubDate><guid>http://www.cppblog.com/tx7do/articles/5964.html</guid><wfw:comment>http://www.cppblog.com/tx7do/comments/5964.html</wfw:comment><comments>http://www.cppblog.com/tx7do/articles/5964.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tx7do/comments/commentRss/5964.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tx7do/services/trackbacks/5964.html</trackback:ping><description><![CDATA[
		<font size="2">   要想检测内存泄漏，必须对程序中的内存分配和释放情况进行记录，所能够采取的办法有重载所有形式的operator new 和 operator delete，截获 new operator 和 delete operator 执行过程中的内存操作信息。下面列出的就是重载形式 </font>
		<a name="N10081">
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code>
														<br />
														<font size="2">void* operator new( size_t nSize, char* pszFileName, int nLineNum )<br />void* operator new[]( size_t nSize, char* pszFileName, int nLineNum )<br />void operator delete( void *ptr )<br />void operator delete[]( void *ptr )<br /></font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<p>
				<font size="2">我们为 operator new 定义了一个新的版本，除了必须的 size_t nSize 参数外，还增加了文件名和行号，这里的文件名和行号就是这次 new operator 操作符被调用时所在的文件名和行号，这个信息将在发现内存泄漏时输出，以帮助用户定位泄漏具体位置。对于 operator delete，因为无法为之定义新的版本，我们直接覆盖了全局的 operator delete 的两个版本。</font>
		</p>
		<p>
				<font size="2">在重载的 operator new 函数版本中，我们将调用全局的 operator new 的相应的版本并将相应的 size_t 参数传入，而后，我们将全局 operator new 返回的指针值以及该次分配所在的文件名和行号信息记录下来，<span style="COLOR: rgb(255,0,0)">这里所采用的数据结构是一个 STL 的 map</span>，以指针值为 key 值。当 operator delete 被调用时，如果调用方式正确的话（调用方式不正确的情况将在后面详细描述），我们就能以传入的指针值在 map 中找到相应的数据项并将之删除，而后调用 free 将指针所指向的内存块释放。当程序退出的时候，map 中的剩余的数据项就是我们企图检测的内存泄漏信息－－已经在堆上分配但是尚未释放的分配信息。</font>
		</p>
		<p>
				<font size="2">以上就是内存检测实现的基本原理，现在还有两个基本问题没有解决：</font>
		</p>
		<p>
				<font size="2">1) 如何取得内存分配代码所在的文件名和行号，并让 new operator 将之传递给我们重载的 operator new。</font>
		</p>
		<p>
				<font size="2">2) 我们何时创建用于存储内存数据的 map 数据结构，如何管理，何时打印内存泄漏信息。</font>
		</p>
		<p>
				<font size="2">先解决问题1。首先我们可以利用 C 的预编译宏 <span style="FONT-WEIGHT: bold">__FILE__ 和 __LINE__</span>，这两个宏将在编译时在指定位置展开为该文件的文件名和该行的行号。而后我们需要将缺省的全局 new operator 替换为我们自定义的能够传入文件名和行号的版本，我们在子系统头文件 MemRecord.h 中定义：</font>
		</p>
		<font size="2">
				<a name="N1009C">
				</a>
				<br />
		</font>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<font size="2">
														<code>
																<br />#define DEBUG_NEW new(__FILE__, __LINE__ )<br /></code>
												</font>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<p>
				<font size="2">而后在所有需要使用内存检测的客户程序的所有的 cpp 文件的开头加入</font>
		</p>
		<font size="2">
				<a name="N100A8">
				</a>
				<br />
		</font>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<font size="2">
														<code>
																<br />#include "MemRecord.h"<br />#define new DEBUG_NEW<br /></code>
												</font>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<p>
				<font size="2">就可以将客户源文件中的对于全局缺省的 new operator 的调用替换为 new (__FILE__,__LINE__) 调用，而该形式的new operator将调用我们的operator new (size_t nSize, char* pszFileName, int nLineNum)，其中 nSize 是由 new operator 计算并传入的，而 new 调用点的文件名和行号是由我们自定义版本的 new operator 传入的。我们建议在所有用户自己的源代码文件中都加入上述宏，如果有的文件中使用内存检测子系统而有的没有，则子系统将可能因无法监控整个系统而输出一些泄漏警告。</font>
		</p>
		<p>
				<font size="2">再说第二个问题。我们用于管理客户信息的这个 map 必须在客户程序第一次调用 new operator 或者 delete operator 之前被创建，而且在最后一个 new operator 和 delete operator 调用之后进行泄漏信息的打印，也就是说它需要先于客户程序而出生，而在客户程序退出之后进行分析。能够包容客户程序生命周期的确有一人--全局对象（appMemory）。我们可以设计一个类来封装这个 map 以及这对它的插入删除操作，然后构造这个类的一个全局对象（appMemory），在全局对象（appMemory）的构造函数中创建并初始化这个数据结构，而在其析构函数中对数据结构中剩余数据进行分析和输出。Operator new 中将调用这个全局对象（appMemory）的 insert 接口将指针、文件名、行号、内存块大小等信息以指针值为 key 记录到 map 中，在 operator delete 中调用 erase 接口将对应指针值的 map 中的数据项删除，注意不要忘了对 map 的访问需要进行互斥同步，因为同一时间可能会有多个线程进行堆上的内存操作。</font>
		</p>
		<p>
				<font size="2">好啦，内存检测的基本功能已经具备了。但是不要忘了，我们为了检测内存泄漏，在全局的 operator new 增加了一层间接性，同时为了保证对数据结构的安全访问增加了互斥，这些都会降低程序运行的效率。因此我们需要让用户能够方便的 enable 和 disable 这个内存检测功能，毕竟内存泄漏的检测应该在程序的调试和测试阶段完成。我们可以使用条件编译的特性，在用户被检测文件中使用如下宏定义：</font>
		</p>
		<font size="2">
				<a name="N100BA">
				</a>
				<br />
		</font>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<font size="2">
														<code>
																<br />#include "MemRecord.h"<br />#if defined( MEM_DEBUG )<br />#define new DEBUG_NEW<br />#endif<br /></code>
												</font>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<p>
				<font size="2">当用户需要使用内存检测时，可以使用如下命令对被检测文件进行编译</font>
		</p>
		<font size="2">
				<a name="N100C6">
				</a>
				<br />
		</font>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<font size="2">
														<code>
																<br />
																<span style="COLOR: rgb(255,0,0)">g++ -c -DMEM_DEBUG xxxxxx.cpp</span>
																<br />
														</code>
												</font>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<p>
				<font size="2">就可以 enable 内存检测功能，而用户程序正式发布时，可以去掉 -DMEM_DEBUG 编译开关来 disable 内存检测功能，消除内存检测带来的效率影响。</font>
		</p>
		<p>
				<font size="2">图2所示为使用内存检测功能后，内存泄漏代码的执行以及检测结果</font>
		</p>
		<p>
				<font size="2">
						<img height="565" alt="" src="http://www-128.ibm.com/developerworks/cn/linux/l-mleak/images/image002.jpg" width="710" border="0" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />
						<br xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />图2 </font>
		</p>
		<p>
				<font size="2">
						<a name="N100E1">
								<span class="atitle2">4．错误方式删除带来的问题</span>
						</a>
						<br />以上我们已经构建了一个具备基本内存泄漏检测功能的子系统，下面让我们来看一下关于内存泄漏方面的一些稍微高级一点的话题。</font>
		</p>
		<p>
				<font size="2">首先，在我们编制 c++ 应用时，有时需要在堆上创建单个对象，有时则需要创建对象的数组。关于 new 和 delete 原理的叙述我们可以知道，对于单个对象和对象数组来说，内存分配和删除的动作是大不相同的，我们应该总是正确的使用彼此搭配的 new 和 delete 形式。但是在某些情况下，我们很容易犯错误，比如如下代码：</font>
		</p>
		<font size="2">
				<a name="N100ED">
				</a>
				<br />
		</font>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<font size="2">
														<code>
																<br />		class Test {};<br />		……<br />		Test* pAry = new Test[10];//创建了一个拥有 10 个 Test 对象的数组<br />		Test* pObj = new Test;//创建了一个单对象<br />		……<br />		delete []pObj;//本应使用单对象形式 delete pObj 进行内存释放，却错误的使用了数<br />//组形式<br />		delete pAry;//本应使用数组形式 delete []pAry 进行内存释放，却错误的使用了单对<br />//象的形式<br /></code>
												</font>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<p>
				<font size="2">不匹配的 new 和 delete 会导致什么问题呢？C++ 标准对此的解答是"未定义"，就是说没有人向你保证会发生什么，但是有一点可以肯定：大多不是好事情--在某些编译器形成的代码中，程序可能会崩溃，而另外一些编译器形成的代码中，程序运行可能毫无问题，但是可能导致内存泄漏。</font>
		</p>
		<p>
				<font size="2">既然知道形式不匹配的 new 和 delete 会带来的问题，我们就需要对这种现象进行毫不留情的揭露，毕竟我们重载了所有形式的内存操作 operator new，operator new[]，operator delete，operator delete[]。</font>
		</p>
		<p>
				<font size="2">我们首先想到的是，当用户调用特定方式（单对象或者数组方式）的 operator new 来分配内存时，我们可以在指向该内存的指针相关的数据结构中，增加一项用于描述其分配方式。当用户调用不同形式的 operator delete 的时候，我们在 map 中找到与该指针相对应的数据结构，然后比较分配方式和释放方式是否匹配，匹配则在 map 中正常删除该数据结构，不匹配则将该数据结构转移到一个所谓 "ErrorDelete" 的 list 中，在程序最终退出的时候和内存泄漏信息一起打印。</font>
		</p>
		<p>
				<font size="2">上面这种方法是最顺理成章的，但是在实际应用中效果却不好。原因有两个，第一个原因我们上面已经提到了：当 new 和 delete 形式不匹配时，其结果"未定义"。如果我们运气实在太差--程序在执行不匹配的 delete 时崩溃了，我们的全局对象（appMemory）中存储的数据也将不复存在，不会打印出任何信息。第二个原因与编译器相关，前面提到过，当编译器处理自定义数据类型或者自定义数据类型数组的 new 和 delete 操作符的时候，通常使用编译器相关的 cookie 技术。这种 cookie 技术在编译器中可能的实现方式是：new operator 先计算容纳所有对象所需的内存大小，而后再加上它为记录 cookie 所需要的内存量，再将总容量传给operator new 进行内存分配。当 operator new 返回所需的内存块后，new operator 将在调用相应次数的构造函数初始化有效数据的同时，记录 cookie 信息。而后将指向有效数据的指针返回给用户。也就是说我们重载的 operator new 所申请到并记录下来的指针与 new operator 返回给调用者的指针不一定一致（图3）。当调用者将 new operator 返回的指针传给 delete operator 进行内存释放时，如果其调用形式相匹配，则相应形式的 delete operator 会作出相反的处理，即调用相应次数的析构函数，再通过指向有效数据的指针位置找出包含 cookie 的整块内存地址，并将其传给 operator delete 释放内存。如果调用形式不匹配，delete operator 就不会做上述运算，而直接将指向有效数据的指针（而不是真正指向整块内存的指针）传入 operator delete。因为我们在 operator new 中记录的是我们所分配的整块内存的指针，而现在传入 operator delete 的却不是，所以就无法在全局对象（appMemory）所记录的数据中找到相应的内存分配信息。</font>
		</p>
		<p>
				<font size="2">
						<img height="166" alt="" src="http://www-128.ibm.com/developerworks/cn/linux/l-mleak/images/image005.jpg" width="344" border="0" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />
						<br xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />图3 </font>
		</p>
		<p>
				<font size="2">综上所述，当 new 和 delete 的调用形式不匹配时，由于程序有可能崩溃或者内存子系统找不到相应的内存分配信息，在程序最终打印出 "ErrorDelete" 的方式只能检测到某些"幸运"的不匹配现象。但我们总得做点儿什么，不能让这种危害极大的错误从我们眼前溜走，既然不能秋后算帐，我们就实时输出一个 warning 信息来提醒用户。什么时候抛出一个 warning 呢？很简单，当我们发现在 operator delete 或 operator delete[] 被调用的时候，我们无法在全局对象（appMemory）的 map 中找到与传入的指针值相对应的内存分配信息，我们就认为应该提醒用户。</font>
		</p>
		<p>
				<font size="2">既然决定要输出warning信息，那么现在的问题就是：我们如何描述我们的warning信息才能更便于用户定位到不匹配删除错误呢？答案：在 warning 信息中打印本次 delete 调用的文件名和行号信息。这可有点困难了，因为对于 operator delete 我们不能向对象 operator new 一样做出一个带附加信息的重载版本，我们只能在保持其接口原貌的情况下，重新定义其实现，所以我们的 operator delete 中能够得到的输入只有指针值。在 new/delete 调用形式不匹配的情况下，我们很有可能无法在全局对象（appMemory）的 map 中找到原来的 new 调用的分配信息。怎么办呢？万不得已，只好使用全局变量了。我们在检测子系统的实现文件中定义了两个全局变量（DELETE_FILE, DELETE_LINE）记录 operator delete 被调用时的文件名和行号，同时为了保证并发的 delete 操作对这两个变量访问同步，还使用了一个 mutex（至于为什么是 CCommonMutex 而不是一个 pthread_mutex_t，在"实现上的问题"一节会详细论述，在这里它的作用就是一个 mutex）。</font>
		</p>
		<font size="2">
				<a name="N10114">
				</a>
				<br />
		</font>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<font size="2">
														<code>
																<br />char DELETE_FILE[ FILENAME_LENGTH ] = {0};<br />int DELETE_LINE = 0;<br />CCommonMutex globalLock;<br /></code>
												</font>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<p>
				<font size="2">而后，在我们的检测子系统的头文件中定义了如下形式的 DEBUG_DELETE</font>
		</p>
		<font size="2">
				<a name="N10120">
				</a>
				<br />
		</font>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<font size="2">
														<code>
																<br />			extern char DELETE_FILE[ FILENAME_LENGTH ];<br />extern int DELETE_LINE;<br />extern CCommonMutex globalLock;//在后面解释<br />#define DEBUG_DELETE 	globalLock.Lock(); \<br />			if (DELETE_LINE != 0) BuildStack();\ （//见第六节解释）<br />			strncpy( DELETE_FILE, __FILE__,FILENAME_LENGTH - 1 );\<br />			DELETE_FILE[ FILENAME_LENGTH - 1 ]= '\0'; \<br />			DELETE_LINE = __LINE__; \<br />			delete<br /></code>
												</font>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<p>
				<font size="2">在用户被检测文件中原来的宏定义中添加一条：</font>
		</p>
		<font size="2">
				<a name="N1012C">
				</a>
				<br />
		</font>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<font size="2">
														<code>
																<br />#include "MemRecord.h"<br />#if defined( MEM_DEBUG )<br />#define new DEBUG_NEW<br />#define delete DEBUG_DELETE<br />#endif<br /></code>
												</font>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<p>
				<font size="2">这样，在用户被检测文件调用 delete operator 之前，将先获得互斥锁，然后使用调用点文件名和行号对相应的全局变量（DELETE_FILE,DELETE_LINE）进行赋值，而后调用 delete operator。当 delete operator 最终调用我们定义的 operator delete 的时候，在获得此次调用的文件名和行号信息后，对文件名和行号全局变量（DELETE_FILE,DELETE_LINE）重新初始化并打开互斥锁，让下一个挂在互斥锁上的 delete operator 得以执行。</font>
		</p>
		<p>
				<font size="2">在对 delete operator 作出如上修改以后，当我们发现无法经由 delete operator 传入的指针找到对应的内存分配信息的时候，就打印包括该次调用的文件名和行号的 warning。</font>
		</p>
		<p>
				<font size="2">天下没有十全十美的事情，既然我们提供了一种针对错误方式删除的提醒方法，我们就需要考虑以下几种异常情况：</font>
		</p>
		<p>
				<font size="2">1．用户使用的第三方库函数中有内存分配和释放操作。或者用户的被检测进程中进行内存分配和释放的实现文件没有使用我们的宏定义。由于我们替换了全局的 operator delete，这种情况下的用户调用的 delete 也会被我们截获。用户并没有使用我们定义的DEBUG_NEW 宏，所以我们无法在我们的全局对象（appMemory）数据结构中找到对应的内存分配信息，但是由于它也没有使用DEBUG_DELETE，我们为 delete 定义的两个全局 DELETE_FILE 和 DELETE_LINE 都不会有值，因此可以不打印 warning。</font>
		</p>
		<p>
				<font size="2">2．用户的一个实现文件调用了 new 进行内存分配工作，但是该文件并没有使用我们定义的 DEBUG_NEW 宏。同时用户的另一个实现文件中的代码负责调用 delete 来删除前者分配的内存，但不巧的是，这个文件使用了 DEBUG_DELETE 宏。这种情况下内存检测子系统会报告 warning，并打印出 delete 调用的文件名和行号。</font>
		</p>
		<p>
				<font size="2">3．与第二种情况相反，用户的一个实现文件调用了 new 进行内存分配工作，并使用我们定义的 DEBUG_NEW 宏。同时用户的另一个实现文件中的代码负责调用 delete 来删除前者分配的内存，但该文件没有使用 DEBUG_DELETE 宏。这种情况下，因为我们能够找到这个内存分配的原始信息，所以不会打印 warning。</font>
		</p>
		<p>
				<font size="2">4． 当出现嵌套 delete（定义可见"实现上的问题"）的情况下，以上第一和第三种情况都有可能打印出不正确的 warning 信息，详细分析可见"实现上的问题"一节。</font>
		</p>
		<p>
				<font size="2">你可能觉得这样的 warning 太随意了，有误导之嫌。怎么说呢？作为一个检测子系统，对待有可能的错误我们所采取的原则是：宁可误报，不可漏报。请大家"有则改之，无则加勉"。</font>
		</p>
		<p>
				<font size="2">
						<a name="N1014D">
								<span class="atitle2">5．动态内存泄漏信息的检测</span>
						</a>
						<br />上面我们所讲述的内存泄漏的检测能够在程序整个生命周期结束时，打印出在程序运行过程中已经在堆上分配但是没有释放的内存分配信息，程序员可以由此找到程序中"显式"的内存泄漏点并加以改正。但是如果程序在结束之前能够将自己所分配的所有内存都释放掉，是不是就可以说这个程序不存在内存泄漏呢？答案：否！在编程实践中，我们发现了另外两种危害性更大的"隐式"内存泄漏，其表现就是在程序退出时，没有任何内存泄漏的现象，但是在程序运行过程中，内存占用量却不断增加，直到使整个系统崩溃。</font>
		</p>
		<p>
				<font size="2">1． 程序的一个线程不断分配内存，并将指向内存的指针保存在一个数据存储中（如 list），但是在程序运行过程中，一直没有任何线程进行内存释放。当程序退出的时候，该数据存储中的指针值所指向的内存块被依次释放。</font>
		</p>
		<p>
				<font size="2">2．程序的N个线程进行内存分配，并将指针传递给一个数据存储，由M个线程从数据存储进行数据处理和内存释放。由于 N 远大于M，或者M个线程数据处理的时间过长，导致内存分配的速度远大于内存被释放的速度。但是在程序退出的时候，数据存储中的指针值所指向的内存块被依次释放。</font>
		</p>
		<p>
				<font size="2">之所以说他危害性更大，是因为很不容易这种问题找出来，程序可能连续运行几个十几个小时没有问题，从而通过了不严密的系统测试。但是如果在实际环境中 7×24 小时运行，系统将不定时的崩溃，而且崩溃的原因从 log 和程序表象上都查不出原因。</font>
		</p>
		<p>
				<font size="2">为了将这种问题也挑落马下，我们增加了一个动态检测模块 MemSnapShot，用于在程序运行过程中，每隔一定的时间间隔就对程序当前的内存总使用情况和内存分配情况进行统计，以使用户能够对程序的动态内存分配状况进行监视。</font>
		</p>
		<p>
				<font size="2">当客户使用 MemSnapShot 进程监视一个运行中的进程时，被监视进程的内存子系统将把内存分配和释放的信息实时传送给MemSnapShot。MemSnapShot 则每隔一定的时间间隔就对所接收到的信息进行统计，计算该进程总的内存使用量，同时以调用new进行内存分配的文件名和行号为索引值，计算每个内存分配动作所分配而未释放的内存总量。这样一来，如果在连续多个时间间隔的统计结果中，如果某文件的某行所分配的内存总量不断增长而始终没有到达一个平衡点甚至回落，那它一定是我们上面所说到的两种问题之一。</font>
		</p>
		<p>
				<font size="2">在实现上，内存检测子系统的全局对象（appMemory）的构造函数中以自己的当前 PID 为基础 key 值创建一个消息队列，并在operator new 和 operator delete 被调用的时候将相应的信息写入消息队列。MemSnapShot 进程启动时需要输入被检测进程的 PID，而后通过该 PID 组装 key 值并找到被检测进程创建的消息队列，并开始读入消息队列中的数据进行分析统计。当得到operator new 的信息时，记录内存分配信息，当收到 operator delete 消息时，删除相应的内存分配信息。同时启动一个分析线程，每隔一定的时间间隔就计算一下当前的以分配而尚未释放的内存信息，并以内存的分配位置为关键字进行统计，查看在同一位置（相同文件名和行号）所分配的内存总量和其占进程总内存量的百分比。</font>
		</p>
		<p>
				<font size="2">图4 是一个正在运行的 MemSnapShot 程序，它所监视的进程的动态内存分配情况如图所示：</font>
		</p>
		<p>
				<font size="2">
						<img height="295" alt="" src="http://www-128.ibm.com/developerworks/cn/linux/l-mleak/images/image006.jpg" width="495" border="0" xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />
						<br xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />图四 </font>
		</p>
		<p>
				<font size="2">在支持 MemSnapShot 过程中的实现上的唯一技巧是--对于被检测进程异常退出状况的处理。因为被检测进程中的内存检测子系统创建了用于进程间传输数据的消息队列，它是一个核心资源，其生命周期与内核相同，一旦创建，除非显式的进行删除或系统重启，否则将不被释放。</font>
		</p>
		<p>
				<font size="2">不错，我们可以在内存检测子系统中的全局对象（appMemory）的析构函数中完成对消息队列的删除，但是如果被检测进程非正常退出（CTRL+C，段错误崩溃等），消息队列可就没人管了。那么我们可以不可以在全局对象（appMemory）的构造函数中使用 signal 系统调用注册 SIGINT，SIGSEGV 等系统信号处理函数，并在处理函数中删除消息队列呢？还是不行，因为被检测进程完全有可能注册自己的对应的信号处理函数，这样就会替换我们的信号处理函数。最终我们采取的方法是利用 fork 产生一个孤儿进程，并利用这个进程监视被检测进程的生存状况，如果被检测进程已经退出（无论正常退出还是异常退出），则试图删除被检测进程所创建的消息队列。下面简述其实现原理：</font>
		</p>
		<p>
				<font size="2">在全局对象（appMemory）构造函数中，创建消息队列成功以后，我们调用 fork 创建一个子进程，而后该子进程再次调用 fork 创建孙子进程，并退出，从而使孙子进程变为一个"孤儿"进程（之所以使用孤儿进程是因为我们需要切断被检测进程与我们创建的进程之间的信号联系）。孙子进程利用父进程（被检测进程）的全局对象（appMemory）得到其 PID 和刚刚创建的消息队列的标识，并传递给调用 exec 函数产生的一个新的程序映象--MemCleaner。</font>
		</p>
		<p>
				<font size="2">MemCleaner 程序仅仅调用 kill(pid, 0);函数来查看被检测进程的生存状态，如果被检测进程不存在了（正常或者异常退出），则 kill 函数返回非 0 值，此时我们就动手清除可能存在的消息队列。</font>
		</p>
		<p>
				<font size="2">
						<a name="N10183">
								<span class="atitle2">6．实现上的问题：嵌套delete</span>
						</a>
						<br />在" 错误方式删除带来的问题"一节中，我们对 delete operator 动了个小手术--增加了两个全局变量（DELETE_FILE,DELETE_LINE）用于记录本次 delete 操作所在的文件名和行号，并且为了同步对全局变量（DELETE_FILE,DELETE_LINE）的访问，增加了一个全局的互斥锁。在一开始，我们使用的是 pthread_mutex_t，但是在测试中，我们发现 pthread_mutex_t 在本应用环境中的局限性。</font>
		</p>
		<p>
				<font size="2">例如如下代码：</font>
		</p>
		<font size="2">
				<a name="N1018F">
				</a>
				<br />
		</font>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<font size="2">
														<code>
																<br />		class B {…};<br /><br />			class A {<br />			public:<br />				A() {m_pB = NULL};<br />				A(B* pb) {m_pB = pb;};<br />				~A() <br />                                {<br />                                       if (m_pB != NULL)<br />           行号1					delete m_pB;		//这句最要命<br />                                 };<br />			private:<br />				class B* m_pB;<br />				……<br />			}<br /><br />		int main()<br />		{<br />			A* pA = new A(new B);<br />			……<br />          行号2		delete pA;		<br />                }<br /></code>
												</font>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<p>
				<font size="2">在上述代码中，main 函数中的一句 delete pA 我们称之为"嵌套删除"，即我们 delete A 对象的时候，在A对象的析构执行了另一个 delete B 的动作。当用户使用我们的内存检测子系统时，delete pA 的动作应转化为以下动作：</font>
		</p>
		<font size="2">
				<a name="N1019B">
				</a>
				<br />
		</font>
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<font size="2">
														<code>
																<br />		上全局锁<br />	        全局变量（DELETE_FILE,DELETE_LINE）赋值为文件名和行号2<br />                delete operator A<br />                  调用~A()<br />	            上全局锁<br />	            全局变量（DELETE_FILE,DELETE_LINE）赋值为文件名和行号1<br />	            delete operator B<br />		      调用~B()<br />                      返回~B()<br />		      调用operator delete B<br />			记录全局变量（DELETE_FILE,DELETE_LINE）值1并清除全局变量（DELETE_FILE,DELETE_LINE）值<br />			打开全局锁<br />		    返回operator delete B<br />	        返回delete operator B<br />             返回~A()<br />         调用 operator delete A<br />	   记录全局变量（DELETE_FILE,DELETE_LINE）值1并清除全局变量（DELETE_FILE,DELETE_LINE）值<br />	   打开全局锁<br />	 返回operator delete A<br />      返回 delete operator A<br /></code>
												</font>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<p>
				<font size="2">在这一过程中，有两个技术问题，一个是 <b xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">mutex 的可重入问题</b>，一个是嵌套删除时 <b xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">对全局变量（DELETE_FILE,DELETE_LINE）</b>现场保护的问题。 </font>
		</p>
		<p>
				<font size="2">所谓 <b xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">mutex 的可重入问题</b>，是指在同一个线程上下文中，连续对同一个 mutex 调用了多次 lock，然后连续调用了多次 unlock。这就是说我们的应用方式要求互斥锁有如下特性： </font>
		</p>
		<p>
				<font size="2">1． 要求在同一个线程上下文中，能够多次持有同一个互斥体。并且只有在同一线程上下文中调用相同次数的 unlock 才能放弃对互斥体的占有。</font>
		</p>
		<p>
				<font size="2">2． 对于不同线程上下文持有互斥体的企图，同一时间只有一个线程能够持有互斥体，并且只有在其释放互斥体之后，其他线程才能持有该互斥体。</font>
		</p>
		<p>
				<font size="2">Pthread_mutex_t 互斥体不具有以上特性，即使在同一上下文中，第二次调用 pthread_mutex_lock 将会挂起。因此，我们必须实现出自己的互斥体。在这里我们使用 semaphore 的特性实现了一个符合上述特性描述的互斥体 CCommonMutex（源代码见附件）。</font>
		</p>
		<p>
				<font size="2">为了支持特性 2，在这个 CCommonMutex 类中，封装了一个 semaphore，并在构造函数中令其资源值为 1，初始值为1。当调用 CCommonMutex::lock 接口时，调用 sem_wait 得到 semaphore，使信号量的资源为 0 从而让其他调用 lock 接口的线程挂起。当调用接口 CCommonMutex::unlock 时，调用 sem_post 使信号量资源恢复为 1，让其他挂起的线程中的一个持有信号量。</font>
		</p>
		<p>
				<font size="2">同时为了支持特性 1，在这个 CCommonMutex 增加了对于当前线程 pid 的判断和当前线程访问计数。当线程第一次调用 lock 接口时，我们调用 sem_wait 的同时，记录当前的 Pid 到成员变量 m_pid，并置访问计数为 1，同一线程（m_pid == getpid()）其后的多次调用将只进行计数而不挂起。当调用 unlock 接口时，如果计数不为 1，则只需递减访问计数，直到递减访问计数为 1 才进行清除 pid、调用 sem_post。（具体代码可见附件）</font>
		</p>
		<p>
				<font size="2">
						<b xmlns:dw="http://www.ibm.com/developerworks/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">嵌套删除时对全局变量（DELETE_FILE,DELETE_LINE）</b>现场保护的问题是指，上述步骤中在 A 的析构函数中调用 delete m_pB 时，对全局变量（DELETE_FILE,DELETE_LINE）文件名和行号的赋值将覆盖主程序中调用 delete pA 时对全局变量（DELETE_FILE,DELETE_LINE）的赋值，造成了在执行 operator delete A 时，delete pA 的信息全部丢失。 </font>
		</p>
		<p>
				<font size="2">要想对这些全局信息进行现场保护，最好用的就是堆栈了，在这里我们使用了 STL 提供的 stack 容器。在 DEBUG_DELETE 宏定义中，对全局变量（DELETE_FILE,DELETE_LINE）赋值之前，我们先判断是否前面已经有人对他们赋过值了--观察行号变量是否等于 0，如果不为 0，则应该将已有的信息压栈（调用一个全局函数 BuildStack() 将当前的全局文件名和行号数据压入一个全局堆栈globalStack），而后再对全局变量（DELETE_FILE,DELETE_LINE）赋值，再调用 delete operator。而在内存子系统的全局对象（appMemory）提供的 erase 接口里面，如果判断传入的文件名和行号为 0，则说明我们所需要的数据有可能被嵌套删除覆盖了，所以需要从堆栈中弹出相应的数据进行处理。</font>
		</p>
		<p>
				<font size="2">现在嵌套删除中的问题基本解决了，但是当嵌套删除与 "错误方式删除带来的问题"一节的最后所描述的第一和第三种异常情况同时出现的时候，由于用户的 delete 调用没有通过我们定义的 DEBUG_DELETE 宏，上述机制可能出现问题。其根本原因是我们利用stack 保留了经由我们的 DEBUG_DELETE 宏记录的 delete 信息的现场，以便在 operator delete 和全局对象（appMemory）的 erase 接口中使用，但是用户的没经过 DEBUG_DELETE 宏的 delete 操作却未曾进行压栈操作而直接调用了 operator delete，有可能将不属于这次操作的 delete 信息弹出，破坏了堆栈信息的顺序和有效性。那么，当我们因为无法找到这次及其后续的 delete 操作所对应的内存分配信息的时候，可能会打印出错误的 warning 信息。</font>
		</p>
<img src ="http://www.cppblog.com/tx7do/aggbug/5964.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tx7do/" target="_blank">杨粼波</a> 2006-04-20 17:45 <a href="http://www.cppblog.com/tx7do/articles/5964.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Linux守护进程的编程方法</title><link>http://www.cppblog.com/tx7do/articles/5963.html</link><dc:creator>杨粼波</dc:creator><author>杨粼波</author><pubDate>Thu, 20 Apr 2006 09:43:00 GMT</pubDate><guid>http://www.cppblog.com/tx7do/articles/5963.html</guid><wfw:comment>http://www.cppblog.com/tx7do/comments/5963.html</wfw:comment><comments>http://www.cppblog.com/tx7do/articles/5963.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tx7do/comments/commentRss/5963.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tx7do/services/trackbacks/5963.html</trackback:ping><description><![CDATA[
		<p>
				<span class="Contents">守护进程（Daemon）是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程是一种很有用的进程。Linux的大多数服务器就是用守护进程实现的。比如，Internet服务器inetd，Web服务器httpd等。同时，守护进程完成许多系统任务。比如，作业规划进程crond，打印进程lpd等。 <br />守护进程的编程本身并不复杂，复杂的是各种版本的Unix的实现机制不尽相同，造成不同Unix环境下守护进程的编程规则并不一致。这需要读者注意，照搬某些书上的规则（特别是BSD4.3和低版本的System V）到Linux会出现错误的。下面将全面介绍Linux下守护进程的编程要点并给出详细实例。 <br /><br />一． 守护进程及其特性 <br /><br />守护进程最重要的特性是后台运行。在这一点上DOS下的常驻内存程序TSR与之相似。其次，守护进程必须与其运行前的环境隔离开来。这些环境包括未关闭的文件描述符，控制终端，会话和进程组，工作目录以及文件创建掩模等。这些环境通常是守护进程从执行它的父进程（特别是shell）中继承下来的。最后，守护进程的启动方式有其特殊之处。它可以在Linux系统启动时从启动脚本/etc/rc.d中启动，可以由作业规划进程crond启动，还可以由用户终端（通常是shell）执行。<br /><br />总之，除开这些特殊性以外，守护进程与普通进程基本上没有什么区别。因此，编写守护进程实际上是把一个普通进程按照上述的守护进程的特性改造成为守护进程。如果读者对进程有比较深入的认识就更容易理解和编程了。 <br /><br />二． 守护进程的编程要点 <br /><br />前面讲过，不同Unix环境下守护进程的编程规则并不一致。所幸的是守护进程的编程原则其实都一样，区别在于具体的实现细节不同。这个原则就是要满足守护进程的特性。同时，Linux是基于Syetem V的SVR4并遵循Posix标准，实现起来与BSD4相比更方便。编程要点如下； <br /><br /></span>1. 屏蔽一些有关控制终端操作的信号。<br />这是为了防止在守护进程没有正常运转起来时，控制终端受到干扰退出或挂起。示例如下：<br /></p>
		<pre style="MARGIN-LEFT: 80px">
				<ccid_code>signal(SIGTTOU,SIG_IGN); <br />signal(SIGTTIN,SIG_IGN); <br />signal(SIGTSTP,SIG_IGN); <br />signal(SIGHUP ,SIG_IGN);</ccid_code>
		</pre>　所有的信号都有自己的名字。这些名字都以“SIG”开头，只是后面有所不同。开发人员可以通过这些名字了解到系统中发生了什么事。当信号出现时，开发人员可以要求系统进行以下三种操作：<br /><p align="left">　忽略信号。大多数信号都是采取这种方式进行处理的，这里就采用了这种用法。但值得注意的是对SIGKILL和SIGSTOP信号不能做忽略处理。<br />　捕捉信号。最常见的情况就是，如果捕捉到SIGCHID信号，则表示子进程已经终止。然后可在此信号的捕捉函数中调用waitpid()函数取得该子进程的进程ID和它的终止状态。另外，如果进程创建了临时文件，那么就要为进程终止信号SIGTERM编写一个信号捕捉函数来清除这些临时文件。<br />　执行系统的默认动作。对绝大多数信号而言，系统的默认动作都是终止该进程。对这些有关终端的信号，一般采用忽略处理，从而保障了终端免受干扰。<br />　这类信号分别是，SIGTTOU（表示后台进程写控制终端）、SIGTTIN（表示后台进程读控制终端）、SIGTSTP（表示终端挂起）和SIGHUP（进程组长退出时向所有会议成员发出的）。 </p><span class="Contents">2. 在后台运行。 <br />为避免挂起控制终端将Daemon放入后台执行。方法是在进程中调用fork使父进程终止，让Daemon在子进程中后台执行。 <br /></span><div style="MARGIN-LEFT: 40px"><div style="MARGIN-LEFT: 40px"><span class="Contents">if(pid=fork()) </span><br /><span class="Contents">exit(0);//是父进程，结束父进程，子进程继续 <br /><br /></span></div><span class="Contents"></span></div><span class="Contents">3. 脱离控制终端，登录会话和进程组 <br />有必要先介绍一下Linux中的进程与控制终端，登录会话和进程组之间的关系：进程属于一个进程组，进程组号（GID）就是进程组长的进程号（PID）。登录会话可以包含多个进程组。这些进程组共享一个控制终端。这个控制终端通常是创建进程的登录终端。 <br />控制终端，登录会话和进程组通常是从父进程继承下来的。我们的目的就是要摆脱它们，使之不受它们的影响。方法是在第1点的基础上，调用setsid()使进程成为会话组长： <br /></span><div style="MARGIN-LEFT: 40px"><div style="MARGIN-LEFT: 40px"><span class="Contents">setsid(); </span><br /></div><span class="Contents"></span></div><span class="Contents">说明：当进程是会话组长时setsid()调用失败。但第一点已经保证进程不是会话组长。setsid()调用成功后，进程成为新的会话组长和新的进程组长，并与原来的登录会话和进程组脱离。由于会话过程对控制终端的独占性，进程同时与控制终端脱离。 <br /><br />4. 禁止进程重新打开控制终端 <br />现在，进程已经成为无终端的会话组长。但它可以重新申请打开一个控制终端。可以通过使进程不再成为会话组长来禁止进程重新打开控制终端： <br /></span><div style="MARGIN-LEFT: 40px"><div style="MARGIN-LEFT: 40px"><span class="Contents">if(pid=fork()) </span><br /><span class="Contents">exit(0);//结束第一子进程，第二子进程继续（第二子进程不再是会话组长） <br /><br /></span></div><span class="Contents"></span></div><span class="Contents">5. 关闭打开的文件描述符 <br />进程从创建它的父进程那里继承了打开的文件描述符。如不关闭，将会浪费系统资源，造成进程所在的文件系统无法卸下以及引起无法预料的错误。按如下方法关闭它们(NOFILE在头文件<sys>中定义)： <br /></sys></span><div style="MARGIN-LEFT: 40px"><div style="MARGIN-LEFT: 40px"><span class="Contents">for(i=0;i &lt; NOFILE;i++)<br />close(i);<br /><br /></span></div><span class="Contents"></span></div><span class="Contents">6. 改变当前工作目录 <br />进程活动时，其工作目录所在的文件系统不能卸下。一般需要将工作目录改变到根目录。对于需要转储核心，写运行日志的进程将工作目录改变到特定目录如/tmp：<br /></span><div style="MARGIN-LEFT: 40px"><div style="MARGIN-LEFT: 40px"><span class="Contents">chdir("/tmp") <br /><br /></span></div><span class="Contents"></span></div><span class="Contents">7. 重设文件创建掩模 <br />进程从创建它的父进程那里继承了文件创建掩模。它可能修改守护进程所创建的文件的存取位。为防止这一点，将文件创建掩模清除：<br /></span><div style="MARGIN-LEFT: 40px"><div style="MARGIN-LEFT: 40px"><span class="Contents">umask(0); <br /><br /></span></div><span class="Contents"></span></div><span class="Contents">8. 处理SIGCHLD信号 <br />处理SIGCHLD信号并不是必须的。但对于某些进程，特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束，子进程将成为僵尸进程（zombie）从而占用系统资源。如果父进程等待子进程结束，将增加父进程的负担，影响服务器进程的并发性能。在Linux下可以简单地将 SIGCHLD信号的操作设为SIG_IGN。 <br /></span><div style="MARGIN-LEFT: 40px"><div style="MARGIN-LEFT: 40px"><span class="Contents">signal(SIGCHLD,SIG_IGN); </span><br /></div><span class="Contents"></span></div><span class="Contents">这样，内核在子进程结束时不会产生僵尸进程。这一点与BSD4不同，BSD4下必须显式等待子进程结束才能释放僵尸进程。 </span><img src ="http://www.cppblog.com/tx7do/aggbug/5963.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tx7do/" target="_blank">杨粼波</a> 2006-04-20 17:43 <a href="http://www.cppblog.com/tx7do/articles/5963.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>