如何重定向 qDebug、qWarning、qCritical 等输出?

发布于 2024-10-16 21:26:28 字数 389 浏览 12 评论 0原文

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

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

I'm using a lot of qDebug() << statements for debug output. Is there any cross-platform way I can redirect that debug output to a file, without resorting to shell scripts? I'm guessing that open() and dup2() will do the job in Linux, but will it work compiled with MinGW in Windows?

And maybe there is a Qt way to do it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

猫腻 2024-10-23 21:26:28

您必须使用qInstallMessageHandler函数安装消息处理程序,然后,您可以使用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();
}

取自 qInstallMessageHandler 的文档(我只添加了注释):

在上面的示例中,函数 myMessageOutput 使用 stderr,您可能需要将其替换为一些其他文件流,或者完全重写该函数!

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

You've to install a message handler using qInstallMessageHandler function, and then, you can use QTextStream to write the debug message to a file. Here is a sample example:

#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();
}

Taken from the doc of qInstallMessageHandler (I only added the comments):

In the above example, the function myMessageOutput uses stderr which you might want to replace with some other file stream, or completely re-write the function!

Once you write and install this function, all your qDebug (as well as qWarning, qCritical etc) messages would be redirected to the file you're writing to in the handler.

儭儭莪哋寶赑 2024-10-23 21:26:28

这里所有功劳都归于精神

#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);
        abort();
    }
    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();
}

From here all credit goes to 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);
        abort();
    }
    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();
}
暮倦 2024-10-23 21:26:28

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

谢谢@罗斯·罗杰斯!

// -- 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;
}

Here is a working example of hooking the default message handler.

Thank you @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;
}
寻梦旅人 2024-10-23 21:26:28

这是一个跨平台解决方案,如果应用程序是从 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

Here is a cross-platform solution to log to the console, if app was ran from Qt Creator, and to the debug.log file, when it is compiled and being ran as a standalone app.

main.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

Log formatting is handled by QString("%1 %2: %3 (%4)").arg... (for the file) and fprintf(stderr, "%s %s: %s (%s:%u, %s)\n"... (for console).

Inspiration: https://gist.github.com/polovik/10714049.

無處可尋 2024-10-23 21:26:28

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

Well, I would say that the moment when you need to redirect your debug output to anything different than stderr is when you could think about some logging tool. If you feel you need one I would recommend using QxtLogger ("The QxtLogger class is an easy to use, easy to extend logging tool.") from Qxt library.

莫多说 2024-10-23 21:26:28

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

void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message)
{
    static QMutex mutex;
    QMutexLocker lock(&mutex);

    static QFile logFile(LOGFILE_LOCATION);
    static bool logFileIsOpen = logFile.open(QIODevice::Append | QIODevice::Text);

    std::cerr << qPrintable(qFormatLogMessage(type, context, message)) << std::endl;

    if (logFileIsOpen) {
        logFile.write(qFormatLogMessage(type, context, message).toUtf8() + '\n');
        logFile.flush();
    }
}

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

Here's a simple, thread safe idiomatic Qt example to log both to stderr and file:

void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message)
{
    static QMutex mutex;
    QMutexLocker lock(&mutex);

    static QFile logFile(LOGFILE_LOCATION);
    static bool logFileIsOpen = logFile.open(QIODevice::Append | QIODevice::Text);

    std::cerr << qPrintable(qFormatLogMessage(type, context, message)) << std::endl;

    if (logFileIsOpen) {
        logFile.write(qFormatLogMessage(type, context, message).toUtf8() + '\n');
        logFile.flush();
    }
}

Install it with qInstallMessageHandler(messageHandler) as described in other answers.

我喜欢麦丽素 2024-10-23 21:26:28

基于 Autodidact 的答案,进行了一些更改:

  • 该答案包含一个令人讨厌的错误:因为每个 case只设置txt的值,仅在switch后打印,QtFatalMsg在打印前会abort()任何事物。这意味着致命错误不会被记录!我的版本修复了它。
  • 它不是在每次记录消息时打开文件并实例化 QTextStream,而是在程序启动时仅设置一次所有内容。这减少了记录消息的开销,如 Paul Masri-Stone
#include <QApplication>
#include <QtDebug>
#include <QFile>
#include <QTextStream>

static QTextStream output_ts;

void myMessageHandler(QtMsgType type, const QMessageLogContext&, const QString& msg)
{
    switch (type) {
    case QtDebugMsg:
        output_ts << QString("Debug: %1").arg(msg) << endl;
        break;
    case QtWarningMsg:
        output_ts << QString("Warning: %1").arg(msg) << endl;
        break;
    case QtCriticalMsg:
        output_ts << QString("Critical: %1").arg(msg) << endl;
        break;
    case QtFatalMsg:
        output_ts << QString("Fatal: %1").arg(msg) << endl;
        abort();
    }
}

int main(int argc, char* argv[])
{
    QString logfilePath = QStringLiteral("C:\\mydir\\log.txt");
    QFile outFile(logfilePath);
    outFile.open(QIODevice::WriteOnly | QIODevice::Append);
    output_ts.setDevice(&outFile);
    qInstallMessageHandler(messageHandler);
    
    QApplication app(argc, argv);
    
    ...
    return app.exec();
}

Based on Autodidact's answer, with a few changes:

  • That answer contains a nasty bug: since each case only sets the value of txt, which is printed only after the switch, QtFatalMsg will abort() before printing anything. This means that fatal errors won't be logged! My version fixes it.
  • Instead of opening the file and instantiating the QTextStream every time a message is logged, it sets everything up only once, when the program starts. This reduces the overhead of logging a message, as suggested by Paul Masri-Stone in a comment .
#include <QApplication>
#include <QtDebug>
#include <QFile>
#include <QTextStream>

static QTextStream output_ts;

void myMessageHandler(QtMsgType type, const QMessageLogContext&, const QString& msg)
{
    switch (type) {
    case QtDebugMsg:
        output_ts << QString("Debug: %1").arg(msg) << endl;
        break;
    case QtWarningMsg:
        output_ts << QString("Warning: %1").arg(msg) << endl;
        break;
    case QtCriticalMsg:
        output_ts << QString("Critical: %1").arg(msg) << endl;
        break;
    case QtFatalMsg:
        output_ts << QString("Fatal: %1").arg(msg) << endl;
        abort();
    }
}

int main(int argc, char* argv[])
{
    QString logfilePath = QStringLiteral("C:\\mydir\\log.txt");
    QFile outFile(logfilePath);
    outFile.open(QIODevice::WriteOnly | QIODevice::Append);
    output_ts.setDevice(&outFile);
    qInstallMessageHandler(messageHandler);
    
    QApplication app(argc, argv);
    
    ...
    return app.exec();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文