介绍
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.
最新评论