01 背景说明
做Qt开发经常需要用到qDebug、qWarning这些来查看输出信息,配合调试程序,但这些输出信息只输出到调试窗口,或者是程序执行的控制台窗口上。
今天我们来学习,如何捕捉所有的Qt调试信息,并统一对调试信息加上时间戳、消息等级、文件名、行号等,最后将消息内容保存到日志文件,方便日后程序的跟踪、和数据分析。
02 关键技术点
关键技术点是使用Qt提供的一个qInstallMessageHandler()函数,qInstallMessageHandler()用于注册消息处理函数,注册后消息处理函数将会捕捉所有的调试信息,并进行统一处理。函数原型和使用示例如下:
// 消息处理函数原型
typedef void (*QtMessageHandler)(QtMsgType, const QMessageLogContext &, const QString );
QtMessageHandler qInstallMessageHandler(QtMessageHandler);
// 例子:
// 定义信息处理函数
void MyMessageHandler(QtMsgType eMsgType, const QMessageLogContext& logContext, const QString &text)
{
// ...
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// 注册消息处理函数
qInstallMessageHandler(MyMessageHandler);
}
03 增加一个日志处理类来处理
关键点我们都懂了,但为了方便使用,我们封装一个日志处理类MyLogging,该日志处理类除了实现捕捉调试信息外,还统一给调试信息加上日期、时间,消息等级等格式化内容,并保存到日志文件。
- MyLogging类设计:
#ifndef MYLOGGING_H
#define MYLOGGING_H
#include <QFile>
// 日志处理类
class MyLogging
{
public:
MyLogging();
// 做成单实例
static MyLogging& getInstance();
// 打开日志功能
bool openLogging(const QString& qstrFileName);
// 关闭日志功能
void closeLogging();
// 保存消息到日志文件
bool saveMsgToLogFile(const QString& qstrMsgText);
private:
static MyLogging gLogging;
// 日志文件对象
QFile m_logFile;
};
#endif // MYLOGGING_H
- 消息处理函数核心代码:
捕捉消息函数入口,在这里可以格式化消息内容,如增加消息时间戳、等级等内容。
#include <QDateTime>
#include <iostream>
// 消息处理函数,用来格式化消息文本,输出屏幕和保存到文件
void MyMessageHandler(QtMsgType eMsgType, const QMessageLogContext& logContext, const QString &msg)
{
// 时间戳:yyyy-MM-dd hh:mm:ss zzz
const QString qstrLogTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss zzz");
// 消息类型
QString qstrMsgType;
switch (eMsgType)
{
case QtMsgType::QtDebugMsg:
qstrMsgType = "Debug";
break;
case QtMsgType::QtWarningMsg:
qstrMsgType = "Warning";
break;
case QtMsgType::QtCriticalMsg:
qstrMsgType = "Critical";
break;
case QtMsgType::QtFatalMsg:
qstrMsgType = "Fatal";
break;
case QtMsgType::QtInfoMsg:
qstrMsgType = "Info";
break;
default: break;
}
// 消息文本日志内容
const QString qstrLogText = QString("%0 [%1] (%2:%3 %4) %5")
.arg(qstrLogTime)
.arg(qstrMsgType)
.arg(logContext.file)
.arg(logContext.line)
.arg(logContext.function)
.arg(msg);
// 打印到屏幕
std::cout << qstrLogText.toStdString() << std::endl;
// 保存到日志文件
MyLogging::getInstance().saveMsgToLogFile(qstrLogText);
}
- 打开日志文件代码:
bool MyLogging::openLogging(const QString &qstrFileName)
{
if (m_logFile.isOpen())
{
// 已经打开,返回true
return true;
}
m_logFile.setFileName(qstrFileName);
QIODevice::OpenMode e_open_mode = QIODevice::WriteOnly | QIODevice::Text;
if (QFile::exists(qstrFileName))
{
// 日志文件已经存在
e_open_mode |= QIODevice::Append;
}
// 打开日志文件
if (!m_logFile.open(e_open_mode))
{
return false;
}
// 注册消息处理函数
qInstallMessageHandler(MyMessageHandler);
return true;
}
- 关闭日志文件代码:
void MyLogging::closeLogging()
{
qInstallMessageHandler(nullptr);
m_logFile.close();
}
- 保存消息到日志文件
bool MyLogging::saveMsgToLogFile(const QString &qstrMsgText)
{
if (!m_logFile.isOpen())
{
return false;
}
// 加上回车符(一条消息,一条日志)
QString qstrLog = qstrMsgText+"\n";
m_logFile.write(qstrLog.toUtf8());
return true;
}
04 使用日志处理类应用到项目
#include <QCoreApplication>
#include <locale>
#include "MyLogging.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// 支持 UTF8 编码
std::locale loc{ "el_GR.utf8" };
std::locale::global(loc);
// 打开日志文件功能
MyLogging::getInstance().openLogging("qt_message_handler_log.txt");
// 输出日志信息
qDebug() << "这是一条 debug 调试消息。";
qWarning() << "这是一条 warning 警告消息。";
qCritical() << "这是一条 critical 严重消息。";
// 关闭日志文件功能
MyLogging::getInstance().closeLogging();
return a.exec();
}
执行输出日志如下:
生成日志文件内容:
05 附录:完整源码
关注公众号下载本示例完整源码(07_Qt_MessageHandler_practical.zip)。
-【End】-
#想了解更多精彩内容,关注下方公众号,还有示例源码、开发工具免费下载。
喜欢本文章,记得点赞、分享、关注哦~