【问题标题】:How to redirect qDebug, qWarning, qCritical etc output?如何重定向 qDebug、qWarning、qCritical 等输出?
【发布时间】:2011-06-24 15:48:42
【问题描述】:

我使用大量qDebug() << 语句进行调试输出。是否有任何跨平台的方式可以将调试输出重定向到文件,而无需使用 shell 脚本?我猜open()dup2() 会在 Linux 中完成这项工作,但它会在 Windows 中使用 MinGW 编译吗?

也许有一种 Qt 方法可以做到这一点?

【问题讨论】:

    标签: c++ debugging qt mingw qdebug


    【解决方案1】:

    如果应用程序是从 Qt Creator 运行的,那么这是一个跨平台的解决方案,用于登录到控制台,以及在编译并作为独立应用程序运行时登录到 debug.log 文件。

    ma​​in.cpp

    #include <QApplication>
    #include <QtGlobal>
    #include <QtDebug>
    #include <QTextStream>
    #include <QTextCodec>
    #include <QLocale>
    #include <QTime>
    #include <QFile>   
    
    const QString logFilePath = "debug.log";
    bool logToFile = false;
        
    void customMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
    {
        QHash<QtMsgType, QString> msgLevelHash({{QtDebugMsg, "Debug"}, {QtInfoMsg, "Info"}, {QtWarningMsg, "Warning"}, {QtCriticalMsg, "Critical"}, {QtFatalMsg, "Fatal"}});
        QByteArray localMsg = msg.toLocal8Bit();
        QTime time = QTime::currentTime();
        QString formattedTime = time.toString("hh:mm:ss.zzz");
        QByteArray formattedTimeMsg = formattedTime.toLocal8Bit();
        QString logLevelName = msgLevelHash[type];
        QByteArray logLevelMsg = logLevelName.toLocal8Bit();
    
        if (logToFile) {
            QString txt = QString("%1 %2: %3 (%4)").arg(formattedTime, logLevelName, msg,  context.file);
            QFile outFile(logFilePath);
            outFile.open(QIODevice::WriteOnly | QIODevice::Append);
            QTextStream ts(&outFile);
            ts << txt << endl;
            outFile.close();
        } else {
            fprintf(stderr, "%s %s: %s (%s:%u, %s)\n", formattedTimeMsg.constData(), logLevelMsg.constData(), localMsg.constData(), context.file, context.line, context.function);
            fflush(stderr);
        }
    
        if (type == QtFatalMsg)
            abort();
    }
    
    int main(int argc, char *argv[])
    {
        QByteArray envVar = qgetenv("QTDIR");       //  check if the app is ran in Qt Creator
    
        if (envVar.isEmpty())
            logToFile = true;
    
        qInstallMessageHandler(customMessageOutput); // custom message handler for debugging
    
        QApplication a(argc, argv);
        // ...and the rest of 'main' follows
    

    日志格式由QString("%1 %2: %3 (%4)").arg...(用于文件)和fprintf(stderr, "%s %s: %s (%s:%u, %s)\n"...(用于控制台)处理。

    灵感:https://gist.github.com/polovik/10714049.

    【讨论】:

    • 我看到您在每个日志事件中都调用了“outFile.close()”。我可以省略吗?
    • 我不建议在此设置中使用它,因为您每次都打开日志文件,因此应该关闭它。但是您可以通过某种方式更改算法,该日志文件仅在应用程序的初始化时打开一次。这样,您只需在应用退出时将其关闭一次。
    • 谢谢!很有帮助。
    • 此消息处理程序不是线程安全的。如果两个线程同时发送日志消息,您将丢失日志消息(其中一个线程的 outFile.open() 将返回 false)。您可以在尝试打开文件之前锁定 QMutex,然后在关闭文件后解锁互斥锁。这是最简单的方法,但会引入线程争用。您需要查看低开销的线程安全消息队列,否则……您可能会更好地使用框架!
    • 我同意你的看法——它远非完美。但它大部分时间都在做它的工作。无论如何,欢迎任何修改!
    【解决方案2】:

    这是一个简单的、线程安全的惯用 Qt 示例,用于记录到 stderr 和文件:

    void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message)
    {
        静态 QMutex 互斥体;
        QMutexLocker lock(&mutex);
    
        静态 QFile 日志文件(LOGFILE_LOCATION);
        静态 bool logFileIsOpen = logFile.open(QIODevice::Append | QIODevice::Text);
    
        std::cerr 
    

    如其他答案中所述,使用qInstallMessageHandler(messageHandler) 安装它。

    【讨论】:

      【解决方案3】:

      好吧,我想说的是,当您需要将调试输出重定向到不同于 stderr 的任何内容时,您可以考虑使用一些日志记录工具。如果您觉得需要,我建议您使用来自Qxt 库的QxtLogger“QxtLogger 类是一种易于使用、易于扩展的日志工具。”)。

      【讨论】:

        【解决方案4】:

        这是一个挂钩默认消息处理程序的工作示例。

        谢谢@Ross Rogers!

        // -- main.cpp
        
        // Get the default Qt message handler.
        static const QtMessageHandler QT_DEFAULT_MESSAGE_HANDLER = qInstallMessageHandler(0);
        
        void myCustomMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
        {
            // Handle the messages!
        
            // Call the default handler.
            (*QT_DEFAULT_MESSAGE_HANDLER)(type, context, msg);
        }
        
        int main(int argc, char *argv[])
        {
            qInstallMessageHandler(myCustomMessageHandler);
        
            QApplication a(argc, argv);
        
            qDebug() << "Wello Horld!";
        
            return 0;
        }
        

        【讨论】:

          【解决方案5】:

          来自here 所有功劳归spirit

          #include <QApplication>
          #include <QtDebug>
          #include <QFile>
          #include <QTextStream>
          
          void myMessageHandler(QtMsgType type, const QMessageLogContext &, const QString & msg)
          {
              QString txt;
              switch (type) {
              case QtDebugMsg:
                  txt = QString("Debug: %1").arg(msg);
                  break;
              case QtWarningMsg:
                  txt = QString("Warning: %1").arg(msg);
              break;
              case QtCriticalMsg:
                  txt = QString("Critical: %1").arg(msg);
              break;
              case QtFatalMsg:
                  txt = QString("Fatal: %1").arg(msg);
              break;
              }
              QFile outFile("log");
              outFile.open(QIODevice::WriteOnly | QIODevice::Append);
              QTextStream ts(&outFile);
              ts << txt << endl;
          }
          
          int main( int argc, char * argv[] )
          {
              QApplication app( argc, argv );
              qInstallMessageHandler(myMessageHandler);   
              ...
              return app.exec();
          }
          

          【讨论】:

          • case QtFatalMsg:...abort(); // 它会在写日志之前退出
          • 从 QT 5 开始,应该使用qInstallMessageHandler 而不是qInstallMsgHandler 来更改消息处理程序。
          • 此消息处理程序不是线程安全的。如果两个线程同时发送日志消息,您将丢失日志消息(其中一个线程的 outFile.open() 将返回 false)。您可以在尝试打开文件之前锁定 QMutex,然后在关闭文件后解锁互斥锁。这是最简单的方法,但会引入线程争用。否则,您将需要查看低开销线程安全消息队列......您可能会更好地使用框架。
          【解决方案6】:

          您必须使用qInstallMsgHandler 函数安装消息处理程序,然后,您可以使用QTextStream调试 消息写入文件。这是一个示例:

          #include <QtGlobal>
          #include <stdio.h>
          #include <stdlib.h>
          
          void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
          {
              QByteArray localMsg = msg.toLocal8Bit();
              switch (type) {
              case QtDebugMsg:
                  fprintf(stderr, "Debug: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
                  break;
              case QtInfoMsg:
                  fprintf(stderr, "Info: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
                  break;
              case QtWarningMsg:
                  fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
                  break;
              case QtCriticalMsg:
                  fprintf(stderr, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
                  break;
              case QtFatalMsg:
                  fprintf(stderr, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);
                  abort();
              }
          }
          
          int main(int argc, char **argv)
          {
              qInstallMessageHandler(myMessageOutput); // Install the handler
              QApplication app(argc, argv);
              ...
              return app.exec();
          }
          

          取自qInstallMsgHandler的文档(我只添加了cmets):

          在上面的例子中,函数myMessageOutput使用了stderr,你可能想用其他文件流替换它,或者完全重写函数!

          编写并安装此函数后,您的所有qDebug(以及qWarningqCritical 等)消息将被重定向到您在处理程序中写入的文件。

          【讨论】:

          • 嘿,非常感谢。它不仅可以让我将调试输出重定向到文件,还可以让我打印更多有用的信息,比如时间戳:)
          • @Septagram:没错。您可以在处理程序本身中添加一些有用的消息;您甚至可以根据您使用的qDebugqWarningqCritical 等将不同的消息输出到不同的文件!
          • 顺便说一句,执行实际输出的回调 - void myMessageOutput(QtMsgType type, const char *msg) - 它以什么编码接收消息?
          • 文档链接和 API 发生了一些变化。 qInstallMsgHandler 在 Qt5 中已弃用并由 qInstallMessageHandler (相同的想法)取代。对于 5.0,qInstallMsgHandler 位于 qt-project.org/doc/qt-5.0/qtcore/…qInstallMessageHandler 也在那里。对于 5.1,qInstallMsgHandler 已完全删除。
          • @Aditya:在 Qt4 中,回调只接受两个参数。所以你可以使用这个:void myMessageOutput(QtMsgType type, const char *msg) { ... }
          猜你喜欢
          • 2014-12-05
          • 2019-02-01
          • 1970-01-01
          • 2016-06-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多