How to redirect qDebug, qWarning, qCritical etc output?

C++DebuggingQtMingwQdebug

C++ Problem Overview


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?

C++ Solutions


Solution 1 - C++

You've to install a message handler using qInstallMsgHandler 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 qInstallMsgHandler (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.

Solution 2 - C++

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

Solution 3 - C++

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

Solution 4 - C++

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.

Solution 5 - C++

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.

Solution 6 - C++

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSeptagramView Question on Stackoverflow
Solution 1 - C++NawazView Answer on Stackoverflow
Solution 2 - C++AutodidactView Answer on Stackoverflow
Solution 3 - C++AndrewView Answer on Stackoverflow
Solution 4 - C++NeurotransmitterView Answer on Stackoverflow
Solution 5 - C++Piotr DobrogostView Answer on Stackoverflow
Solution 6 - C++mrtsView Answer on Stackoverflow