Tuesday, November 24, 2009

Persistent logging (for debug purposes)

Many of us, Qt developers, resort to qDebug() (and the companion qWarning(), qCritical() and qFatal() functions) to write out debugging and tracing information along the flow of our application.

Most of the time, it's just a question of sending the desired message (a QtMsgType value) to the output stream provided by qDebug():
qDebug() << "Date: " << QDate::currentDate();
Moreover, the streaming operator << can be extended to provide support for custom types, for instance:
QDebug operator<<(QDebug dbg, const Circle &c)
{
    dbg.nospace() << "Radius: " << c.radius();
    return dbg.space();
}

Make it persistent
What if we need to get a copy of all the debug messages thrown by the application during a debugging session?

We can override the default message output handler, and assigning it a file as destination. The following source fragment must be present in main.cpp:
...
#ifndef QT_NO_DEBUG_OUTPUT

QTextStream *out = 0;

void logOutput(QtMsgType type, const char *msg)
{
    QString debugdate = QDateTime::currentDateTime().toString("yyyy.MM.dd hh:mm:ss");
    switch (type)
    {
    case QtDebugMsg:
        debugdate += "[D]";
        break;
    case QtWarningMsg:
        debugdate += "[W]";
        break;
    case QtCriticalMsg:
        debugdate += "[C]";
        break;
    case QtFatalMsg:
        debugdate += "[F]";
    }
    (*out) << debugdate << " " << msg << endl;

    if (QtFatalMsg == type)
    {
        abort();
    }
}
#endif

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

#ifndef QT_NO_DEBUG_OUTPUT
    QString fileName = QCoreApplication::applicationFilePath().replace(".exe", ".log");
    QFile *log = new QFile(fileName);
    if (log->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
        out = new QTextStream(log);
        qInstallMsgHandler(logOutput);
    } else {
        qDebug() << "Error opening log file '" << fileName << "'. All debug output redirected to console.";
    }
#endif
    ...
    ...
}
Now all debug information will be written with a timestamp to a log file called ‘myAppName.log’ in your application's directory. When compiling your final application, be sure to add the flags -DQT_NO_DEBUG and -DQT_NO_DEBUG_OUTPUT. These will reduce the application size and ensure that the log file isn’t used. Alternately, you can keep NO_DEBUG_OUTPUT out of the Makefile if you still want the log file. This solution should suffice for every day's debugging issues. If you're in need for a more robust logging approach, you may want to consider Log4Qt, a C++ port of the Apache Software Foundation log4j package using the Qt framework. Stay tuned for an upcoming review about that tool.

1 comment:

  1. Thanks for your solution of File-Logging by using the normal qDebug in Qt.

    ReplyDelete