log4cplus server client模式、log4cplus、log4cplus库(三)(将日志写入到文件)

转载地址1:http://blog.csdn.net/dds_dev_group/article/details/6973847


转载地址2:http://blog.csdn.net/guang11cheng/article/details/7400134


转载地址3:http://www.cppblog.com/API/archive/2011/04/02/143281.html


一、log4cplus server client模式

在多进程使用log4cplus同时向一个日志文件写的时候,官方的FAQ建议使用SoskcetAppender, 即以server client模式来写日志,保证写日志同步。

写了一个小程序,fork出一个server进程,和5个client进程来写日志,当日志大小到达4G的时候做切割。

[cpp]  view plain copy
  1. /* 
  2.  * ===================================================================================== 
  3.  * 
  4.  *       Filename:  multiprocesslog.cpp 
  5.  * 
  6.  *    Description:   
  7.  * 
  8.  *        Version:  1.0 
  9.  *        Created:  11/15/2011 02:48:44 PM 
  10.  *       Revision:  none 
  11.  *       Compiler:  gcc 
  12.  * 
  13.  *         Author:  Xu Zhe Ming,  
  14.  *        Company:   
  15.  * 
  16.  * ===================================================================================== 
  17.  */  
  18.   
  19. #include   
  20. #include   
  21. #include   
  22. #include   
  23. #include   
  24. #include   
  25. #include   
  26. #include   
  27. #include   
  28. #include   
  29. #include   
  30. #include   
  31.   
  32. using namespace log4cplus;  
  33. using namespace std;  
  34. using namespace log4cplus::helpers;  
  35.   
  36. //server端  
  37. int log_server(int port)  
  38. {  
  39.     //创建一个socket,绑定端口  
  40.     helpers::ServerSocket serverSocket(port);  
  41.     Logger debug_logger;  
  42.     Logger info_logger;  
  43.   
  44.     //注意int会溢出  
  45.     long log_size = long(1024) * 1024 * 1024 * 4;  
  46.   
  47.     //初始化一个debug logger,并绑定到一个文件  
  48.     {  
  49.     SharedAppenderPtr pFileAppender_normal(new RollingFileAppender(("log_file_debug") , log_size, 10));  
  50.     debug_logger = Logger::getInstance("debug_logger");  
  51.     debug_logger.addAppender(pFileAppender_normal);  
  52.     }  
  53.   
  54.     //初始化一个info logger,并绑定到一个文件  
  55.     {  
  56.     SharedAppenderPtr pFileAppender_normal(new RollingFileAppender(("log_file_info") , log_size, 10));  
  57.     info_logger = Logger::getInstance("info_logger");  
  58.     info_logger.addAppender(pFileAppender_normal);  
  59.     }  
  60.   
  61.     //初始化其他的logger,如warn, error, fatal  
  62.     //...  
  63.   
  64.     while(1)  
  65.     {  
  66.     //accept  
  67.     helpers::Socket clientsock = serverSocket.accept();  
  68.     SocketBuffer msgSizeBuffer(sizeof(unsigned int));  
  69.     if(!clientsock.read(msgSizeBuffer))  
  70.     {  
  71.         return 0;  
  72.     }  
  73.     unsigned int msgSize = msgSizeBuffer.readInt();  
  74.     SocketBuffer buffer(msgSize);  
  75.     //读取日志消息到buffer  
  76.     if(!clientsock.read(buffer))  
  77.     {  
  78.         return 0;  
  79.     }  
  80.       
  81.     //转化成event  
  82.     spi::InternalLoggingEvent event = readFromBuffer(buffer);  
  83.     int level = event.getLogLevel();  
  84.     //判断日志的level  
  85.     if(level == DEBUG_LOG_LEVEL)  
  86.     {  
  87.         debug_logger.callAppenders(event);  
  88.     }  
  89.     else if(level == INFO_LOG_LEVEL)  
  90.     {  
  91.         info_logger.callAppenders(event);  
  92.     }  
  93.     //...  
  94.     }  
  95.   
  96.     return 0;  
  97. }  
  98.   
  99. //client端  
  100. int log_client(string server_ip, int server_port)  
  101. {  
  102.     SharedAppenderPtr _append(new SocketAppender(server_ip, server_port, "ServerName"));  
  103.     Logger::getRoot().addAppender(_append);  
  104.     while(1)  
  105.     {  
  106.     int rand_num = rand();  
  107.     int remainder = rand_num % 2;  
  108.     if (remainder == 0)  
  109.         LOG4CPLUS_INFO(Logger::getRoot(), "client logging event number ... ");  
  110.     else  
  111.         LOG4CPLUS_DEBUG(Logger::getRoot(), "client logging odd number... ");  
  112.     //sleep(1);  
  113.     }  
  114. }  
  115.   
  116.   
  117. int main()  
  118. {  
  119.       
  120.     pid_t pid = fork();  
  121.       
  122.     //child, let's run a log server process  
  123.     if(pid == 0)  
  124.     {  
  125.     log_server(20000);  
  126.     }  
  127.     //parent, let's fork some more processes to write logs  
  128.     else  
  129.     {  
  130.     for(int i = 0; i < 5; i++)  
  131.     {  
  132.         pid = fork();  
  133.         //child, let's run a log client process  
  134.         if(pid == 0)  
  135.         {  
  136.         log_client("localhost", 20000);  
  137.         }  
  138.     }  
  139.     }  
  140.   
  141.     while(1)  
  142.     {  
  143.     sleep(1);  
  144.     }  
  145.   
  146.     return 0;  
  147. }  


二、log4cplus

1,向文件中输出日志

#include

#include
#include
#include
#include
#include
#include


using namespace log4cplus;


const int LOOP_COUNT = 1000000;


int main()
{
    // RollingFileAppender and TTCCLayout
    SharedAppenderPtr append1(
        new RollingFileAppender(LOG4CPLUS_TEXT("test1.log"), 5*1024, 5)); // 系统要求的最低大小为200k,这里设置的是5k,系统会自动调整为200k
    append1->setName(LOG4CPLUS_TEXT("1"));
    append1->setLayout(std::auto_ptr(new TTCCLayout()));


    // FileAppender and SimpleLayout
    SharedAppenderPtr append2(
        new FileAppender(LOG4CPLUS_TEXT("test2.log"))); // 文件大小无止境?
    append2->setName(LOG4CPLUS_TEXT("2"));
    append2->setLayout(std::auto_ptr(new SimpleLayout()));


    /* DailyRollingFileAppender(不止是daily,也可以是
    MONTHLY(每月)、WEEKLY(每周)、DAILY(每日)、TWICE_DAILY(每两天)、
    HOURLY(每时)、MINUTELY(每分)) and PatternLayout。*/
    SharedAppenderPtr append3(
        new DailyRollingFileAppender(LOG4CPLUS_TEXT("test3.log"), DAILY, true, 10));
    append3->setName(LOG4CPLUS_TEXT("3"));


    std::string pattern = "[%D{%Y-%m-%d %H:%M:%S %q}] [%-5p] <%l %M> %m%n";
    append3->setLayout(std::auto_ptr(new PatternLayout(pattern)));


    Logger root = Logger::getRoot();
    root.setLogLevel(ALL_LOG_LEVEL); // 输出所有级别的日志
    Logger logger1 = Logger::getInstance(LOG4CPLUS_TEXT("test1"));
    Logger logger2 = Logger::getInstance(LOG4CPLUS_TEXT("test2"));
    Logger logger3 = Logger::getInstance(LOG4CPLUS_TEXT("test3"));


    logger1.addAppender(append1);
    logger2.addAppender(append2);
    logger3.addAppender(append3);


    for (int i = 0; i < LOOP_COUNT; ++i) 
    {
        /*log4cplus中嵌入诊断上下文(Nested Diagnostic Context),即NDC。
        对log系统而言,
        当输入源可能不止一个,而只有一个输出时,往往需要分辩所有输出消息的来源,比如服务器处理来自不同
        客户端的消息时就需要作此判断,NDC可以为交错显示的信息打上一个标记(stamp), 使得辨认工作看起来
        容易些。这个标记是线程特有的,利用了线程局部存储机制,称为线程私有数据(Thread-specific
        Data,或TSD)。*/
        NDCContextCreator _context(LOG4CPLUS_TEXT("loop")); 
        
        LOG4CPLUS_DEBUG(logger1, "Entering loop #" << i);
        LOG4CPLUS_DEBUG(logger2, "Entering loop #" << i);
        LOG4CPLUS_DEBUG(logger3, "Entering loop #" << i);
    }


    // 从配置文件中获取logger、appender和layout
    PropertyConfigurator::doConfigure(LOG4CPLUS_TEXT("urconfig.properties"));
    Logger fileCat = Logger::getInstance(LOG4CPLUS_TEXT("filelogger"));


    for (int i = 0; i < LOOP_COUNT; ++i)
    {
        LOG4CPLUS_TRACE(fileCat, LOG4CPLUS_TEXT("This is a TRACE...") << i);
        LOG4CPLUS_DEBUG(fileCat, LOG4CPLUS_TEXT("This is a DEBUG...") << i);
        LOG4CPLUS_INFO(fileCat, LOG4CPLUS_TEXT("This is a INFO...") << i);
        LOG4CPLUS_WARN(fileCat, LOG4CPLUS_TEXT("This is a WARN...") << i);
        LOG4CPLUS_ERROR(fileCat, LOG4CPLUS_TEXT("This is a ERROR...") << i);
        LOG4CPLUS_FATAL(fileCat, LOG4CPLUS_TEXT("This is a FATAL...") << i);
    }


    /* 问题:DailyRollingFileAppender和RollingFileAppender不能同时使用,即一个appender不能既是
       DailyRollingFileAppender的又同时是RollingFileAppender的
    */


    return 0;
}


/*
g++ logdemo5.cpp -o demoxx -g -Wall -I /usr/local/include/ -L /usr/local/lib/ -llog4cplus
或者
g++ logdemo5.cpp -o demo1 -g -Wall -I /usr/local/include/ /usr/local/lib/liblog4cplus.a -lpthread

*/


// urconfig.properties文件

log4cplus.logger.filelogger=TRACE, append_1

log4cplus.appender.append_1=log4cplus::DailyRollingFileAppender
log4cplus.appender.append_1.Schedule=MINUTELY
log4cplus.appender.append_1.File=./log/test.log #目录要手动创建!
#log4cplus.appender.append_1.MaxFileSize=512000 #无效果
#log4cplus.appender.append_1.MaxBackupIndex=5   #貌似也无效果

log4cplus.appender.append_1.layout=log4cplus::PatternLayout
log4cplus.appender.append_1.layout.ConversionPattern=[%D{%Y-%m-%d %H:%M:%S %q}] [%-5p] <%l %M> %m%n


2,性能数据:



void performanceTest()
{
    #define LOOP_COUNT 100000
    PropertyConfigurator::doConfigure(LOG4CPLUS_TEXT("log4cplus.properties"));
    Logger root = Logger::getRoot();
    Logger logger = Logger::getInstance(LOG4CPLUS_TEXT("testlogger"));
    
    LOG4CPLUS_WARN(Logger::getRoot (), "Starting test loop....");
    Time start = Time::gettimeofday();
    tstring msg(LOG4CPLUS_TEXT("This is a WARNING..."));
    
    int i = 0;
    for (i = 0; i < LOOP_COUNT; ++i)
    {
        LOG4CPLUS_WARN(logger, msg);
    }
    
    Time end = Time::gettimeofday();
    Time diff = end - start;
    
    LOG4CPLUS_WARN(LOG4CPLUS_TEXT("root"), "Logging " << LOOP_COUNT << " took: " << diff);
    LOG4CPLUS_WARN(root, "Logging average: " << (diff / LOOP_COUNT));
}


#log4cplus.properties配置文件
log4cplus.rootLogger=TRACE, STDOUT 
log4cplus.logger.testlogger=TRACE, TEST
log4cplus.additivity.testlogger=FALSE


log4cplus.appender.STDOUT=log4cplus::ConsoleAppender
log4cplus.appender.STDOUT.layout=log4cplus::PatternLayout
log4cplus.appender.STDOUT.layout.ConversionPattern=%d{%m/%d/%y %H:%M:%S} [%t] %-5p %c - %m %n


log4cplus.appender.TEST=log4cplus::FileAppender
log4cplus.appender.TEST.File=test_output.log
log4cplus.appender.TEST.layout=log4cplus::PatternLayout
log4cplus.appender.TEST.layout.ConversionPattern=%d{%y-%m-%d %H:%M:%S,%q} [%t] %-5p %c <%x> - %m%n


// 程序输出
03/28/12 01:58:35 [3078437120] WARN  root - Starting test loop.... 
03/28/12 01:58:38 [3078437120] WARN  root - Logging 100000 took: 2sec 459840usec
03/28/12 01:58:38 [3078437120] WARN  root - Logging average: 0sec 24usec -- 向文件中打印每条日志平均需要24微妙


test_output.log日志文件的内容:
12-03-28 01:57:29,677 [3078301952] WARN  testlogger <> - This is a WARNING...


三、log4cplus库(三)(将日志写入到文件) 

一.如何将日志写入到文件
将log信息记录到文件应该说是日志系统的一个基本功能,log4cplus在此基础上,提供了更多的功能,
可以按照你预先设定的大小来决定是否转储,当超过该大小,后续log信息会另存到新文件中,
依次类推;或者按照日期来决定是否转储。log4cplus提供了三个类用于文件操作,它们是:
FileAppender类、RollingFileAppender类、DailyRollingFileAppender类

二.FileAppender类
实现了基本的文件操作功能,构造函数如下:
FileAppender(const log4cplus::tstring& filename,                    
             LOG4CPLUS_OPEN_MODE_TYPE mode = LOG4CPLUS_FSTREAM_NAMESPACE::ios::trunc,                    
             bool immediateFlush = true);                    
     
   filename: 文件名
   mode :    文件类型,可选择的文件类型包括app、ate、binary、in、out、trunc,
                 因为实际上只是对stl的一个简单包装,缺省是trunc,表示将先前文件删除。
   immediateFlush :缓冲刷新标志,如果为true表示每向文件写一条记录就刷新一次缓存,
                    否则直到FileAppender被关闭或文件缓存已满才更新文件,一般是要设置true的,
                    比如你往文件写的过程中出现了错误(如程序非正常退出),即使文件没有正常关闭也可以保证程序终
                    止时刻之前的所有记录都会被正常保存。

三. RollingFileAppender类
构造函数如下:
log4cplus::RollingFileAppender::RollingFileAppender(
           const log4cplus::tstring& filename,                                                   
           long maxFileSize,                                                   
           int maxBackupIndex,                                                   
           bool immediateFlush)
    
      filename                : 文件名
      maxFileSize          : 文件的最大尺寸
      maxBackupIndex : 最大记录文件数
      immediateFlush    : 缓冲刷新标志                                                   

RollingFileAppender类可以根据你预先设定的大小来决定是否转储,当超过该大小,后续log信息会另存到新文件中,
除了定义每个记录文件的大小之外,你还要确定在RollingFileAppender类对象构造时最多需要多少个这样的记录文件
(maxBackupIndex+1),当存储的文件数目超过maxBackupIndex+1时,会删除最早生成的文件,保证整个文件数目等于
maxBackupIndex+1。然后继续记录,比如以下代码片段:


            
  #define LOOP_COUNT 200000        
 SharedAppenderPtr _append( new RollingFileAppender("Test.log", 5*1024, 5));    
 _append->setName("file test");    
 _append->setLayout( std::auto_ptr( new TTCCLayout()) );    
 Logger::getRoot().addAppender(_append);    
 Logger root = Logger::getRoot();    
 Logger test = Logger::getInstance("test");    
 Logger subTest = Logger::getInstance("test.subtest");    
  for( int i=0; i<1000;i++)     {        
            NDCContextCreator _context("loop");        
            LOG4CPLUS_DEBUG(subTest, "Entering loop #" << i)   
   }
        
         

    运行结果:运行后会产生6个输出文件,Test.log、Test.log.1、Test.log.2、Test.log.3、Test.log.4、Test.log.5
                       其中Test.log存放着最新写入的信息,而最后一个文件中并不包含第一个写入信息,说明已经被不断更新了。
                       需要指出的是,这里除了Test.log之外,每个文件的大小都是200K,而不是我们想像中的5K,
                       这是因为log4cplus中隐含定义了文件的最小尺寸是200K,只有大于200K的设置才生效,<= 200k的设置都会被认为是200K.

四. DailyRollingFileAppender类
构造函数如下:
 DailyRollingFileAppender::DailyRollingFileAppender(const log4cplus::tstring& filename,
                                                    DailyRollingFileSchedule schedule,                                                  
                                                    bool immediateFlush,                                                  
                                                    int maxBackupIndex)                                                  
       filename       : 文件名
       schedule       : 存储频度
       immediateFlush : 缓冲刷新标志
       maxBackupIndex : 最大记录文件数

DailyRollingFileAppender类可以根据你预先设定的频度来决定是否转储,当超过该频度,
后续log信息会另存到新文件中,这里的频度包括:
MONTHLY(每月)、WEEKLY(每周)、DAILY(每日)、TWICE_DAILY(每两天)、HOURLY(每时)、MINUTELY(每分)。
maxBackupIndex的含义同上所述,比如以下代码片段:


            
 SharedAppenderPtr _append( new DailyRollingFileAppender("Test.log", MINUTELY,  true, 5));    
 _append->setName("file test");    
 _append->setLayout( std::auto_ptr( new TTCCLayout()) );    
 Logger::getRoot().addAppender(_append);    
 Logger root = Logger::getRoot();    
 Logger test = Logger::getInstance("test");    
 Logger subTest = Logger::getInstance("test.subtest");    
  for( int i=0; i<100;i++)     {        
             NDCContextCreator _context("loop");        
             LOG4CPLUS_DEBUG(subTest, "Entering loop #" << i)    
 }
       
 
    

  运行结果:运行后会以分钟为单位,分别生成名为
  Test.log.2004-10-17-03-03、Test.log.2004-10-17-03-04和Test.log.2004-10-17-03-05这样的文件。
  需要指出的是,刚看到按照频度(如HOURLY、MINUTELY)转储这样的概念,以为log4cplus提供了内部定
  时器,感觉很奇怪,因为日志系统不应该主动记录,而loging事件总是应该被动触发的啊。仔细看了源
  代码后才知道这里的"频度"并不是你写入文件的速度,其实是否转储的标准并不依赖你写入文件的速度,
  而是依赖于写入的那一时刻是否满足了频度条件,即是否超过了以分钟、小时、周、月为单位的时间刻度,
  如果超过了就另存。本部分详细介绍log信息的几种文件操作方式,下面将重点介绍一下如何有选择地控
  制log信息的输出。








你可能感兴趣的:(C/C++)