12.4-在Qt中使用Log4Qt输出Log文件,看这一篇就足够了
一、为啥要使用第三方Log库,而不用平台自带的Log库
二、Log4j系列库的功能介绍与基本概念
三、Log4Qt库的基本介绍
四、将Log4qt组装成为一个单独模块
五、使用配置文件的方式配置Log4Qt
六、使用代码的方式配置Log4Qt
七、在Qt工程中引入Log4Qt库模块的方法
八、获取示例中的源代码
一、为啥要使用第三方Log库,而不用平台自带的Log库
首先要说明的是,在平时开发和调试中开发平台自带的“打印输出”已经足够了。但是在深入使用的场合则显得“心有余而力不足”了。
比如在如下几个使用场合:
记录软件崩溃log文件:
正式发布的软件,需要把使用过程中的报错信息写入到log文件中。这个“Log文件”主要记录软件的使用环境、操作动作、警告信息、报错信息等。在软件发生崩溃的时候,这些log文件对开发者定位bug并修改bug有非常大的帮助。是大型软件必不可少的功能。
记录软件使用详细过程
对于一些涉及数据安全的软件,需要对软件的操作步骤和过程做详细的log记录。从而避免操作人员随意或恶意修改数据却不留下任何证据。比如:医疗数据、金融数据、电力数据、化工数据等。同时为了log记录的安全,需要远程储存log数据。常用的方式是用“远程数据库”来记录log。
因此,在这些重量级的需求下,发展出来了各种个样的第三方Log库,而最为著名的就是Log4J这个开源库。
二、Log4j系列库的功能介绍与基本概念
Log4J相比于系统自带log的优点
支持多源输出:支持输出log到控制台、文件、套接口服务器等;
线程安全:可以安全的在多线程应用中使用;
输出格式可控:可以根据需求定制输出格式:xml格式、数据库表格式、(时间,日期,线程,级别)自由组合格式;
支持日志级别:可以用不同级别来记录log,比如:debug、info、warn、error。同时可以灵活限定输出等级;
配置简单:使用统一的配置文件配置即可实现定制化的log输出,配置文件可以随时修改动态生效;
多个输出源同时输出:比如终点输出和文件输出可以同时记录log;
多平台可用:现在Log4J被移植到多个平台上支持多种编程语言;
Log4j的组成部分
Log4j由三个组件组成,分别是:记录器(logger)、数据输出源(appender)、布局(layout)。开发者可以根具输出消息类型、等级、输出格式以及使用那种方式输出作出灵活的配置。这些都离不开这三个组件的支持。
记录器(logger):在代码中采集需要输出的消息。并且根具所配置的等级过滤输入消息
数据输出源(appender):log消息数据输出的目标地点。比如:终端、文件、远程socket
布局(layout):指定消息输出的格式。比如:xml格式、时间,日期,线程,级别)自由组合格式;
Log4j的输出等级
日志信息的优先级从高到低有FATAL、ERROR、WARN、INFO、DEBUG、TRACE、ALL,分别用来指定这条日志信息的重要程度。Log4j建议只使用四个级别,优先级从高到低分别是ERROR、WARN、INFO、DEBUG。Log4j只输出,级别设置等级以及比设置等级高的消息。比如设置等级为Info。则属于“FATAL、ERROR、WARN、INFO”等级的消息可以输出到log。而“DEBUG、TRACE”级别的消息则不会被输出。
常用的输出源(Appender)
org.apache.log4j.ConsoleAppender(控制台)
org.apache.log4j.FileAppender(文件)
org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)
org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)
org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
常用的布局(Layout)
org.apache.log4j.HTMLLayout(以HTML表格形式布局)
org.apache.log4j.PatternLayout(根据patten符号格式化输出数据,类似printf的格式化方式)
org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串)
org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等信息)
一些规则说明
一个Logger可以挂接多个Appender(日志信息同时转发到多个输出源)
一个Appender只能指定一个Layout进行格式化
RootLogger可以设置一个基本log输出等级限制
每个Appender可以单独设置自己的输出等级,但是受限于RootLogger的log输出等级限制
三、Log4Qt库的基本介绍
Log4Qt作为Log4j的一个分支扩展。使得在Qt应用程序中也可以使用到这个优秀的log库。在Apache-2.0协议下发布,这个开源库可以使用在开源软件和商业软件中。项目地址如下:
https://github.com/MEONMedical/Log4Qt
1
Log4qt在Log4j的基础增加的功能
//简单的包含时间“输出布局”
SimpleTimeLayout (“dd.MM.yyyy hh:mm [Thread] Level Logger Message”)
//多彩颜色终端输出(仅支持windows)
ColorConsoleAppender (render colorized message by escape sequence and put it to console)
//当记录输出消息的时候,发生一个qt平台的信号
SignalAppender (emit signal when log event happens)
//数据库“输出源”
DatabaseAppender (append log event into sql table)
//数据库“输出布局”
DatabaseLayout (put log event into sql table columns)
//Telnet“输出源”
Telnet appender (append log event to telnet clients)
//支持和qDebug()一样的消息拼接方式
LogStream (qDebug()-style log messages appending)
//主线程“输出源”,通过消息循环,以代理的方式记录数据log消息
MainThreadAppender (Proxy appender for sending log messages through event loop)
//xml的“输出布局”
XMLLayout to support apache chainsaw
//每天一个log文件的方式的输出源
DailyFileAppender which generates a logfile for each day (add current date formatted to the filename)
//二进制消息“记录器”
Binary logger
//windows下的调试终端“输出源”
Windows Debug Console Appender
使用Log4Qt库的基本需求
要求Qt的最低版本为5.12
如果要使用5.3及以下的qt版本,使用本开源库的1.4或1.4.x分支
如果要使用5.7及以下的qt版本,使用本开源库的1.5或1.5.x分支
使用本库需要启用C++11
最低编译器版本要求:MSVC14, GCC 4.8 或 CLANG 3.3
四、将Log4qt组装成为一个单独模块
这里并没有直接把log4qt和自己的工程融合在一起。而是设计为一个独立的模块。这样做的意义如下:
如果以后有需要,可以随时替换为其他log模块
由于是独立模块,公司中的其他项目再次快速使用
独立模块,方便单独维护
把log4qt组装成为一个跨平台log库模块的步骤如下:
编译各个平台下的log4qt动态库
下载好源码之后,使用你所需要的各个平台安装好的Qt Creater编译源码(release)。在编译输出目录可以看到生成的log4qt动态库文件。如下图所示:
在这里插入图片描述
整理动态库和所需要的头文件(形成一个log4qt模块)
a.新建一个log4qt文件夹,作为log4qt模块的文件夹。
b.在log4qt目录下创建一个bin目录,把之前生成的动态库文件放入。
c.在log4qt目录下创建一个include目录,把下载好的Log4qt的源码中的"Log4Qt-master/src/"下的log4qt目录复制的刚刚创建好的include目录。
d.在log4qt目录下创建一个helper目录,此目录用于存放配置log4qt的代码。此部分代码将在第五小节和第六小节详细说明
log4qt模块整理完成如下图所示:
编写log4qt模块的引入文件“log4qtlib.pri”
message(“log4qt_lib_been_attached”)
CONFIG += c++11
INCLUDEPATH += P W D / h e l p e r I N C L U D E P A T H + = PWD/helper INCLUDEPATH += PWD/helperINCLUDEPATH+=PWD/include
DEPENDPATH += $$PWD/include
SOURCES +=
KaTeX parse error: Undefined control sequence: \ at position 45: …_by_coding.cpp \̲ ̲ PWD/helper/log4qt_init_helper_by_config.cpp
HEADERS +=
KaTeX parse error: Undefined control sequence: \ at position 43: …er_by_coding.h \̲ ̲ PWD/helper/log4qt_init_helper_by_config.h
macx {
macx: LIBS += -LKaTeX parse error: Expected 'EOF', got '}' at position 21: …in/ -llog4qt.1 }̲ win32 { wi…PWD/bin/ -llog4qt
}
DISTFILES +=
$$PWD/log4qt.properties
至此,log模块就组装完成了。以上过程提到的相关目录和代码,都在第八节源码中找到大完整示例
五、使用配置文件的方式配置Log4Qt
Log4qt的功能强大,使用配置也非常简单。只需要编写简单的配置文件就可以完成个性化log输出的配置。在网上可以找到好多log4j的配置文件。这里要强调一点,虽然Log4qt是Log4j的分支,但是由于平台的不同,Log4qt不完全支持log4j的配置文件。
下面介绍在本log4qt模块中怎样使用配置文件配置log4qt。
注:可以在第八节的源码中获取本节完整代码。为了良好的阅读体验,这里仅列出核心代码。
在log4qt/helper目录下创建一个“log4qt_init_helper_by_config.h“文件,内容如下(简化代码):
#ifndef LOG4QT_HELPER_BY_CONFIG_H
#define LOG4QT_HELPER_BY_CONFIG_H
//启动log4qt库,使用配置文件的方式配置log4qt
extern void SetupLog4QtByConfigWithConfigFileAbsPath(QString config_file_abs_path);
extern void ShutDownLog4QtByConfig();//关闭log4qt库
#endif // LOG4QT_HELPER_H
在log4qt/helper目录下创建一个”log4qt_init_helper_by_config.cpp“文件。内容如下(简化代码):
#include “log4qt_init_helper_by_config.h”
void SetupLog4QtByConfigWithConfigFileAbsPath(QString config_file_abs_path)
{
if (QFile::exists(config_file_abs_path)) {
Log4Qt::PropertyConfigurator::configure(config_file_abs_path);
} else {
qDebug() << “Can’t find log4qt-config-file path:” << config_file_abs_path;
}
}
void ShutDownLog4QtByConfig()
{
auto logger = Log4Qt::Logger::rootLogger();
logger->removeAllAppenders();
logger->loggerRepository()->shutdown();
}
以上编写好了加载log4qt配置文件的代码,下面正式介绍配置文件的编写。配置文件如下,相关配置的说明直接以注视的形式写在配置文件中,配置文件“log4qt.properties”如下所示:
#设置储存log文件的根目录
logpath=.
log4j.reset=true
log4j.Debug=WARN
log4j.threshold=NULL
#设置是否监听QDebug输出的字符串
log4j.handleQtMessages=true
#在运行中,是否监视此文件配置的变化
log4j.watchThisFile=false
#设置根Logger的输出log等级为All
#设置Log输出的几种输出源(appender):console, daily, rolling
log4j.rootLogger=ALL, console, daily
#设置终端打印记录器
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.target=STDOUT_TARGET
log4j.appender.console.layout=org.apache.log4j.TTCCLayout
log4j.appender.console.layout.dateFormat=dd.MM.yyyy hh:mm:ss.zzz
log4j.appender.console.layout.contextPrinting=true
log4j.appender.console.threshold=ALL
#设置一个每日储存一个log文件的记录器
log4j.appender.daily=org.apache.log4j.DailyFileAppender
log4j.appender.daily.file= l o g p a t h / p r o p e r t y c o n f i g u r a t o r . l o g l o g 4 j . a p p e n d e r . d a i l y . a p p e n d F i l e = t r u e l o g 4 j . a p p e n d e r . d a i l y . d a t e P a t t e r n = y y y y M M d d l o g 4 j . a p p e n d e r . d a i l y . k e e p D a y s = 90 l o g 4 j . a p p e n d e r . d a i l y . l a y o u t = {logpath}/propertyconfigurator.log log4j.appender.daily.appendFile=true log4j.appender.daily.datePattern=_yyyy_MM_dd log4j.appender.daily.keepDays=90 log4j.appender.daily.layout= logpath/propertyconfigurator.loglog4j.appender.daily.appendFile=truelog4j.appender.daily.datePattern=yyyyMMddlog4j.appender.daily.keepDays=90log4j.appender.daily.layout={log4j.appender.console.layout}
log4j.appender.daily.layout.dateFormat= l o g 4 j . a p p e n d e r . c o n s o l e . l a y o u t . d a t e F o r m a t l o g 4 j . a p p e n d e r . d a i l y . l a y o u t . c o n t e x t P r i n t i n g = {log4j.appender.console.layout.dateFormat} log4j.appender.daily.layout.contextPrinting= log4j.appender.console.layout.dateFormatlog4j.appender.daily.layout.contextPrinting={log4j.appender.console.layout.contextPrinting}
log4j.appender.rolling=org.apache.log4j.RollingFileAppender
log4j.appender.rolling.file= l o g p a t h / p r o p e r t y c o n f i g u r a t o r r o l l i n g . l o g l o g 4 j . a p p e n d e r . r o l l i n g . a p p e n d F i l e = t r u e l o g 4 j . a p p e n d e r . r o l l i n g . m a x F i l e S i z e = 20 M B l o g 4 j . a p p e n d e r . r o l l i n g . m a x B a c k u p I n d e x = 10 l o g 4 j . a p p e n d e r . r o l l i n g . l a y o u t = {logpath}/propertyconfigurator_rolling.log log4j.appender.rolling.appendFile=true log4j.appender.rolling.maxFileSize= 20MB log4j.appender.rolling.maxBackupIndex= 10 log4j.appender.rolling.layout= logpath/propertyconfiguratorrolling.loglog4j.appender.rolling.appendFile=truelog4j.appender.rolling.maxFileSize=20MBlog4j.appender.rolling.maxBackupIndex=10log4j.appender.rolling.layout={log4j.appender.console.layout}
log4j.appender.rolling.layout.dateFormat= l o g 4 j . a p p e n d e r . c o n s o l e . l a y o u t . d a t e F o r m a t l o g 4 j . a p p e n d e r . r o l l i n g . l a y o u t . c o n t e x t P r i n t i n g = {log4j.appender.console.layout.dateFormat} log4j.appender.rolling.layout.contextPrinting= log4j.appender.console.layout.dateFormatlog4j.appender.rolling.layout.contextPrinting={log4j.appender.console.layout.contextPrinting}
log4j.logger.LoggerObjectPrio=ERROR, rolling
#设置为false,表示“LoggerObjectPrio”这个类的logger不继承的rootLogger输出源(appender)
log4j.additivity.LoggerObjectPrio=false
至此,使用配置文件配置log4qt的方式就完成了,在主要数分别调用,启动函数和停止函数即可。需要说明的一点是,一般这个配置文件储存在软件主体所在的目录。log文件储存的位置由配置文件配置。
六、使用代码的方式配置Log4Qt
另一种为使用代码配置log4qt的方法。使用代码的配置方法的优点是,比配置文件更加灵活且可定制性强。比如,在配置log文件储存位置的时候。使用配置文件只能在软件运行前配置好。而使用代码配置的时候,可以在软件启动后,获取一个动态的目录作为log文件的储存位置。当然各有优点,根据你的实际需求,选择其中任意一种配置方法都可以。
下面介绍在本log4qt模块中怎样使用代码的方式来配置log4qt。
注:可以在第八节的源码中获取本节完整代码。为了良好的阅读体验,这里仅列出核心代码。
在log4qt/helper目录下创建一个“log4qt_init_helper_by_coding.h“文件,内容如下(简化代码):
#ifndef LOG4QT_HELPER_BY_CODING_H
#define LOG4QT_HELPER_BY_CODING_H
//layouts
#include “log4qt/ttcclayout.h”
//appenders
#include “log4qt/consoleappender.h”
#include “log4qt/rollingfileappender.h”
extern void SetupLog4QtByCodingWithLogSavingDirAbsPath(QString log_saving_dir_abs_path);
extern void ShutDownLog4QtByCoding();
#endif // LOG4QT_HELPER_H
在log4qt/helper目录下创建一个“log4qt_init_helper_by_coding.cpp“文件,内容如下(简化代码):
#include “log4qt_init_helper_by_coding.h”
void SetupLog4QtByCodingWithLogSavingDirAbsPath(QString log_saving_dir_abs_path)
{
QString absPath = log_saving_dir_abs_path;
auto rootLogger = Log4Qt::Logger::rootLogger();
// Create a layout
auto *layout = new Log4Qt::TTCCLayout();
layout->setName(QStringLiteral("My Layout"));
layout->setDateFormat("dd.MM.yyyy hh:mm:ss.zzz");
layout->activateOptions();
// Create a console appender
Log4Qt::ConsoleAppender *consoleAppender =
new Log4Qt::ConsoleAppender(layout, Log4Qt::ConsoleAppender::STDOUT_TARGET);
consoleAppender->setName(QStringLiteral("My console Appender"));
consoleAppender->activateOptions();
rootLogger->addAppender(consoleAppender);
//Create a rolling file appender
Log4Qt::RollingFileAppender *rollingFileAppender =
new Log4Qt::RollingFileAppender(layout, absPath + "/basic.log", true);
rollingFileAppender->setName(QStringLiteral("My rolling file appender"));
//default is 10 MB (10 * 1024 * 1024).
rollingFileAppender->setMaximumFileSize(20 * 1024 * 1024);
rollingFileAppender->setThreshold(Log4Qt::Level::Value::INFO_INT);//设置子输出等级过滤
rollingFileAppender->activateOptions();
rootLogger->addAppender(rollingFileAppender);
//设置根logger允许所以等级的消息被输出(子输出过滤是在根输出过滤的基础上)
rootLogger->setLevel(Log4Qt::Level::ALL_INT);
Log4Qt::LogManager::setHandleQtMessages(true);//设置监听qt自带的log输出
}
void ShutDownLog4QtByCoding()
{
auto logger = Log4Qt::Logger::rootLogger();
logger->removeAllAppenders();
logger->loggerRepository()->shutdown();
}
七、在Qt工程中引入Log4Qt库模块的方法
通过第四节、五节、六节,我们得到了一个跨平台的Log模块,这里介绍一下这个模块的使用方法。
使用步骤如下:
新建一个Qt Creater工程并引入做好的“log4qt模块”
使用Qt Creater新建一个工程QWidget工程,名称叫做“Log4qtDemo”。在工程文件(.pro)中加入如下配置,用于导入之前配置好的log4qt模块。
include($$PWD/src/third_libs/log4qt/log4qtlib.pri)
1
在主函数中加入如下内容
#include “ui/demodialog.h”
#include
#include
#include
#include “log4qt_init_helper_by_coding.h”
#include “log4qt/logger.h” //每个使用log4qt的类都需要包含此头文件
//在类的cpp文件中,使用此静态方法声明logger(此方法比较通用)
//第二个参数写类名字,因此,输出的log条目中包含其对应的类名
LOG4QT_DECLARE_STATIC_LOGGER(logger, Main)
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//使用QStandardPaths获取标准个人程序数据储存目录,需要先设置以下名称
QCoreApplication::setApplicationName("Log4qtDemo");
QCoreApplication::setApplicationVersion("0.0.1");
QCoreApplication::setOrganizationName("OrgName");
QCoreApplication::setOrganizationDomain("name.org");
/*
//使用config 配置Log4Qt
//把源代码目录中的"log4qt.properties"文件复制到编译好的可执行文件所在的目录
//QString configFileAbsPath = QCoreApplication::applicationFilePath() + QStringLiteral(".log4qt.properties");//配置文件包括应用程序名称
QString configFileAbsPath = QCoreApplication::applicationDirPath() +"/"+ QStringLiteral("log4qt.properties");//配置文件不包括应用程序名称
SetupLog4QtByConfigWithConfigFileAbsPath(configFileAbsPath);
*/
//使用纯代码配置Log4Qt
QStringList path_list2 = QStandardPaths::standardLocations(QStandardPaths::DataLocation);
QString std_base_path = path_list2[0];
QString my_log_path = std_base_path + "/logs";
// QString logSavingDirAbsPath = QCoreApplication::applicationDirPath();
qDebug() << "my_log_path = " << my_log_path;
SetupLog4QtByCodingWithLogSavingDirAbsPath(my_log_path);
//可以使用以下三种方式编写Log输出条目
//1.log4qt基本的logger输出
logger()->debug() << "example ####11##### logger()->debug()";
logger()->error() << "example ####11##### logger()->error()";
//2.log4qt基本的宏定义输出
l4qError() << "example ####22##### l4qError() ";
l4qError(QStringLiteral("example ####22##### l4qError() %1"), 10);
//3.使用qt平台的Log库输出,(Log4Qt会监听qt的log的输出,并统一输出到Log文件中)
qDebug() << "example ####33##### qDebug()\n\n\n";
DemoDialog w;
w.show();
int ret = a.exec();
//ShutDownLog4QtByConfig();//exec()执行完成后,才关闭logger
ShutDownLog4QtByCoding();//exec()执行完成后,才关闭logger
return ret;
}
Log4qtDemo完整代码
log4qt跨平台模块,在src/third_libs目录中
log4qt开源库下载下来源代码,用于编译出各个系统平台下的log4qt动态库, 在src/third_libs_src_code目录中