C++编程之自定义日志类 ——log4cpp使用详解

  • log4cpp简介与安装
    • log4cpp安装
    • log4cpp简单介绍
  • layout布局——日志输出格式
    • log4cpp::BasicLayout
    • log4cpp::PatternLayout
  • appender
    • log4cpp::FileAppender
    • log4cpp::RollingFileAppender
    • log4cpp::OstreamAppender
    • log4cpp::StringQueueAppender
  • Category
  • 自定义日志类

log4cpp简介与安装

log4cpp是一个开源的C++日志管理库,可以通过它来记录程序运行过程中产生的各种信息。也可以进行再包装实现个人自定义的日志类。

log4cpp安装

  1. 下载:
    wget https://nchc.dl.sourceforge.net/project/log4cpp/log4cpp-1.1.x%20%28new%29/log4cpp-1.1/log4cpp-1.1.3.tar.gz
  2. 解包
    tar zxvf log4cpp-1.1.3.tar.gz
  3. cd log4cpp
  4. ./configure --with-pthreads
  5. ./configure
  6. make
  7. make install
  8. 添加环境变量
    vim /etc/profile.d/log4cpp.sh
    在文件中添加:
    LD_LIBRARY_PATH=:$LD_LIBRARY_PATH:/usr/local/lib export LD_LIBRARY_PATH
    修改文件权限
    chmod a+x /etc/profile.d/log4cpp.sh
  9. ldconfig -v

安装好后, 在编译源码文件时要加上-llog4cpp -lpthread来链接动态库

log4cpp简单介绍

log4cpp库中主要分为三大类:Category(种类)、Appender(附加目的地)和Layout(布局)。

  • category类是日志记录的主要执行类,它负责写日志
  • appender类用来指明目的地,即日志要写到什么地方去
  • layout类指明日志输出的格式

应用时的大致流程:

  1. 定义一个layout类对象,确定输出日志信息的格式
  2. 定义一个appender类对象,确定日志输出到什么地方,然后把layout对象用setlayout方法绑定一下。
  3. 定义一个catergory对象,与appender类对象绑定
  4. 调用catergory对象进行写日志

简单示例

#include <iostream>
#include <log4cpp/Category.hh>
#include <log4cpp/FileAppender.hh>
#include <log4cpp/BasicLayout.hh>
#include <log4cpp/BasicLayout.hh>int main()
{//1. 初始化一个layout对象log4cpp::Layout* layout =  new log4cpp::BasicLayout();// 2. 初始化一个appender 对象log4cpp::Appender* appender = new log4cpp::FileAppender("FileAppender","./test_log4cpp1.log");// 3. 把layout对象附着在appender对象上appender->setLayout(layout);// 4. 实例化一个category对象log4cpp::Category& warn_log = log4cpp::Category::getInstance("mywarn");// 5. 把appender对象附到category上warn_log.setAppender(appender);// 6. 设置category的优先级,低于此优先级的日志不被记录warn_log.setPriority(log4cpp::Priority::WARN);// 记录一些日志warn_log.info("Program info which cannot be wirten");warn_log.debug("This debug message will fail to write");warn_log.alert("Alert info");// 其他记录日志方式warn_log.log(log4cpp::Priority::WARN, "This will be a logged warning");log4cpp::Priority::PriorityLevel priority;bool this_is_critical = true;if(this_is_critical){priority = log4cpp::Priority::CRIT;}else{priority = log4cpp::Priority::DEBUG;}warn_log.log(priority,"Importance depends on context");// 清理所有资源log4cpp::Category::shutdown();return 0;
}

可以看到整套流程下来还是有点复杂的,可以在后续包装成一个自定义的日志类进行日志记录。

layout布局——日志输出格式

layout对象规定了日志输出的内容格式,创建后需要和appender对象绑定生效。
需要注意的是,一个布局对象只能绑定一个appender对象。
比较常用的布局有两种:log4cpp::BasicLayoutlog4cpp::PatternLayout

log4cpp::BasicLayout

log4cpp::BasicLayout是最简单的布局,输出时间戳,消息优先级和消息内容。
示例代码如下:

#include<iostream>
#include"log4cpp/Category.hh"
#include"log4cpp/OstreamAppender.hh"
#include"log4cpp/BasicLayout.hh"
#include"log4cpp/Priority.hh"
using namespace std;int main(int argc,char* argv[])
{log4cpp::OstreamAppender* osAppender=newlog4cpp::OstreamAppender("osAppender",&cout);
osAppender->setLayout(newlog4cpp::BasicLayout());
log4cpp::Category& root =log4cpp::Category::getRoot();root.addAppender(osAppender);
root.setPriority(log4cpp::Priority::DEBUG);
root.error("Hello log4cpp in aError Message!");
root.warn("Hello log4cpp in aWarning Message!");log4cpp::Category::shutdown();    return 0;
}

输出的日志格式如下:

1248337987 ERROR  : Hello log4cppin a Error Message!1248337987 WARN  : Hello log4cppin a Warning Message!

log4cpp::PatternLayout

log4cpp::PatternLayout布局支持通过类似printf函数的格式控制符的方式自定义输出的信息和内容。通过使用以下函数进行设置:

log4cpp::PatternLayout::setConversionPattern (conststd::string& conversionPattern) ;

该函数接收的参数为格式控制字符串,其中符号含义如下:

 %c: 记录日志的category对象名称;%d: 日期;日期可以进一步的设置格式,用花括号包围,例如%d{%H:%M:%S,%l} 或者 %d{%d %m %Y%H:%M:%S,%l}。如果不设置具体日期格式,则如下默认格式被使用“Wed Jan 02 02:03:55 1980”。日期的格式符号与ANSI C函数strftime中的一致。但增加了一个格式符号%l,表示毫秒,占三个十进制位。%m: 要输出的日志消息字符串;%n 换行符,会根据平台的不同而不同,但对于用户透明;%p 优先级,warn,debug,info等待;%r 自从layout被创建后的毫秒数;%R 从1970年1月1日0时开始到目前为止的秒数;%u 进程开始到目前为止的时钟周期数;%x NDC

代码示例:

MyLog::screenLayout = new log4cpp::PatternLayout();
screenLayout->setConversionPattern("%d{%Y/%m/%d,%H:%M:%S} -- %p %c: %m%n");

上述代码表示日志记录的信息依次是“日期(年月日时分秒)-- 优先级 catgory:消息 换行”

appender

appender对象指定日志输出到什么地方去,创建后需要和category对象绑定才能生效。
一个apender只能和一个category对象绑定,但是一个category对象可以有多个appnder,可以输出到多个位置。

常用的appender类如下:

log4cpp::FileAppender                      // 输出到文件log4cpp::RollingFileAppender         // 输出到回卷文件,即当文件到达某个大小后回卷log4cpp::OstreamAppender           // 输出到一个ostream类log4cpp::StringQueueAppender             // 输出到内存队列

log4cpp::FileAppender

构造函数如下:

   FileAppender(conststd::string& name, conststd::string& fileName, bool append = true, mode_tmode = 00644);

一般仅使用前两个参数,即“名称”( FileAppender对象的名称)和“日志文件名”(要写入日志的文件名)。第三个参数指示是否在日志文件写满后继续记入日志,还是清空原日志文件再记录。第四个参数说明文件的打开方式。
  FileAppender对象创建完毕后,调用成员函数setLayout来绑定一个布局对象。

log4cpp::RollingFileAppender

RollingFileAppender对象会在文件长度到达指定值时循环记录日志,文件长度不会超过指定值。
构造函数如下:

RollingFileAppender(const std::string&name,  const std::string&fileName,size_tmaxFileSize =10*1024*1024,  unsigned intmaxBackupIndex = 1,boolappend = true,  mode_t mode =00644);

该函数与FileAppender的创建函数很类似,但是多了两个参数:maxFileSize指出了回滚文件的最大值;maxBackupIndex指出了回滚文件所用的备份文件的最大个数。所谓备份文件,是用来保存回滚文件中因为空间不足未能记录的日志,备份文件的大小仅比回滚文件的最大值大1kb。

log4cpp::OstreamAppender

log4cpp::OstreamAppender 对象可以将日志信息输出到指定的流类中:
构造方法如下:
log4cpp::OstreamAppender* osAppender = newlog4cpp::OstreamAppender("osAppender", &cout);

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

log4cpp::StringQueueAppender

log4cpp::StringQueueAppender 可以将日志信息保存到内存队列中,在程序运行结束后再进行处理,主要用于记录多线程程序或者实时程序的日志,防止输出操作引起IO中断导致线程挂起,影响效率。
其构造函数如下:
log4cpp::OstreamAppender(const std::string& name);
可以通过成员函数getQueue()获取队列指针,从而访问内存中的日志信息队列。
队列的类型为std::queue<std::string> _queue;

Category

  • category是日志记录活动的主要承担者。负责接收信息并记录。

  • log4cpp中有一个总是可用并实例化好的Category,即根Category。使用log4cpp::Category::getRoot()可以得到根Category的引用。在大多数情况下,一个应用程序只需要一个日志种类(Category),但是有时也会用到多个Category,此时可以使用根Category的getInstance方法来得到子Category。

  • 注意category类的构造函数是私有成员寒素,因此只能 通过getInstance方法或getRoot方法获取对象的引用,而不能直接创建对象。

  • 通过category类的成员函数setPriority设置优先级敏感度,低于该优先级的日志信息将不被记录。
    log4cpp优先级一览,取值越小优先级越高:
    typedef enum {EMERG = 0,
    FATAL = 0,
    ALERT = 100,
    CRIT = 200,
    ERROR = 300,
    WARN = 400,
    NOTICE = 500,
    INFO = 600,
    DEBUG = 700,
    NOTSET = 800
    } PriorityLevel;

自定义日志类

将上述过程封装,即可得到自己的日志类

/*采用单例模式设计,包含两个category对象,一个负责输出到屏幕的信息,一个负责记录到日志的信息,通过设置优先级差别,可以实现所有信息都记录在日志中,遇到error及以上的信息时打印到屏幕上*/
class MyLog
{private:MyLog(bool b){outToScreen = b;}~MyLog(){}static MyLog * log;bool outToScreen;//是否输出日志信息到屏幕static std::string _screenInfo;//屏幕日志信息static std::string _logName;//文件日志名称static log4cpp::Category& logCat;static log4cpp::Category& coutCat;static log4cpp::FileAppender* logFile;//文件日志输入static log4cpp::OstreamAppender* logScreen;//屏幕日志输入static log4cpp::Priority::PriorityLevel logPri;//文件日志优先级static log4cpp::Priority::PriorityLevel coutPri;//屏幕日志优先级static log4cpp::PatternLayout* logLayout;//日志布局 static log4cpp::PatternLayout* screenLayout;//屏幕布局
public://获取日志函数,默认参数选择是否输出到屏幕static MyLog* getLog(bool toScreen = true,std::string coutName ="screenInfo",std::string logName = "log"){if(MyLog::log == NULL){MyLog::log = new MyLog(toScreen);MyLog::_logName = logName;MyLog::_screenInfo = coutName;logScreen = new log4cpp::OstreamAppender("logScreen",&std::cout);logFile = new log4cpp::FileAppender("logFile",MyLog::_logName);//设置布局MyLog::logLayout = new log4cpp::PatternLayout();MyLog::screenLayout = new log4cpp::PatternLayout();logLayout->setConversionPattern("%d{%Y/%m/%d,%H:%M:%S} -- [%p] %c: %m%n");screenLayout->setConversionPattern("%d{%Y/%m/%d %H:%M:%S} -- [%p] %c: %m%n");MyLog::logScreen->setLayout(screenLayout);MyLog::logFile->setLayout(logLayout);//追加到目录MyLog::logCat.addAppender(MyLog::logFile);MyLog::coutCat.addAppender(MyLog::logScreen);//设置优先级MyLog::logCat.setPriority(MyLog::logPri);MyLog::coutCat.setPriority(MyLog::coutPri);}MyLog::log->outToScreen = toScreen;return MyLog::log;}//销毁日志对象static void destoryLog(){log4cpp::Category::shutdown();delete MyLog::log;}//设置日志记录优先级static void setPri(log4cpp::Priority::PriorityLevel coutLevel,log4cpp::Priority::PriorityLevel logLevel){MyLog::logPri = logLevel;MyLog::coutPri = coutLevel;MyLog::logCat.setPriority(MyLog::logPri);MyLog::coutCat.setPriority(MyLog::coutPri);}//记录日志,调用参数__FILE__, __LINE__ ,__FUNCTION__void warn(const char * msg,const char *filename = __FILE__,int line = __LINE__,const char *function = "warn"){char info[4096] = {0};sprintf(info,"\nIn file %s,line %d,function %s:%s",filename,line,function,msg);if(this->outToScreen){logCat.warn(info);coutCat.warn(info);}else{logCat.warn(info);}}void error(const char * msg,const char *filename = __FILE__,int line = __LINE__,const char *function = "error"){char info[4096] = {0};sprintf(info,"\nIn file %s,line %d,function %s:%s",filename,line,function,msg);if(this->outToScreen){logCat.error(info);coutCat.error(info);}else{logCat.error(info);}}void debug(const char * msg,const char *filename = __FILE__,int line = __LINE__,const char *function = "debug"){char info[4096] = {0};sprintf(info,"\nIn file %s,line %d,function %s:%s",filename,line,function,msg);if(this->outToScreen){logCat.debug(info);coutCat.debug(info);}else{logCat.debug(info);}}void info(const char * msg,const char *filename = __FILE__,int line = __LINE__,const char *function = "info"){char info[4096] = {0};sprintf(info,"\nIn file %s,line %d,function %s:%s",filename,line,function,msg);if(this->outToScreen){logCat.info(info);coutCat.info(info);}else{logCat.info(info);}}
};
MyLog* MyLog::log = NULL;
std::string MyLog::_screenInfo = "screenInfo";
std::string MyLog::_logName = "log";log4cpp::Category& root = log4cpp::Category::getRoot();
log4cpp::Category& MyLog::logCat = root.getInstance(MyLog::_logName);
log4cpp::Category& MyLog::coutCat = root.getInstance(MyLog::_screenInfo);log4cpp::Priority::PriorityLevel MyLog::coutPri = log4cpp::Priority::INFO;
log4cpp::Priority::PriorityLevel MyLog::logPri = log4cpp::Priority::NOTSET;log4cpp::PatternLayout* MyLog::logLayout  = NULL;
log4cpp::PatternLayout* MyLog::screenLayout  = NULL;log4cpp::FileAppender* MyLog::logFile = NULL;//文件日志输入
log4cpp::OstreamAppender* MyLog::logScreen = NULL;//屏幕日志输入//为避免每次调用都要填写参数__FILE__,__LINE__和__FUNCTION__,可以使用带参数的宏定义
#define  MyLogWARN(msg) MyLog::getLog()->warn(msg,__FILE__,__LINE__,__FUNCTION__);
#define  MyLogINFO(msg) MyLog::getLog()->info(msg,__FILE__,__LINE__,__FUNCTION__);
#define  MyLogERROR(msg) MyLog::getLog()->error(msg,__FILE__,__LINE__,__FUNCTION__);
#define  MyLogDEBUG(msg) MyLog::getLog()->debug(msg,__FILE__,__LINE__,__FUNCTION__);

C++编程之自定义日志类 ——log4cpp使用详解相关推荐

  1. python面向对象编程的三大特性_Python面向对象总结及类与正则表达式详解

    Python3 面向对象 -------------------------------------------------------------------------------- 一丶面向对象 ...

  2. 在python中使用关键字define定义函数_python自定义函数def的应用详解

    这里是三岁,来和大家唠唠自定义函数,这一个神奇的东西,带大家白话玩转自定义函数 自定义函数,编程里面的精髓! def 自定义函数的必要函数:def 使用方法:def 函数名(参数1,参数2,参数-): ...

  3. c++类的构造函数详解

    c++构造函数的知识在各种c++教材上已有介绍,不过初学者往往不太注意观察和总结其中各种构造函数的特点和用法,故在此我根据自己的c++编程经验总结了一下c++中各种构造函数的特点,并附上例子,希望对初 ...

  4. [转]c++类的构造函数详解

    c++构造函数的知识在各种c++教材上已有介绍,不过初学者往往不太注意观察和总结其中各种构造函数的特点和用法,故在此我根据自己的c++编程经验总结了一下c++中各种构造函数的特点,并附上例子,希望对初 ...

  5. python def函数报错详解_python自定义函数def的应用详解

    这篇文章主要介绍了python自定义函数def的应用详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 这里是三岁,来和大家唠唠 ...

  6. Java自动日志监控框架auto-log详解

    Java自动日志监控框架auto-log详解 1. 需求概述 2. auto-log简介 2.1 auto-log定义 2.2 auto-log目的 2.3 auto-log特性 2.4 注解说明 2 ...

  7. Nginx内置变量以及日志格式变量参数详解

    Nginx内置变量以及日志格式变量参数详解 $args #请求中的参数值 $query_string #同 $args $arg_NAME #GET请求中NAME的值 $is_args #如果请求中有 ...

  8. java 重启线程_java 可重启线程及线程池类的设计(详解)

    了解JAVA多线程编程的人都知道,要产生一个线程有两种方法,一是类直接继承Thread类并实现其run()方法:二是类实现Runnable接口并实现其run()方法,然后新建一个以该类为构造方法参数的 ...

  9. 全国计算机棋类竞赛,自主招生认可的12类主流竞赛详解

    自主招生认可的12类主流竞赛详解 2016年全国有90所自主招生院校,其中77所针对全国招生,13所针对本省招生.在如此多的自主招生院校中,大家最为感兴趣的就是,2017高校自主招生到底认可什么样的竞 ...

  10. (38)System Verilog类class复制详解

    (38)System Verilog类class复制详解 1.1 目录 1)目录 2)FPGA简介 3)System Verilog简介 4)System Verilog类class复制详解 5)结语 ...

最新文章

  1. Kafka实战-Flume到Kafka
  2. for 循环里调用ajax,for循环中ajax异步问题如何解决?
  3. crontab FAQ
  4. 【解决方案】QT读写文件
  5. 爬虫篇——代理IP爬取备用及存储
  6. 关于C++中的条件编译
  7. 八皇后问题初始思路python_Python 学习笔记(一)10行代码解决八皇后问题
  8. 《移动项目实践》实验报告——Android数据存储
  9. POM.xml红叉解决方法
  10. MYSQL基础(事务,触发器,函数,过程指令操作)
  11. linux内存管理:kmap、vmap、ioremap
  12. java httpclient 返回xml_通过httpClient通过post向接口发送xml数据,并处理返回的xml报文...
  13. 数据库是.frm,.myd,myi备份如何导入mysql (转)
  14. 2022-2027年中国办公设备租赁市场竞争态势及行业投资潜力预测报告
  15. Android SDK Platform Tools下载(包括adb 、 fastboot 和 systrace)
  16. matlab智能算法30个案例分析源码,MATLAB智能算法30个案例分析 源代码
  17. Swift 优化OC接口 NS_REFINED_FOR_SWIFT
  18. oracle library is not loaded解决方法
  19. Python 3.8.0百度网盘资源下载
  20. 微信开放平台绑定各移动应用、网站应用、公众号、小程序、第三方的个数

热门文章

  1. 如何通过安全的网络防御XcodeGhost?
  2. 猿编程python,python编程猿
  3. 将超星图书转成PDF文档
  4. 学习《21天学通java(第7版)》之错误收集
  5. 浅谈欧姆龙system studio和Cx-one软件编程上区别
  6. 在CF卡上实现EWF功能
  7. 在线解析下载微软官方商店离线安装包
  8. NOIP2017小凯的疑惑
  9. python获取快手无水印demo
  10. 宇宙最强下载器:IDM 俄罗斯大神版