如何重定向 qDebug、 qPolice、 qCritic 等输出?

我在调试输出中使用了大量 qDebug() <<语句。有没有什么跨平台的方法,我可以重定向调试输出到一个文件,而不诉诸于 shell 脚本?我猜测 打开()Dup2()将在 Linux 中完成这项工作,但它能在 Windows 中用 MinGW 编译吗?

也许有一个 QT 方法可以做到这一点?

88771 次浏览

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.

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.

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

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

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.

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.