/* 该类实现将调试信息打印输出到文件的功能,同时每次运行程序,会自动检查过期日志文件并删除,过期时间为一分钟,可在deleteLog方法中进行修改。
思路:使用QtMessageHandler自带的回调消息处理函数myMessageHandler(可重写),获取调试信息所处文件位置及代码所处行数,并输出信息内容到文件,其中还要使用QSetting类对ini配置文件进行模块开关的读写。
头文件代码如下:
logOutput.h
#ifndef LOGOUTPUT_H
#define LOGOUTPUT_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LOG_MAXSIZE 5 * 1024 //单个log文件最大值
class logOutput : public QObject
{
Q_OBJECT
public:
explicit logOutput(QObject *parent = nullptr);
~logOutput();
#if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
static void logThread::outPutMsg(QtMsgType type, const char *msg);
#else
//信息处理函数(重写的myMessageHandler)
/*功能说明:通过调试信息保存到日志文件
*
*参数说明:
* msgType: 调试信息类型或级别(qdebug, qwarning, qfatal 。。。。)
* context: 调试信息所处文本,可使用context.file和context.line获取文本所处行数及所处文件路径,以及使用context.function获取文本所处函数名
* msg: 调试信息内容,自定义
*/
static void outPutMsg(QtMsgType msgType, const QMessageLogContext &context, const QString &msg);
#endif
static void install(); //安装信息处理函数
static void uninstall(); //卸载信息处理函数
static void deleteLog(); //删除过期日志
// static void setLogMaxSize(qint64 maxsize); //设置单个日志文件最大值
private:
/*
* 函数功能:
* 1、根据调试信息以及日期,保存到相应的文件。
* 2、在保存文件前需要判断文件大小是否大于自定义值,如果大于,便按照序号从小到大新建一个。
*
*
*/
static void saveLog(QString message, QString type);
//参数为模块开关名
static bool judgeSwitch(QString switchName); //判断模块是否存在
static void write_ini_file(QString switchName); //写入ini信息
static QVariant read_ini_file(QString switchName); //读取ini信息
};
#endif // LOGOUTPUT_H
头文件的方法中,outPutmsg方法的作用是当其他模块调用qDebug等调试函数是,就会将当前语句的context内容、信息种类(qDebug、qWarning、qInfo。。。。)以及信息内容。其中context内容还可获取该语句的文件位置及行数。install是用来间接安装消息处理程序的。uninstall同理,一旦使用uninstall以后,该语句后面的调试信息都将得不到处理,从而输出到控制台上。deleteLog,是用来删除创建超过一定时间的过期日志的。saveLog是将消息处理函数得出的message保存到指定文件中。最后三个函数是对开关配置文件进行检查、读取和写入的操作。
logOutput.cpp
#include "logoutput.h"
#include
logOutput::logOutput(QObject *parent)
: QObject{parent}
{
}
logOutput::~logOutput()
{
qDebug("内存已释放");
}
//安装日志函数
void logOutput::install()
{
//安装消息处理函数
#if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
qInstallMsgHandler(outPutMsg);
#else
qInstallMessageHandler(outPutMsg);
#endif
//创建log文件夹
QString logPath = QApplication::applicationDirPath() + "/log";
QDir dir(logPath);
if(!dir.exists())
{
dir.mkdir(logPath);
qDebug()<<"文件夹创建成功";
}
deleteLog(); //删除过期日志
}
//卸载日志函数
void logOutput::uninstall()
{
#if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
qInstallMsgHandler(0);
#else
qInstallMessageHandler(0);
#endif
}
//日志信息处理函数
#if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
void logThread::outPutMsg(QtMsgType type, const char *msg)
#else
void logOutput::outPutMsg(QtMsgType msgType, const QMessageLogContext &context, const QString &msg)
#endif
{
bool switchValue; //判断开关标志
static QMutex mutex; //设置互斥锁
//通过文件路径获取模块名比如widget.cpp
QString switchKey = QString::fromLocal8Bit(context.file);
int last = switchKey.lastIndexOf("\\");
int leng = switchKey.length();
switchKey = switchKey.right(leng-last-1);
if(judgeSwitch(switchKey) == true) //判断对应模块配置属性是否存在
{
switchValue = read_ini_file(switchKey).toBool(); //读取配置的值
}
else
{
write_ini_file(switchKey); //新写入不存在的配置
switchValue = read_ini_file(switchKey).toBool(); //读取配置的值
}
//判断信息类型
QString type;
switch (msgType) {
case QtDebugMsg:
type = QString("Debug");
break;
case QtWarningMsg:
type = QString("Warning");
switchValue = true; //警告信息不能通过开关关闭
break;
case QtCriticalMsg:
type = QString("Critical");
switchValue = true; //危险信息不能通过开关关闭
break;
case QtFatalMsg:
type = QString("Fatal");
switchValue = true; //致命信息不能通过开关关闭
break;
case QtInfoMsg:
type = QString("Info");
default:
break;
}
if(switchValue == true) //判断配置开关是否打开
{
mutex.lock(); //互斥关锁
//文件名和行数以及函数
QString contextInfo = QString("[File:(%1), Line:(%2), Funtion(%3)]:").arg(context.file).arg(context.line).arg(context.function);
//获取当前时间,精确到秒
QString currentTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
//拼接信息字符串
QString message = QString("[%1] %2: %3 %4").arg(currentTime).arg(type).arg(contextInfo).arg(msg);
//存入信息到日志文件
saveLog(message, type);
mutex.unlock(); //开锁
}
}
//判断配置是否存在
bool logOutput::judgeSwitch(QString switchName)
{
QSettings ini_config("logSetting.ini", QSettings::IniFormat);
ini_config.beginGroup("class_Switch");
bool isContains = ini_config.contains(switchName);
ini_config.endGroup();
return isContains;
}
//读取配置文件
QVariant logOutput::read_ini_file(QString switchName)
{
QSettings ini_config("logSetting.ini", QSettings::IniFormat);
ini_config.beginGroup("class_Switch");
QVariant switchValue = ini_config.value(switchName);
ini_config.endGroup();
return switchValue;
}
//写入配置文件
void logOutput::write_ini_file(QString switchName)
{
QSettings ini_config("logSetting.ini", QSettings::IniFormat);
ini_config.beginGroup("class_Switch");
ini_config.setValue(switchName, false);
ini_config.endGroup();
}
//删除过期日志
void logOutput::deleteLog()
{
//获取日志文件夹地址
QString dirName = QApplication::applicationDirPath() + "/log";
QDir dir(dirName);
//获取文件夹下所有文件信息列表
QFileInfoList infoList = dir.entryInfoList(QDir::Files);
//遍历日志文件
foreach (QFileInfo fileInfo, infoList) {
//将文件创建时间与过期时间作比较,如果创建时间小于过期时间,则删除(代码是一分钟期限,如果改天为单位可以使用adddays)
if(fileInfo.birthTime() <= QDateTime::currentDateTime().addSecs(-1))
{
QFile::setPermissions(dirName + "/" +fileInfo.fileName(), QFileDevice::ReadOther | QFileDevice::WriteOther);
if(QFile::remove(dirName + "/" +fileInfo.fileName()))
{
qDebug("日志删除成功!");
}
else
{
qDebug("日志删除失败!");
}
}
}
}
//保存日志到文件
void logOutput::saveLog(QString message, QString type)
{
int i = 1; //当文件大小超过最大值时,给新文件添加编号
//以天为单位给文件命名
QString fileName = QApplication::applicationDirPath() + "/log/" + QDateTime::currentDateTime().toString("yyyy-MM-dd")+ "_" + type + "_log";
//文件名右边(后缀)
QString fileNameRight;
//最终要写入的文件名
QString fileNameLast = fileName + ".txt";
//绑定文件对象
QFile file(fileNameLast);
//判断文件大小
while(file.size() >= LOG_MAXSIZE)
{
//给新文件加入序号后缀
fileNameRight = QString("%1.txt").arg(i);
//拼接最终文件名
fileNameLast = fileName + fileNameRight;
//修改file绑定的文件名
file.setFileName(fileNameLast);
i++;
}
//只写和拼接的方式打开文件
bool isopen = file.open(QIODevice::WriteOnly | QIODevice::Append);
if(isopen == true)
{
QTextStream write(&file);
qDebug() << message;
write << message << "\r\n";
file.flush();
file.close();
}
}
代码运行流程:首先在main函数调用install静态方法安装消息处理函数后,然后创建一个log文件夹用以存放日志文件,同时在创建文件夹以后调用deleteLog函数,用以每次运行程序时,检查过期文件并删除。
以qDebug(“msg”)为例,然后等待主程序运行到该函数的位置。然后这时会调用outPutMsg函数,获取该语句的类型为QtDebug、文件内容context、以及信息内容“msg”。这时需要先对使用context.file获取文件地址,并提取其文件名,然后调用judgeSwitch方法,判断该内容的开关是否存在,如果是否,就调用写入配置函数,将该文件名写入开关配置,初始为false。接着通过switch语句判断信息类型,如果是qDebug和QInfo信息,则获取种类字符串type即可,其他信息种类必须把开关标志位设置为true。也就是不能通过开关关闭,防止错过致命消息。然后判断开关标志位,如果是true,则进行下一步,如果是false则停止。进入下一步以后,获取当前时间,信息所处文件名、行数、函数名以及信息内容本身。然后将他们拼接成一个message字符串(自定义输出到文件的内容,代码仅供参考),然后调用saveLog,传入信息内容和类型以保存文件。
首先,需要获取当天,当前类型的log文件夹大小,超过文件最大值则再创建一个同名文件,编号依次递增,这里设置的是最大5KB,然后使用QdataStream写入mesaage,具体实现请阅读代码,注释的很清楚。
展示:
配置文件内容如下:
log文件夹内容:
代码调试语句:
log文件内容:
message = 当前时间 + 信息类型 + 文件位置 + 代码行数 + 函数名 + 信息内容。
ini文件的读写请阅读:https://www.cnblogs.com/QingYiShouJiuRen/p/16639547.html
后续扩展:可以将日志存储地址写入配置文件。也可使用代码直接设置地址。