title: “ qInstallMessageHandler输出重定向实现日志功能\t\t”
tags:
- qt
url: 880.html
id: 880
categories: - Qt
date: 2018-01-16 19:54:01
介绍
Qt提供了一系列消息模板:qInfo、qDebug、qWarning、qCritical、qFatal,官方文档 qDebug可能比较常用,他们分别表示消息、调试信息、一般警告、严重错误、致命错误,默认情况会输出至调试窗口。而在release模式下可以将其输出重定向至文件,从而实现日志功能。
qInstallMessageHandler
使用此方法可以将上述五种消息重定向至指定函数,范例如下:
/**
- @brief CustomOutputMessage
- @param type 消息类型
- @param context 消息信息
- @param msg 消息内容
*/
void CustomOutputMessage(QtMsgType type,
const QMessageLogContext &context,
const QString &msg) {
QString message_type;
switch(type) {
case QtInfoMsg:
message_type = QString(“Info:”);
break;
case QtDebugMsg:
message_type = QString(“Debug:”);
break;
case QtWarningMsg:
message_type = QString(“Warning:”);
break;
case QtCriticalMsg:
message_type = QString(“Critical:”);
break;
case QtFatalMsg:
message_type = QString(“Fatal:”);
}
QString current_date = QDateTime::currentDateTime().
toString(“yyyy-MM-dd hh:mm:ss”);
QFile file(“log.txt”);
file.open(QIODevice::WriteOnly | QIODevice::Append);
QString message = QString(“%1 %2 (%3)\r\n”).arg(message_type).
arg(msg).arg(current_date);
file.write(message.toLatin1());
file.close();
if(type == QtFatalMsg) {
QMessageBox::critical(nullptr, “致命错误”,
QString(“%2\r\n%1\r\n请解决问题后重新启动程序”).
arg(msg).arg(current_date));
}
}
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
//日志功能
ifndef QT_DEBUG
qInstallMessageHandler(CustomOutputMessage);
endif
qDebug(“123”);
// qFatal(“123”);
MainWindow w;
w.show();
return a.exec();
}
其中使用qInstallMessageHandler方法前判断了QT_DEBUG宏,即在调试模式直接输出,在release模式输出至文件 函数模板:
typedef void (*QtMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &);
方法的第一个参数是枚举类型,除上述类型外还有一个QtSystemMsg=QtCriticalMsg
其他
取消重定向
通过qInstallMessageHandler(nullptr);即可取消重定向。此处不建议写0,毕竟此处应当传递的是一个指针。
qFatal
Qt比较人性化,当发生qFatal时(致命错误),会在完成qInstallMessageHandler的操作后就直接调用std::abort让程序被系统kill,并由系统弹出错误框。系统弹出提示框的情况在用户使用时并不美观。 分析原因: qFatal宏会调用如下方法:
void QMessageLogger::fatal(const char *msg, …) const Q_DECL_NOTHROW
{
QString message;
va_list ap;
va_start(ap, msg); // use variable arg list
QT_TERMINATE_ON_EXCEPTION(message = qt_message(QtFatalMsg, context, msg, ap));//1
va_end(ap);
qt_message_fatal(QtFatalMsg, context, message);//2
}
1处会调用qInstallMessageHandler指向的函数句柄 2处代码如下:
static void qt_message_fatal(QtMsgType, const QMessageLogContext &context, const QString &message)
{
if defined(Q_CC_MSVC) && defined(QT_DEBUG) && defined(_DEBUG) && defined(_CRT_ERROR)
wchar_t contextFileL[256];
convert_to_wchar_t_elided(contextFileL, sizeof contextFileL / sizeof contextFileL,
context.file);
int reportMode = _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_WNDW);
_CrtSetReportMode(_CRT_ERROR, reportMode);
int ret = _CrtDbgReportW(_CRT_ERROR, contextFileL, context.line, _CRT_WIDE(QT_VERSION_STR),
reinterpret_cast<const wchar_t >(message.utf16()));
if ((ret == 0) && (reportMode & _CRTDBG_MODE_WNDW))
return; // ignore
else if (ret == 1)
_CrtDbgBreak();
else
Q_UNUSED(context);
Q_UNUSED(message);
endif
std::abort();
}
最后调用了abort。。。。真的是致命错误,所以直接通过abort令系统杀死当前程序,这算他杀性致命? 对比qDebug:
void QMessageLogger::debug(const char *msg, …) const
{
va_list ap;
va_start(ap, msg); // use variable arg list
const QString message = qt_message(QtDebugMsg, context, msg, ap);
va_end(ap);
if (isFatal(QtDebugMsg))//5
qt_message_fatal(QtDebugMsg, context, message);
}
会在5行提前判断一下是不是fatal,此处当然不是,所以不会调用后面的qt_message_fatal。 为什么明知不是还判断?isFatal里面对非Fatal做了一些操作,具体的没有深入阅读 解决方案: 上述代码修改:
if(type == QtFatalMsg) {
QMessageBox::critical(nullptr, “致命错误”,
QString(“%2\r\n%1\r\n请解决问题后重新启动程序”).
arg(msg).arg(current_date));
exit(0);
}
此方法等于主动退出,算是自杀了。但是无法很好地处理所有的线程、内存等各种问题。如果没有exit,紧接着即将调用的abort也无法优雅的解决这些问题,线程问题、数据存储等等该如何处理就要看系统了。 当然,既然已经发生了致命错误,并且成功在log日志文件中记录了此错误,那就让他崩溃吧。由此也说明程序中除非无法挽回的问题用qFatal,其他的最好最高只用到qCritical.