在开发QT程序的时候,很多开发者也就仅仅用QT的日志模块qDebug一下调试信息,在真正的日志记录上还是采用一些别的日志库。其实QT的日志模块还是很强大的,可以满足日常的基本需求。这里就详细介绍一下QT日志模块的个性化使用方法。
格式化日志输出
默认情况下,日志格式是只输出对应的日志内容没有额外信息的。我们可以通过修改环境变量QT_MESSAGE_PATTERN或者调用方法 qSetMessagePattern来修改日志的输出格式。日志格式中常用的占位符号如下所示:
%{appname} 应用程序的名称(QCoreApplication::applicationName())
%{category} 日志所处的领域
%{file} 打印该日志的文件路径
%{function} 打印日志的函数
%{line} 打印日志在文件中的行数
%{message} 日志的内容
%{pid} 打印日志的程序的PID(QCoreApplication::applicationPid())
%{threadid} 打印日志的线程ID
%{qthreadptr} 打印日志的线程指针
%{type} 日志级别("debug", "warning", "critical" or "fatal")
%{time process}日志发生时程序启动了多久
%{time boot} 日志发生时系统启动了多久
%{time [format]}以固定时间格式输出日志打印的时间,默认为QISODate格式
格式化日志的调用方法如下:
int main(int argc,char*argv[])
{
QCoreApplication app(argc, argv);
qSetMessagePattern("%{time yyyy-MM-dd hh:mm:ss}--[%{type}]--%{function}:%{message}");
qDebug() << "exception occured";
qInfo() << "call other function";
return app.exec();
}
输出的日志内容格式如下:
2022-06-09 10:09:54--[debug]--main:exception occured
2022-06-09 10:09:55--[info]--main:call other function
我们还可以使用条件变量
%{if-debug}, %{if-info} %{if-warning}, %{if-critical} or %{if-fatal}
给不同级别的日志指定不同的格式,使用方法如下:
int main(int argc,char*argv[])
{
QCoreApplication app(argc, argv);
//针对Warning信息和Fatal信息输出了额外的内容
qputenv("QT_MESSAGE_PATTERN", QByteArray("%{time yyyy-MM-dd hh:mm:ss} [%{type}]%{if-warning}[%{function}]%{endif}%{if-fatal}[%{function}--%{line}]%{endif}:%{message}"));
qDebug() << "debuginfo occured";
qInfo() << "call other function";
qWarning() << "doesn't work";
qFatal("fatal error");
return app.exec();
}
输出的日内容如下:
2022-06-09 10:48:32 [debug]:debuginfo occured
2022-06-09 10:48:32 [info]:call other function
2022-06-09 10:48:32 [warning][main]:doesn't work
2022-06-09 10:48:32 [fatal][main--116]:fatal error
我们可以通过修改QT_MESSAGE_PATTERN环境变量动态的修改日志的输出格式。如果同时调用qSetMessagePattern和QT_MESSAGE_PATTERN环境变量来修改日志的输出格式,那么QT_MESSAGE_PATTERN的优先级要高于qSetMessagePattern。
输出日志到文本
QT默认的日志内容是输出到终端的,不会输出到文件里面,如果需要将日志内容输出到文件中我们需要通过qInstallMessageHandler设置日志信息处理函数。使用方法如下:
//日志消息的处理函数
void logmessageHander(QtMsgType type, const QMessageLogContext& context, const QString& message)
{
//获取格式化的日志信息
QString typeStr = qFormatLogMessage(type,context,message);
//可以根据日志的级别进行过滤
QString levelText;
switch (type) {
case QtDebugMsg:
levelText = "Debug";
break;
case QtInfoMsg:
levelText = "Info";
break;
case QtWarningMsg:
levelText = "Warning";
break;
case QtCriticalMsg:
levelText = "Critical";
break;
case QtFatalMsg:
levelText = "Fatal";
break;
}
QFile file("myapp.log");
file.open(QIODevice::WriteOnly | QIODevice::Append);
QTextStream textStream(&file);
textStream << typeStr << endl;
}
int main(int argc,char*argv[])
{
QCoreApplication app(argc, argv);
qSetMessagePattern("%{time yyyy-MM-dd hh:mm:ss} [%{type}]%{if-warning}[%{function}]%{endif}%{if-fatal}[%{function}--%{line}]%{endif}:%{message}");
qInstallMessageHandler(logmessageHander);
qDebug() << "debuginfo occured";
qInfo() << "call other function";
qWarning() << "doesn't work";
qFatal("fatal error");
return app.exec();
}
如果需要关闭日志输出,取消之前注册的日志处理函数,我们可以调用
//取消注册的日志处理函数
qInstallMessageHandler(0);
日志输出对象信息
在调试一些复杂对象的时候,我们需要输出对象的成员信息到日志当中。但是默认情况下qt的日志库是不支持输出自定义对象的。这时候我们可以通过重写操作符实现对自定义象的日志输出。使用方法如下:
//消费者信息类
struct Customer {
QString name;
int age;
QString clientid;
QString addr;
};
QDebug operator<<(QDebug debug, const Customer& customer)
{
//保存QDebug的状态
QDebugStateSaver saver(debug);
debug.nospace() << "("
<< "name: " << customer.name << ","
<< "age: " << customer.age << ","
<< "clientid: " << customer.clientid << ","
<< "addr: " << customer.addr << ","
<< ")";
return debug;
}
int main(int argc,char*argv[])
{
QCoreApplication app(argc, argv);
Customer customer = { "Liming", 22, "677888", "北京市海淀区"};
qDebug() << "Customer info" << customer;
return app.exec();
}
QDebugStateSaver类可以保存QDebug的配置,并在销毁的时候自动恢复。使用它我们可以避免在操作符重载的时候破坏QDebug中的配置。