技术开发 频道

基于LGPL开源项目 Log4cpp安装与使用

  6、Appender

  笔者认为Appender是log4cpp中最精彩的一个部分。我仔细阅读了大部分Appender的源代码并对设计者感到非常敬仰。

  Log4cpp中所有可直接使用的Appender列表如下:

log4cpp::IdsaAppender             // 发送到IDS或者
log4cpp::FileAppender             // 输出到文件
log4cpp::RollingFileAppender     // 输出到回卷文件,即当文件到达某个大小后回卷
log4cpp::OstreamAppender         // 输出到一个ostream类
log4cpp::RemoteSyslogAppender     // 输出到远程syslog服务器
log4cpp::StringQueueAppender     // 内存队列
log4cpp::SyslogAppender         // 本地syslog
log4cpp::Win32DebugAppender     // 发送到缺省系统调试器
log4cpp::NTEventLogAppender     // 发送到win 事件日志

  其中SyslogAppender和RemoteSyslogAppender需要与Syslog配合使用,因此这里不介绍。顺便提一句,Syslog是类Unix系统的一个核心服务,用来提供日志服务,在Windows系统中并没有直接提供支持,当然可以用相关工具提供Windows系统中的syslog服务。

  IdsaAppender的功能是将日志写入Idsa服务,这里也不介绍。因此主要介绍以下Appender:

log4cpp::FileAppender             // 输出到文件
log4cpp::RollingFileAppender     // 输出到回卷文件,即当文件到达某个大小后回卷
log4cpp::OstreamAppender         // 输出到一个ostream类
log4cpp::StringQueueAppender     // 内存队列
log4cpp::Win32DebugAppender     // 发送到缺省系统调试器
log4cpp::NTEventLogAppender     // 发送到win 事件日志

  6.1 OstreamAppender

  在我刚刚学习C/C++编程时,一位老师告诉我,如果没有好用的调试工具,就在代码中加入printf语句,将调试信息打印出来(当时在linux下面,确实没有什么易用的c++调试工具)。现在有了OstreamAppender,一切都好办了,它可以将日志记入一个流,如果该流恰好是cout,则会在标准控制台上输出。比printf优越的是,除了输出消息外,还可以轻松的输出时间、时钟数、优先级等大量有用信息。

  OstreamAppender的使用非常简单,在前面的HelloWorld程序中已经见过,创建一个OstreamAppender的具体方法如下:

log4cpp::OstreamAppender* osAppender = new log4cpp::OstreamAppender("osAppender", &cout);

  第一个参数指定OstreamAppender的名称,第二个参数指定它关联的流的指针。

  6.2 StringQueueAppender

  后来一位高手又告诉我“在调试多线程程序时,不能随意使用printf”。因为printf导致IO中断,会使得本线程挂起,其花费的时间比一条普通指令多数千倍,若多个线程同时运行,则严重干扰了线程间的运行方式。所以调试多线程程序时,最好是将所有调试信息按顺序记入内存中,程序结束时依次打印出来。为此当时我们还写了一个小工具,没想到时隔多年,我碰上了StringQueueAppender。

  我很怀疑StringQueueAppender被设计出来就是用于记录多线程程序或者实时程序的日志,虽然log4cpp的文档中并没有明确指出这一点。StringQueueAppender的功能是将日志记录到一个字符串队列中,该字符串队列使用了STL中的两个容器,即字符串容器std::string和队列容器std::queue,具体如下:

std::queue<std::string> _queue;

   _queue变量是StringQueueAppender类中用于具体存储日志的内存队列。StringQueueAppender的使用方法与OstreamAppender类似,其创建函数只接收一个参数“名称”,记录完成后需要程序员自己从队列中取出每条日志,例子程序StringQueueAppenderExam如下:

#include <iostream>
#include
<log4cpp/Category.hh>
#include
<log4cpp/OstreamAppender.hh>
#include
<log4cpp/BasicLayout.hh>
#include
<log4cpp/Priority.hh>
#include
<log4cpp/StringQueueAppender.hh>
using namespace std;

int main(int argc, char* argv[])
{
    log4cpp::StringQueueAppender* strQAppender = new log4cpp::StringQueueAppender("strQAppender");
    strQAppender->setLayout(new log4cpp::BasicLayout());
    
    log4cpp::Category& root = log4cpp::Category::getRoot();
    root.addAppender(strQAppender);
    root.setPriority(log4cpp::Priority::DEBUG);
    
    root.error("Hello log4cpp in a Error Message!");
    root.warn("Hello log4cpp in a Warning Message!");
    
    cout
<<"Get message from Memory Queue!"<<endl;
    cout<<"-------------------------------------------"<<endl;
    queue<string
>& myStrQ = strQAppender->getQueue();
    while(!myStrQ.empty())
    {
        cout
<<myStrQ.front();
        myStrQ.pop();
    }

    log4cpp::Category::shutdown();    
    return 0;
}

  程序输出为:

Get message from Memory Queue!
-------------------------------------------
1248839389 ERROR  : Hello log4cpp in a Error Message!
1248839389 WARN  : Hello log4cpp in a Warning Message!

  6.3 FileAppender和RollingFileAppender

  FileAppender和RollingFileAppender是log4cpp中最常用的两个Appender,其功能是将日志写入文件中。它们之间唯一的区别就是前者会一直在文件中记录日志(直到操作系统承受不了为止),而后者会在文件长度到达指定值时循环记录日志,文件长度不会超过指定值(默认的指定值是10M byte)。

  FileAppender的创建函数如下:

        /**
           Constructs a FileAppender.
           @param name the name of the Appender.
           @param fileName the name of the file to which the Appender has
           to log.
           @param append whether the Appender has to truncate the file or
           just append to it if it already exists. Defaults to 'true'.
           @param mode file mode to open the logfile with. Defaults to 00644.
        **/  
        FileAppender(const std::string& name, const std::string& fileName,
                     bool append = true, mode_t mode = 00644);

  一般仅使用前两个参数,即“名称”和“日志文件名”。第三个参数指示是否在日志文件后继续记入日志,还是清空原日志文件再记录。第四个参数说明文件的打开方式。

  RollingFileAppender的创建函数如下:

        RollingFileAppender(const std::string& name,
                            const std::string& fileName,
                            size_t maxFileSize = 10*1024*1024,
                            unsigned int maxBackupIndex = 1,
                            bool append = true,
                            mode_t mode = 00644);

  它与FileAppender的创建函数很类似,但是多了两个参数:maxFileSize指出了回滚文件的最大值;maxBackupIndex指出了回滚文件所用的备份文件的最大个数。所谓备份文件,是用来保存回滚文件中因为空间不足未能记录的日志,备份文件的大小仅比回滚文件的最大值大1kb。所以如果maxBackupIndex取值为3,则回滚文件(假设其名称是rollwxb.log,大小为100kb)会有三个备份文件,其名称分别是rollwxb.log.1,rollwxb.log.2和rollwxb.log.3,大小为101kb。另外要注意:如果maxBackupIndex取值为0或者小于0,则回滚文件功能会失效,其表现如同FileAppender一样,不会有大小的限制。这也许是一个bug。

  例子程序FileAppenderExam如下:

#include <iostream>
#include
<log4cpp/Category.hh>
#include
<log4cpp/Appender.hh>
#include
<log4cpp/FileAppender.hh>
#include
<log4cpp/Priority.hh>
#include
<log4cpp/PatternLayout.hh>
#include
<log4cpp/RollingFileAppender.hh>
using namespace std;

int main(int argc, char* argv[])
{
    log4cpp::PatternLayout* pLayout1 = new log4cpp::PatternLayout();
    pLayout1->setConversionPattern("%d: %p %c %x: %m%n");

    log4cpp::PatternLayout* pLayout2 = new log4cpp::PatternLayout();
    pLayout2->setConversionPattern("%d: %p %c %x: %m%n");
    
    log4cpp::Appender* fileAppender = new log4cpp::FileAppender("fileAppender","wxb.log");
    fileAppender->setLayout(pLayout1);

    log4cpp::RollingFileAppender* rollfileAppender = new log4cpp::RollingFileAppender(
        "rollfileAppender","rollwxb.log",5*1024,1);
    rollfileAppender->setLayout(pLayout2);
    
    log4cpp::Category& root = log4cpp::Category::getRoot().getInstance("RootName");
    root.addAppender(fileAppender);
    root.addAppender(rollfileAppender);
    root.setPriority(log4cpp::Priority::DEBUG);

    for (int i = 0; i
< 100; i++)
    {
        string strError;
        ostringstream oss;
        oss<<i<<":Root Error Message!";
        strError
= oss.str();
        
root.error(strError);
    }
    
    log4cpp::Category::shutdown();
    return 0;
}

  程序运行后会产生两个日志文件wxb.log和rollwxb.log,以及一个备份文件rollwxb.log.1。wxb.log的大小为7kb,记录了所有100条日志;rollwxb.log大小为2kb,记录了最新的22条日志;rollwxb.log.1大小为6kb,记录了旧的78条日志。

  6.4 Win32DebugAppender

  Win32DebugAppender是一个用于调试的Appender,其功能是向Windows的调试器中写入日志,目前支持MSVC和Borland中的调试器。创建Win32DebugAppender仅需要一个参数“名称”,其使用非常简单,下面是例子代码DebugAppenderExam:

#include <iostream>
#include
<log4cpp/Category.hh>
#include
<log4cpp/Appender.hh>
#include
<log4cpp/Win32DebugAppender.hh>
#include
<log4cpp/Priority.hh>
#include
<log4cpp/PatternLayout.hh>
using namespace std;

int main(int argc, char* argv[])
{
    log4cpp::PatternLayout* pLayout1 = new log4cpp::PatternLayout();
    pLayout1->setConversionPattern("%d: %p %c %x: %m%n");
    
    log4cpp::Appender* debugAppender = new log4cpp::Win32DebugAppender("debugAppender");
    debugAppender->setLayout(pLayout1);
    
    log4cpp::Category& root = log4cpp::Category::getRoot().getInstance("RootName");
    root.addAppender(debugAppender);
    root.setPriority(log4cpp::Priority::DEBUG);
    
    root.error("Root Error Message!");
    root.warn("Root Warning Message!");
    
    log4cpp::Category::shutdown();
    return 0;
}

  在VC6中调试该代码会得到如下图所示的调试信息,注意最下方的两行调试信息:

Appender 

  6.5 NTEventLogAppender

   该Appender可以将日志发送到windows的日志,在运行程序后可以打开windows的计算机管理?系统工具?事件查看器?应用程序,可以看到下图,注意图中第一行和第二行的两个日志。

Appender 

  例子程序NTAppenderExam如下:

#include <iostream>
#include
<log4cpp/Category.hh>
#include
<log4cpp/Appender.hh>
#include
<log4cpp/NTEventLogAppender.hh>
#include
<log4cpp/Priority.hh>
#include
<log4cpp/PatternLayout.hh>
using namespace std;

int main(int argc, char* argv[])
{
    log4cpp::PatternLayout* pLayout1 = new log4cpp::PatternLayout();
    pLayout1->setConversionPattern("%d: %p %c %x: %m%n");
    
    log4cpp::Appender* ntAppender = new log4cpp::NTEventLogAppender("debugAppender","wxb_ntlog");
    ntAppender->setLayout(pLayout1);
    
    log4cpp::Category& root = log4cpp::Category::getRoot().getInstance("RootName");
    root.addAppender(ntAppender);
    root.setPriority(log4cpp::Priority::DEBUG);
    
    root.error("Root Error Message!");
    root.warn("Root Warning Message!");
    
    log4cpp::Category::shutdown();
    return 0;
}
4
相关文章