log4cpp

log4cpp

log4整体架构

使用

使用上的话参照整体架构进行理解,首先就是Category日志种类,然后不同种类可以绑定多种日志输出方式,然后这些日志输出方式可以指定需要的输出格式进行输出。

  • 一个程序可以创建多套root,参考categoryTest.cpp

log4cpp日志格式化

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

性能分析

同步日志方法

FileAppender

FileAppender这种日志记录方式是对每条日志直接调用write进行写入,因此对于单条日志数据量小的情况是效率较低的。

RollingFileAppender – RollingFileAppender.cpp

RollingFileAppender这种日志记录的方式,他是在FileAppender::_append()之后,会使用lseek系统调用获取当前文件大小,如果超过了文件限制,就进行rollOver()操作;
每次的leek系统调用会导致,他会比FileAppender效率更低。
改进方案 – 避免大批量的lseek:
可以增加一个当前文件大小的字段,每次更新日志都更新这个字段,从而避免lseek的重复性调用。

改进方案:
FileAppender

异步日志方案:

  1. 针对这个问题一种解决的思路就是对日志进行缓冲,减少write系统调用,从而提升效率,但是缓冲的标准有问题,就是如果一段时间都没有触发缓冲条件,那么已有的日志就会一直不进行写入。
  2. 这样的话就需要加入一个定时器方案,或者通过一个日志管理线程去管理。
RollingFileAppender

避免大批量的lseek:
可以增加一个当前文件大小的字段,每次更新日志都更新这个字段,从而避免lseek的重复性调用。

StringQueueAppender模式

这种模式就是第5个例子说用的模式,也就是异步日志方案,这种方式就是先将日志信息通过日志接口进行输出,不过他是输出在了StringQueueAppender缓冲区,然后在需要的时候(周期性、或者达到一定数量)就将缓冲区中的数据刷到文件中。

生产者消费者模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fZNM8Of0-1652873142450)(./images/product_consume.png “log4cpp日志格式化”)]
上面的StringQueueAppender模式本质上就是个生产消费者模式,那么对于这个模型,有下面几个问题:

  • 该如何唤醒日志落盘线程?
    生产者消费者模式一般搭配mutex + notify的机制会更有效率;然后考虑到超时可以使用pthread_cond_timedwait();
  • 每次发notify对性能有没有影响?
    每次都发有影响,因此需要进行缓冲,缓冲多条再进行批量写入磁盘。
  • 日志写入磁盘时是批量写入还是单条写入?
    批量,单条没意义,还是write的系统调用没变;
  • 日志批量写入磁盘的时候会对日志记录线程有没有影响?
    生产者消费者模型必定存在锁,那么在进行大批量磁盘写入就会引起日志写入阻塞,因此需要使用双缓冲机制(两个缓冲区,一个在进行磁盘写入的时候,另一个继续负责日志写入功能)。
双缓冲机制

两种方式触发缓冲日志写入磁盘:

  • 写满一个缓冲区进行条件变量的notify;
  • 通过pthread_cond_timedwait()去限制最大超时时间,避免日志写入磁盘异步太久。一般就是1秒,具体看场景。
  • 如果日志写高峰,那么可以以缓冲区队列的形式去不断缓冲。甚至可以使用两个队列进行缓冲。

categoryTest测试代码:

#include "log4cpp/Category.hh"
#include "log4cpp/FileAppender.hh"
#include "log4cpp/OstreamAppender.hh"
#include "log4cpp/BasicLayout.hh"
#include "log4cpp/SimpleLayout.hh"
#include <log4cpp/PatternLayout.hh>

int main()
{
    log4cpp::Layout *layout = new log4cpp::SimpleLayout();                                           // 就简单的格式
    log4cpp::Appender *osappender1 = new log4cpp::OstreamAppender("OstreamAppender", &std::cout);    // 纯粹的享受,直接控制端打印
    osappender1->setLayout(layout);

    log4cpp::Category &log_test1 = log4cpp::Category::getInstance("RootName.test1");
    log_test1.setAdditivity(true);
    log_test1.addAppender(osappender1);
    log_test1.setPriority(log4cpp::Priority::DEBUG);         // 设置category的优先级,低于此优先级的日志不被记录

    log_test1.error("Program info which cannot be wirten, welsey = %d", 100);

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

    log4cpp::Category& root = log4cpp::Category::getRoot().getInstance("RootName");
    root.addAppender(fileAppender);
    root.setPriority(log4cpp::Priority::DEBUG);

    root.info("hello");


    //*********************************************************************************

    log4cpp::Layout *layout2 = new log4cpp::SimpleLayout();                                          // 就简单的格式
    log4cpp::Appender *osappender2 = new log4cpp::OstreamAppender("OstreamAppender", &std::cout);    // 纯粹的享受,直接控制端打印
    osappender2->setLayout(layout2);

    log4cpp::Category &log_test1_2 = log4cpp::Category::getInstance("RootName.test1.test2");
    log_test1_2.setAdditivity(true);
    log_test1_2.setAppender(osappender2);
    log_test1_2.setPriority(log4cpp::Priority::DEBUG);

    log_test1_2.warn("Program info which cannot be wirten, welsey = %d", 100);

    
    //*********************************************************************************
    log4cpp::PatternLayout* pLayout3 = new log4cpp::PatternLayout();
    pLayout3->setConversionPattern("%d: %p %c %x: %m%n");    
    log4cpp::Appender* fileAppender2 = new log4cpp::FileAppender("rootLog","rootLog2.log", true);
    fileAppender2->setLayout(pLayout3);

    log4cpp::Category& root2 = log4cpp::Category::getRoot().getInstance("RootName2");
    root2.addAppender(fileAppender2);
    root2.setPriority(log4cpp::Priority::DEBUG);

    root2.info("hello");

    
    // clean up and flush all appenders
    log4cpp::Category::shutdown();
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值