Qt Weekly #1: Categorized Logging

Categorized logging allows you to structure your logging statements into categories, and to conveniently configure logging for individual categories in a central place. The feature is part of Qt 5.2, but will get some convenient extensions in Qt 5.3.

Since the dawn of times Qt has had it's own little logging helpers - qDebug(), qWarning(), qCritical(), and qFatal(). Being part of QtCore, they are ubiquitous, also in the Qt code itself. Anyhow, the problem with logging is often enough that you're doing too much of it (you get flooded with output), too few (you do not get enough to e.g. debug a problem), or both ... In Qt 5.2 we started to address this by letting you split your logging messages into "categories", and with Qt 5.3 there will finally be an easy, default way to enable output for these rules, through a configuration file.

Logging Categories

Let's have a look at an example. Say you're working on an awsome new game named "FlappyCat", which features a high score:

bool Highscore::load(const QString &filepath)
{
    QLoggingCategory fcIo("fc.io");
    qCDebug(fcIo) << "Loading highscore ...";

QFile file(filepath); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qCWarning(fcIo) << "Could not open file " << filepath << "!"; return; } // ... }

which prints:

fc.io: Loading highscore ...
fc.io: Could not open file  "highscore.xml" !

So, what's happening here?

  • QLoggingCategory represents a category in code, in this case it's "fc.io". It's not a one to one mapping though ... you can have multiple instances representing the same category.
  • qCDebug, qCWarning, and qCCritical allow you to log into a specific category. They also check whether the category has been enabled or not before assembling the message, which ensures adding logging statements is cheap when not used (which is the reason we could not just overload qDebug() and friends).
  • The default QtMessageHandler prints the logging category in front of the message.

Alternatively, we could also define all categories in one place:

fclogging.h:

#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(fcIo)
Q_DECLARE_LOGGING_CATEGORY(fcRender)
Q_DECLARE_LOGGING_CATEGORY(fcLogic)

fclogging.cpp:

#include "fclogging.h"
Q_LOGGING_CATEGORY(fcIo, "fc.io")
Q_LOGGING_CATEGORY(fcRender, "fc.render")
Q_LOGGING_CATEGORY(fcLogic, "fc.logic")

Q_DECLARE_LOGGING_CATEGORY and Q_LOGGING_CATEGORY are macros that let you conveniently define global categories.

Logging Rules

So how can you tweak the categories to suppress or enable individual levels? Let's have a look at main.cpp:

int main(int argc, char **argv)
{
    QGuiApplication app(argc, argv);
    QLoggingCategory::setFilterRules("*.debug=false\n"
                                     "fc.io.debug=true");
    // ...
}

Logging rules are defined as free text, and set e.g. by calling setFilterRules(). In this case the first filter disabled any QtDebugMsg output, for all categories. But the second rule overwrites this for the "fc.io" category.

Logging Configuration File (5.3 addition!)

However, it's still inconvenient to recompile your application, just to set the rules. In Qt 5.3 you will therefore be able to set a logging configuration file, too.

~/.config/QtProject/qtlogging.ini:

[rules]
*.debug=false
fc.io.debug=true

The logging configuration file is looked up in different locations depending on the platform. Another convenient addition in Qt 5.3 will be that you can use categorized logging also in a printf-style way:

qCDebug(fcIo, "Loading highscore ...");

Check out the documentation in Qt 5.3, once it becomes available !

Qt Weekly is a new blog post series that aims to give your daily Qt usage a boost. Stay tuned for next week's post!


Blog Topics:

Comments