log4cpp库的使用
log4cpp的使用
log4cpp开发文档:Main Page (sourceforge.net)
安装
Log4cpp的主页为:Log library for C++ download | SourceForge.net
下载log4cpp(如log4cpp-1.1.1.tar.gz)
先将log4cpp-1.1.1.tar.gz拖入用户主目录(~)
依次执行下面的步骤
$ cd ~/log4cpp/ $ ./configure $ make $ make check $ sudo make install //这里已经安装成功.
默认lib库路径是 : /usr/local/lib/

默认头文件的位置: /usr/local/include/log4cpp

make出现报错 ISO C++17 does not allow dynamic exception specifications

解决方法:
- 安装低版本的g++及gcc(如g++-9及gcc-9)
sudo apt-get install gcc-9
sudo apt-get install g++-9
- 设置默认编辑器
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 50 --slave /usr/bin/g++ g++ /usr/bin/g++-9
log4cpp基本结构
log4cpp从大的结构上分为,。除此以外还提供了一些帮助类,简化我们对日志的代码设置。
日志类别(Category)
Category,日志输出主体类,提供了输出各种日志级别的方法,并且能够设置类别优先级,低于此优先级的类都不再输出,,其他分类都是root分类的子分类,有点类似于java的单继承结构,子分类的输出同时会输出到父分类中。
// 根分类root
log4cpp::Category& root = log4cpp::Category::getRoot();
// 子分类subCat1
log4cpp::Category& sub1 = log4cpp::Category::getInstance("sub1"); //用于%c时输出Category的类别“sub1”
root.error("test root error");
root.warn("test root error");
root.info("test root error");
sub1.fatal("test sub1 error");
sub1.notice("test sub1 error");
sub1.debug("test sub1 error");
日志追加器(Appender)
Appender,主要是用来确定日志输出行为,例如。类Appender是由Category来加载的,,一个Appender也可以被多个Category加载,但是有些Appender类没有设置互斥,日志的多线程支持主要是在Category里做的处理,因此不要使用一个Appender加载到多个Category中。
// log4cpp::OstreamAppender:输出到输出流中(输出到控制台上)
// log4cpp::FileAppender:输出到文件中
// log4cpp::RollingFileAppender:输出到文件中并且能够设置文件最大超过多少时生成新文件
// log4cpp::DailyRollingFileAppender:每天生成一个新文件
// log4cpp::NTEventLogAppender:将日志输出到Windows事件日志中去
// log4cpp::StringQueueAppender // 内存队列
// ...
log4cpp::Appender *appender = new log4cpp::FileAppender("test", "D:/log4cppTest/log4cppTest.log");
root.addAppender(appender);
StringQueueAppender 内存队列-多线程调试
“”。因为printf导致IO中断,会使得本线程挂起,其花费的时间比一条普通指令多数千倍,若多个线程同时运行,则严重干扰了线程间的运行方式。所以。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 namespacestd;
int main(int argc,char* argv[])
{
log4cpp::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();
return 0;
}
日志布局(Layout)
Layout,设置日志输出风格,有BasicLayout、SimpleLayout、PatternLayout,其中BasicLayout,SimpleLayout主要是提供的成型的简单日志风格,实际中基本不会使用,主要是使用PatternLayout对日志做格式输出,Layout是加载到Appender中去的。
// %% 输出一个%
// %c 输出Categroy名称
// %d 输出日期
// %m 输出消息
// %n 输出换行
// %p 输出Priority
// %d{%Y-%m-%d %H:%M:%S:%l}对输出日期做定制,年月日时分秒毫秒
// %t 线程ID
log4cpp::PatternLayout *patternLayout = new log4cpp::PatternLayout();
patternLayout->setConversionPattern("%d{%Y-%m-%d %H:%M:%S.%l} %t [%p] %m %n");
appender->setLayout(patternLayout);
Category、Appender和Layout三者的关系
系统中可以有多个Category,它们都是继承自同一个根,每个Category负责记录自己的日志;每个Category可以添加多个Appender,每个Appender指定了一个日志的目的地,例如文件、字符流或者Windows日志,当Category记录一条日志时,该日志被写入所有附加到此Category的Appender;每个Append都包含一个Layout,该Layout定义了这个Appender上日志的格式。
日志级别(Priority)
。
系统中默认的优先级等级如下:
typedef enum
{
EMERG = 0, // emergency
FATAL = 0, // fatal
ALERT = 100, // alert
CRIT = 200, // critical
ERROR = 300, // error
WARN = 400, // wanning
NOTICE = 500, // notice
INFO = 600, // infomation
DEBUG = 700, // debug
NOTSET = 800
} PriorityLevel;
注意:取值越小,优先级越高。例如一个Category的优先级为101,则所有EMERG、FATAL、ALERT日志都可以记录下来,而其他则不能。
// 代码设置
root.setPriority(log4cpp::Priority::DEBUG);
appender->setThreshold()
帮助类
帮助类, log4cpp主要提供如下几个类来简化代码设置:SimpleConfigurator,BasicConfigurator,PropertyConfigurator,其中SimpleConfigurator和BasicConfigurator,主要是使用代码的方式,进行了一些简单的默认配置,而PropertyConfigurator,是通过配置文件的方式对日志进行配置,也是最常用的,下面就是配置文件的格式:
#文件名为logTest.Property
#配置root Category的Priority为DEBUG, Appender为rootAppender
log4cpp.rootCategory=DEBUG, rootAppender
#配置子Category sub1的Priority为DEBUG, Appender为A1
log4cpp.category.sub1=DEBUG, A1
#配置rootAppender为FileAppender
log4cpp.appender.rootAppender=FileAppender
#配置日志文件名为test1.log,存放在D盘根目录下
log4cpp.appender.rootAppender.fileName=D:/test1.log
#配置layout为PatternLayout
log4cpp.appender.rootAppender.layout=PatternLayout
#设置日志输出风格
log4cpp.appender.rootAppender.layout.ConversionPattern=%d{%Y-%m-%d %H:%M:%S.%l} %t [%p] %m %n
#配置A1为RollingFileAppender
log4cpp.appender.A1=RollingFileAppender
#配置日志文件名为test2.log,存放在D盘根目录下
log4cpp.appender.A1.fileName=D:/test2.log
#配置日志文件最大不能超过1M
log4cpp.appender.A1.maxFileSize=1024 * 1024
#日志文件最多存储3个文件,超过就会删除旧的
log4cpp.appender.A1.maxBackupIndex=2
#设置日志风格
log4cpp.appender.A1.layout=PatternLayout
log4cpp.appender.A1.layout.ConversionPattern=%d{%Y-%m-%d %H:%M:%S.%l} %t [%p] %m %n
// 代码设置配置文件
log4cpp::PropertyConfigurator::configure(logTest.Property);
;;; 注意
代码使用
windows环境下
获取到源代码后,找到源码目录中的msvc10目录,使用VS2010打开msvc10.sln解决方案文件,编译源码获得log4cpp.DLL和log4cpp.lib;在源码目录中有include文件夹,将include整个拷贝到我们的工程目录下,将include添加到工程属性中的附加包含目录中(QT工程则添加到INCLUDEPATH中去),这时就能够正常使用了。
linux环境下
编译使用log4cpp库的CPP文件时,要加上库文件,才能顺利的编译通过,如下示例
$ g++ log4test.cpp -llog4cpp -lpthread
运行时,如若提示缺少log4cpp库文件,表示,需要进行以下设置
- 以管理员身份登录终端,然后执行以下操作:
$ sudo vim /etc/ld.so.conf
- 在打开的文件末尾另起一行添加动态库log4cpp的路径(这里是/usr/local/lib),然后保存退出
- 执行命令ldconfig使设置生效即可。
$ sudo ldconfig //更新库文件的缓存信息
测试用例
#include <log4cpp/BasicLayout.hh>
#include <log4cpp/SimpleLayout.hh>
#include <log4cpp/PatternLayout.hh>
#include <log4cpp/OstreamAppender.hh>
#include <log4cpp/FileAppender.hh>
#include <log4cpp/RollingFileAppender.hh>
#include <log4cpp/Category.hh>
#include <log4cpp/Priority.hh>
#include <iostream>
using std::cout;
using std::endl;
using namespace log4cpp;//一次性将log4cpp中的实体全部引出来
void test()
{
//日志的格式
/* BasicLayout *pbl = new BasicLayout(); */
SimpleLayout *psl = new SimpleLayout();
//日志的目的地
/* OstreamAppender *pos = new OstreamAppender("OstreamAppender123", &cout); */
/* pos->setLayout(pbl); */
OstreamAppender *pos = new OstreamAppender("OstreamAppender123", &cout);
pos->setLayout(psl);
//日志种类(日志记录器)
Category &root = Category::getRoot();
root.addAppender(pos);
root.setPriority(Priority::ERROR);//过滤器
root.fatal("The log is fatal");
root.alert("The log is alter");
root.crit("The log is crit");
root.error("The log is error");
root.warn("The log is warn");
//回收资源
Category::shutdown();
}
void test2()
{
//日志的格式
PatternLayout *ppl1 = new PatternLayout();
ppl1->setConversionPattern("%d %c [%p] %m %n");
PatternLayout *ppl2 = new PatternLayout();
ppl2->setConversionPattern("%d %c [%p] %m %n");
//日志的目的地
OstreamAppender *pos = new OstreamAppender("OstreamAppender123", &cout);
pos->setLayout(ppl1);
RollingFileAppender *ppfa =
new RollingFileAppender("RollingFileAppender123", "test.log",
5 * 1024, 3);
ppfa->setLayout(ppl2);
//日志种类(日志记录器)
Category &root = Category::getRoot();
root.addAppender(pos);
root.addAppender(ppfa);
root.setPriority(Priority::ERROR);//过滤器
size_t idx = 0;
while(idx < 100)
{
root.fatal("The log is fatal");
root.alert("The log is alter");
root.crit("The log is crit");
root.error("The log is error");
root.warn("The log is warn");
++idx;
}
//回收资源
Category::shutdown();
}
void test3()
{
//日志的格式
PatternLayout *ppl1 = new PatternLayout();
ppl1->setConversionPattern("%d %c [%p] %m %n");
PatternLayout *ppl2 = new PatternLayout();
ppl2->setConversionPattern("%d %c [%p] %m %n");
//日志的目的地
OstreamAppender *pos = new OstreamAppender("OstreamAppender123", &cout);
pos->setLayout(ppl1);
FileAppender *pfl = new FileAppender("FileAppender1234", "wd.log");
pfl->setLayout(ppl2);
//日志种类(日志记录器)
Category &root = Category::getRoot().getInstance("MyCat");
root.addAppender(pos);
root.addAppender(pfl);
root.setPriority(Priority::ERROR);//过滤器
root.fatal("The log is fatal");
root.alert("The log is alter");
root.crit("The log is crit");
root.error("The log is error");
root.warn("The log is warn");
//回收资源
Category::shutdown();
}
int main(int argc, char **argv)
{
test2();
return 0;
}
封装成日志类(单例模式)
线程安全、自动释放、回滚日志、同时存在文件和屏幕打印
#include <log4cpp/Category.hh>
#include <log4cpp/RollingFileAppender.hh>
#include <log4cpp/OstreamAppender.hh>
#include <log4cpp/PatternLayout.hh>
#include <log4cpp/Priority.hh>
#include <iostream>
#include <string>
using log4cpp::Category;
using log4cpp::RollingFileAppender;
using log4cpp::OstreamAppender;
using log4cpp::PatternLayout;
using log4cpp::Priority;
using std::cout;
using std::endl;
using std::string;
class Logger
{
public:
static Logger *getInstance(const char* fileName = "myLog.txt");
static void destroy();
void warn(const char *msg);
void warn(const char * msg, const char *file, const char *func, int line);
void error(const char *msg);
void error(const char * msg, const char *file, const char *func, int line);
void debug(const char *msg);
void debug(const char * msg, const char *file, const char *func, int line);
void info(const char *msg);
void info(const char * msg, const char *file, const char *func, int line);
private:
Logger(string fileName);
~Logger();
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
private:
PatternLayout *_ptnLayout1;
PatternLayout *_ptnLayout2;
OstreamAppender *_osAppender;
RollingFileAppender *_rfAppender;
Category &_mycategory;
};
#include "Logger.h"
Logger *Logger::getInstance(const char *fileName )
{
static Logger logger(fileName);
// 注册destroy函数,在进程结束的时候执行,从而自动回收单例
atexit(Logger::destroy);
return &logger;
}
void Logger::destroy()
{
Category::shutdown();
}
Logger::Logger(string fileName)
: _ptnLayout1(new PatternLayout())
, _ptnLayout2(new PatternLayout())
, _osAppender(new OstreamAppender("osAppender", &cout))
, _rfAppender(new RollingFileAppender("fAppender", fileName, 5 * 1024, 3))
, _mycategory(Category::getRoot().getInstance("myCategory"))
{
cout << "Logger()" << endl;
_ptnLayout1->setConversionPattern("%d [%p] %m%n");
_ptnLayout2->setConversionPattern("%d [%p] %m%n");
_osAppender->setLayout(_ptnLayout1);
_rfAppender->setLayout(_ptnLayout2);
_mycategory.setPriority(Priority::DEBUG);
_mycategory.addAppender(_osAppender); // 设置同时存入文件和屏幕显示
_mycategory.addAppender(_rfAppender);
}
Logger::~Logger()
{
cout << "~Logger()" << endl;
}
void Logger::warn(const char *msg)
{
_mycategory.warn("%s", msg);
}
void Logger::warn(const char *msg, const char *file, const char *func, int line)
{
_mycategory.warn("%s@%s,%d : %s", func, file, line, msg);
}
void Logger::error(const char *msg)
{
_mycategory.error("%s", msg);
}
void Logger::error(const char *msg, const char *file, const char *func, int line)
{
_mycategory.error("%s@%s,%d : %s", func, file, line, msg);
}
void Logger::debug(const char *msg)
{
_mycategory.debug("%s", msg);
}
void Logger::debug(const char *msg, const char *file, const char *func, int line)
{
_mycategory.debug("%s@%s,%d : %s", func, file, line, msg);
}
void Logger::info(const char *msg)
{
_mycategory.info("%s", msg);
}
void Logger::info(const char *msg, const char *file, const char *func, int line)
{
_mycategory.info("%s@%s,%d : %s", func, file, line, msg);
}
#include "Logger.h"
int main()
{
Logger * log = Logger::getInstance();
log->warn("This is warn msg!", __FILE__, __FUNCTION__, __LINE__);
log->error("This is error msg!", __FILE__, __FUNCTION__, __LINE__);
log->debug("This is debug msg!", __FILE__, __FUNCTION__, __LINE__);
log->info("This is info msg!", __FILE__, __FUNCTION__, __LINE__);
log->warn("This is warn msg!");
log->error("This is error msg!");
log->debug("This is debug msg!");
log->info("This is info msg!");
//Logger::destory();
return 0;
}
$./a.out
Logger()
2024-04-11 23:03:02,056 [WARN] main@test.cc,6 : This is warn msg!
2024-04-11 23:03:02,057 [ERROR] main@test.cc,7 : This is error msg!
2024-04-11 23:03:02,057 [DEBUG] main@test.cc,8 : This is debug msg!
2024-04-11 23:03:02,057 [INFO] main@test.cc,9 : This is info msg!
2024-04-11 23:03:02,058 [WARN] This is warn msg!
2024-04-11 23:03:02,058 [ERROR] This is error msg!
2024-04-11 23:03:02,059 [DEBUG] This is debug msg!
2024-04-11 23:03:02,059 [INFO] This is info msg!
~Logger()