[Development] (long) thoughts on categorized logging (qLog)

Lincoln Ramsay lincoln.ramsay at nokia.com
Wed Feb 22 07:37:20 CET 2012


Hi all,

There seems to be some confusion over what qLog is and is not. I suspect 
that the direction was perhaps mis-aligned slightly. Please allow me to 
try to summarize things. This is more of a vision thing and in writing 
this, parts of the implementation have been identified that should be 
updated. Writing this led to much discussion here, this represents 
something we are happy with and I'm hoping it matches the expectations 
of others too. Note that some different names are being proposed here, 
the changes to functionality suggested the names but as before, they're 
not set in stone yet.

First a mission statement:

The categorized logging feature is intended as a replacement for qDebug 
and qWarning. With logging statements in an app/library, a developer can 
turn on the statements they care about and turn off the ones they don't. 
They can do all this without re-compiling the app and in some cases, 
without restarting the app. The categorized logging feature is intended 
to have as small an impact as possible until it has been explicitly enabled.


The categorized logging feature is made up of several parts.


PART 1: The logging statements.

To keep the code compact (and for performance reasons), categories are 
identified locally by a short (locally-unique) token. The short tokens 
are mapped onto longer, (globally-unique) identifiers. Categories must 
be declared before use.

QT_LOG_CATEGORY(PLUGIN, "com.nokia.qt.plugins")

For a library, I'd expect to see a header that declares the categories 
used by that library but it's also possible to just drop the macro in 
the top of .cpp files as required. (impl detail: might need a "declare" 
and a "implement" macro for libs to avoid duplicate symbols)

The actual logging statements use the category token.

qCDebug(PLUGIN) << "Scanning for plugins in" << paths;
qCWarning(PLUGIN) << "Could not load plugin" << loader->errorString();

As can be seen, we're differentiating between debug (defualt off) and 
warning (default on) statements. To identify these when turning 
categories on/off there is a .debug or .warning suffix added to the 
identifier (more on this later).

It's important to note that logging statements should not have side 
effects because they may not be called.


PART 2: Compatibility with qDebug/qWarning

qDebug() and qWarning() will be re-implemented as categorized logging 
statements. Both will use the same, hard-coded category.

qDebug() << "debug";
qWarning() << "warning";

Is almost equivalent to:

QT_LOG_CATEGORY(LEGACY, "legacy")
qCDebug(LEGACY) << "debug";
qCWarning(LEGACY) << "warning";

I say "almost equivalent" because qDebug() defaults on (while 
categorized debugs default off).

The older qDebug("debug") and qWarning("warning") syntax will also work 
and will be treated the same way.

For display purposes, qFatal will also have a category but it won't be 
influenced by the logging configuration.


PART 3: Turning statements on/off.

As mentioned, debug statements (except for qDebug) default off and 
warning statements default on. Logging statements can be enabled or 
disabled using rules. Rules specify a category identifier, optionally 
using a global-style * to match multiple categories.

eg.
# Turn on Qt plugin debug
com.nokia.qt.plugins.debug = true
# Turn off network warnings
com.nokia.qt.network.warning = false
# Turn on all webkit debug
org.webkit.*.debug = true
# Turn off all legacy qDebug/qWarning
legacy.* = false

For apps that want to provide a UI for logging and for future expansion 
possibilities (eg. add-ons that allow turning logging on/off via some 
novel means), there will be a C++ API to set the rules.

qSetCategoryRules(const QByteArray &rules);

However, since there are likley to be many apps that don't care about 
logging (or may not expose a way to turn on logging in a third party 
library) there will also be a text-based config file that the user can 
create. Where should this file be located? Either the app sets the 
location of this file by calling qSetLoggingConfigFile() or the user 
sets the QT_LOGGING_CONFIG environment variable (which overrides the 
app's default). If the config file exists, the C++ API has no effect. 
QFileWatcher will be used to monitor the config file so that categories 
can be turned on/off while the program is running by updating the file.


PART 4: Formatting.

There's a %{category} entry that can appear in the format string. This 
will be in the default format string.


PART 5: Saving logs to a file.

As a special case, the user can direct enabled messages to a file. This 
is done from the config file.

# write output to a file (in the user's home directory)
logging.output.file = file.txt
# write output to a specific file
logging.output.file = /path/to/file.txt
# write output.file to a specific file on Windows
logging.output.file = c:\log.txt

# this is the default (output goes to message handler or file, not both)
logging.output.both = false
# write to both the file and the message handler
logging.output.both = true

# for messages being written into the file, overwrite the formatting string
logging.output.format = "%{category}: %{message}"

Ideally, this would exist in an add-on. In this case we feel it is 
justified to build this into the library due to the difficulty of 
collecting logs from apps on different platforms.



-- 
Lincoln Ramsay - Senior Software Engineer
Qt Development Frameworks, Nokia - http://qt.nokia.com/



More information about the Development mailing list