跳转至

三个组件

Log4cpp有三个主要的组件:类别(Category)、附加目的地(Appender,即输出源)、布局(Layout)。

类别 职责 说明
Category 负责向日志写入信息 1. 系统中可以有多个Category,它们都是继承自同一个根,每个Category负责记录自己的日志
2. 每个Category可以添加多个Appender
3. 当Category记录一条日志时,该日志被写入所有附加到此Category的Appender
Appender 负责指定日志的输出地点 1. 每个Appender指定一个日志的目的地,例如文件、字符流或者Windows日志
2. 每个Append都包含一个Layout,该Layout定义了这个Appender上的日志格式
Layout 负责设定日志的格式

类别(Category)

【说明】每个Category都有一个优先级,该优先级可以由setPriority方法设置,或者从其父的Category中继承而来。

【优先级】如果**日志优先级**高于**Category的优先级**,该日志被记录,否则被忽略。 日志优先级:DEBUG < INFO < WARN < ERROR < FATAL

  1. 如果配置文件中设置的级别是DEBUG,则任意的log都能打印出来
  2. 如果配置的级别是ERROR,则只有高于ERROR优先级的日志才可以打印出来
    typedefenum {
        EMERG  = 0,
        FATAL  = 0,
        ALERT  = 100,
        CRIT   = 200,
        ERROR  = 300,
        WARN   = 400,
        NOTICE =500,
        INFO   = 600,
        DEBUG  = 700,
        NOTSET = 800
    }PriorityLevel;
    
    【补充】Log4cpp中有一个总是可用并实例化好的Category,即根Category。使用log4cpp::Category::getRoot()可以得到根Category。在大多数情况下,一个应用程序只需要一个日志种类(Category),但是有时也会用到多个Category,此时可以使用根Category的getInstance方法来得到子Category。不同的子Category用于不同的场合

【例子】这个例子中共有三个Category,分别是根、sub1和sub2,其中sub1记录了一条日志,sub2记录了两条日志。Sub2另外两个日志由于优先级不够未能记录。

log4cpp::OstreamAppender*osAppender1 = new log4cpp::OstreamAppender("osAppender1",&cout);
osAppender1->setLayout(newlog4cpp::BasicLayout());

log4cpp::OstreamAppender*osAppender2 = new log4cpp::OstreamAppender("osAppender2",&cout);
osAppender2->setLayout(newlog4cpp::BasicLayout());

log4cpp::Category& root =log4cpp::Category::getRoot();
root.setPriority(log4cpp::Priority::DEBUG);

log4cpp::Category& sub1 =root.getInstance("sub1");
sub1.addAppender(osAppender1);
sub1.setPriority(log4cpp::Priority::DEBUG);

sub1.error("suberror");

log4cpp::Category& sub2 =root.getInstance("sub2");
sub2.addAppender(osAppender2);
sub2.setPriority(101);

sub2.warn("sub2warning");
sub2.fatal("sub2fatal");
sub2.alert("sub2alert");
sub2.crit("sub2crit");

log4cpp::Category::shutdown();

输出源(Appender)

输出源用来输出日志(被layout格式化后)到一些设备上,比如文件、命令行、内存等。也可以自定义appender,以输出到别的设备上。 log4cpp中可以直接使用的Appender列表如下:

类别 职责
log4cpp::IdsaAppender 发送到Idsa服务
log4cpp::FileAppender  输出到文件
log4cpp::RollingFileAppender 输出到回卷文件,即当文件到达某个大小后回卷
log4cpp::OstreamAppender 输出到一个ostream类
log4cpp::RemoteSyslogAppender 输出到远程syslog服务器。需要与Syslog配合使用
log4cpp::StringQueueAppender 内存队列
log4cpp::SyslogAppender 本地syslog。需要与Syslog配合使用
log4cpp::Win32DebugAppender 发送到缺省系统调试器
log4cpp::NTEventLogAppender 发送到win事件日志

补充:Syslog是类Unix系统的一个核心服务,用来提供日志服务,在Windows系统中并没有直接提供支持,当然可以用相关工具()提供Windows系统中的syslog服务。

OstreamAppender

【功能】将日志记录成一个流,如果该流恰好是cout,则会在标准控制台上输出。比printf优越的是,除了输出消息外,还可以轻松的输出时间、时钟数、优先级等大量有用信息。

【使用】

log4cpp::OstreamAppender* osAppender = newlog4cpp::OstreamAppender("osAppender", &cout);
    //第一个参数指定OstreamAppender的名称
    //第二个参数指定它关联的流的指针。

StringQueueAppender

【背景】“在调试多线程程序时,不能随意使用printf”。因为printf导致IO中断,会使得本线程挂起,其花费的时间比一条普通指令多数千倍。若多个线程同时运行,则严重干扰了线程间的运行方式。

【功能】将日志记录到一个字符串队列中,该字符串队列使用了STL中的两个容器,即字符串容器std::string和队列容器std::queue

【原理】

std::queue<std::string> _queue;
    //_queue变量是StringQueueAppender类中用于具体存储日志的内存队列
    //StringQueueAppender的使用方法与OstreamAppender类似
        //数只接收一个参数“名称”,记录完成后需要程序员自己从队列中取出每条日志

【示例】

og4cpp::StringQueueAppender* strQAppender = newlog4cpp::StringQueueAppender("strQAppender");
strQAppender->setLayout(newlog4cpp::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 WarningMessage!");
cout<<"Get message from MemoryQueue!"<<endl;

cout<<"-------------------------------------------"<<endl;
queue<string>& myStrQ =strQAppender->getQueue();
while(!myStrQ.empty())
{
    cout<<myStrQ.front();
    myStrQ.pop();
}
log4cpp::Category::shutdown();  

FileAppender和RollingFileAppender

【说明】FileAppender和RollingFileAppender是log4cpp中最常用的两个Appender,其功能是将日志写入文件中。

【两者的区别】它们之间唯一的区别就是前者会一直在文件中记录日志(直到操作系统承受不了为止),而后者会在文件长度到达指定值时循环记录日志,文件长度不会超过指定值(默认的指定值是10M byte)。

//FileAppender创建函数
FileAppender(const std::string& name,       //名称
             const std::string& fileName,   //日志文件名
             bool append = true,            //是否在日志文件后继续记入日志,还是清空原日志文件再记录
             mode_tmode = 00644);           //文件的打开方式
//RollingFileAppender的创建函数
RollingFileAppender(const std::string&name,         
                    const std::string&fileName,  
                    size_t maxFileSize =10*1024*1024, //回滚文件的最大值
                    unsigned intmaxBackupIndex = 1, //回滚文件所用的备份文件的最大个数
                    boolappend = true,  
                    mode_t mode =00644);

【补充】

  1. 所谓备份文件,是用来保存回滚文件中因为空间不足未能记录的日志,备份文件的大小仅比回滚文件的最大值大1kb。所以如果maxBackupIndex取值为3,则回滚文件(假设其名称是rollwxb.log,大小为100kb)会有三个备份文件,其名称分别是rollwxb.log.1,rollwxb.log.2和rollwxb.log.3,大小为101kb。
  2. 如果maxBackupIndex取值为0或者小于0,则回滚文件功能会失效,其表现如同FileAppender一样,不会有大小的限制。这也许是一个bug。

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

log4cpp::PatternLayout* pLayout1 = newlog4cpp::PatternLayout();
pLayout1->setConversionPattern("%d: %p %c%x: %m%n");

log4cpp::PatternLayout* pLayout2 = newlog4cpp::PatternLayout();
pLayout2->setConversionPattern("%d: %p %c%x: %m%n");

log4cpp::Appender* fileAppender = newlog4cpp::FileAppender("fileAppender","wxb.log");
fileAppender->setLayout(pLayout1);

log4cpp::RollingFileAppender* rollfileAppender = newlog4cpp::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<<":RootError Message!";
    strError = oss.str();
    root.error(strError);
}

log4cpp::Category::shutdown();

Win32DebugAppender

【功能】Win32DebugAppender是一个用于调试的Appender,其功能是向Windows的调试器中写入日志,目前支持MSVC和Borland中的调试器

log4cpp::PatternLayout* pLayout1 = newlog4cpp::PatternLayout();
pLayout1->setConversionPattern("%d: %p %c%x: %m%n");
log4cpp::Appender* debugAppender = newlog4cpp::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();

NTEventLogAppender

【功能】该Appender可以将日志发送到windows的日志,在运行程序后可以打开windows的计算机管理->系统工具->事件查看器->应用程序

log4cpp::PatternLayout* pLayout1 = newlog4cpp::PatternLayout();
pLayout1->setConversionPattern("%d: %p %c%x: %m%n");
log4cpp::Appender* ntAppender = newlog4cpp::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();

布局(Layout)

【介绍】布局用来显示样式,表示让用户根据类似于C语言printf函数的转换模式来输出格式。 【Layout类】Layout本身是一个虚类,共有三个子类

类别 说明 例子
BasicLayout 1. 以“时间戳”为优先级
2. NDC标签:日志信息
1248337987 ERROR: ..
PatternLayout 1. 让用户根据类似于printf方式输出 
2. 如果不选择扩展Layout,建议使用PatternLayout,从而达到理想的输出格式
2009-07-24 15:59:55,703:INFO
SimpleLayout 1. 以“日志信息”为优先级

PatternLayout

PatternLayout使用setConversionPattern函数来设置日志的输出格式。该函数的声明如下:

void log4cpp::PatternLayout::setConversionPattern(const std::string& conversionPattern)
    throw (ConfigureFailure) [virtual]
参数const std::string& conversionPattern用格式化字符串来描述格式,如%c、%d、%p等,具体说明请见下文“ConversionPattern的参数含义”。

总结

这三种类型的组件一起工作使得系统可以根据信息的类型和级别记录它们,并且在运行时控制这些信息的输出格式和位置。 三者关系:

  1. Category和Appender的关系是:多个Appender可以附加到一个Category上,这样一个日志消息可以同时输出到多个设备上
  2. Appender和Layout的关系是:Layout附加在Appender上,appender调用layout处理完日志消息后,记录到某个设备上。