What's New in Qt 5.6.0: Logging to syslog and journald

What's New in Qt 5.6.0: Logging to syslog and journald

By Jeff Tranter

It is often desirable to log events from an application, like errors or debug information, to a log file or even to a different computer. A new feature of Qt in version 5.6.0 is the ability to direct the output of the commonly used Qt message logging functions to the standard syslog and journald logging systems available on most Linux platforms.

In this blog post, we'll look at how to use this new facility, including example code. We'll also look at how you can implement this using a custom message handler so you can support different logging back ends, even with versions of Qt older than 5.6.0 The specific code examples will apply to Linux (or UNIX) platforms only, but the concepts apply to other platforms.

Background

Qt provides five message handler functions that you can call to report various types of events. The functions are qDebug(), qInfo(), qWarning(), qCritical() and qFatal(). Each function accepts a format string and a list of arguments similar to the standard C library printf() function, i.e.

    qWarning(const char *message, ...)

 

By default, the output goes to standard error on Linux systems, and the debugger on Windows. You can disable output by defining the relevant symbols during compilation of your application: QT_NO_WARNING_OUTPUT, QT_NO_INFO_OUTPUT and QT_NO_DEBUG_OUTPUT. You can also set the environment variables QT_FATAL_WARNINGS or QT_FATAL_CRITICALS to control whether warnings and critical errors cause your application to exit. In addition, the qFatal() function calls abort() to generate a core dump on UNIX/Linux systems and start the debugger on Windows, if available.

New in Qt 5.6.0 is the ability to automatically send the output of these message functions to the standard Linux syslog or journald logging systems.

Syslog (1) is a logging service originally developed in the 1980s on UNIX systems. It has become a de facto standard, both for UNIX and Linux systems, as well as other operating systems and on network devices, such as routers. It has been standardized by the IETF as RFC 5424.

Journald is a newer logging service that is part of the Systemd (2) init system. Systemd has been adopted by many Linux distributions as a replacement for the older UNIX System V or BSD init systems, although its design has caused some controversy.

As a side note, the QNX RTOS platform provides the slog2 logging framework and Qt on QNX supports this as a back end for logging. Note also that many embedded Linux systems have syslog or journald present or available as an option for logging, so this discussion is applicable on those platforms as well.

Requirements

To enable the syslog or journald logging support in Qt 5.6.0 or later, you need to configure it into Qt. They default to being disabled, so you need to explicitly use the -journald or -syslog options when running the configure script. If successfully enabled, you should see the relevant output of the configure script indicate it as being enabled, as shown below:

  Logging backends: 
    journald ............... yes
    syslog   ............... yes

 

You should only enable one logging back end, corresponding to whichever service your system uses. Otherwise Qt will only log to one of the back ends in an undefined order (in Qt 5.6.0 the check for journald support is made before syslog and overrides it).

A First Example

Let's look at an example of sending messages to syslog. Assuming you are on a Linux-based system such as an Ubuntu desktop and have built Qt 5.6.0 or later with syslog or journald support enabled, we could compile the following example program (3):

/*

Example of logging to syslog or journald.
Requires Qt 5.6.0 or later.
Qt must have been configured with -syslog or -journald options.
Logs typically appear in the file /var/log/syslog.

*/

#include <QtGlobal>
#include <QByteArray>

int main()
{
    // Needed to enable logging to syslog or journald.
    qputenv("QT_LOGGING_TO_CONSOLE", QByteArray("0"));

    // Send some messages, which should go to syslog/journald.

    qDebug("Debug log message from Qt test program");

    qInfo("Info log message from Qt test program");

    qWarning("Warning log message from Qt test program");

    qCritical("Critical log message from Qt test program");

    // Causes program to abort. Uncomment to test.
    //qFatal("Fatal log message from Qt test program");
}

 

The program sends messages using each of the logging functions. After running it, I can see the following log entries appear in the file /var/log/syslog on my Ubuntu 14.04 system:

Mar 31 08:04:30 delllaptop example1: Debug log message from Qt test program
Mar 31 08:04:30 delllaptop example1: Info log message from Qt test program
Mar 31 08:04:30 delllaptop example1: Warning log message from Qt test program
Mar 31 08:04:30 delllaptop example1: Critical log message from Qt test program

 

The output includes the date and time, host name, program name and the log message.

One pitfall to be aware of: the destination of logging depends on an environment variable. If the variable QT_LOGGING_TO_CONSOLE is set to 1, the message functions will always log to the console. If set to 0, they will not log to the console, and will instead log to syslog, if enabled. When the environment variable is not set, the message functions log to a console if one is present (i.e. if the program is attached to a terminal). Thus, to ensure that the output of our example program goes to syslog, I set the environment variable to 0 within the program.

If you use the logging functions for debugging, you will likely want to enable logging to the console during development so you can more easily see them. If you run the test program and see the output on the console, the most likely reason is that your version of Qt was not built with syslog or journald support enabled.

A Second Example

If you want to direct the output of the message functions to another location, Qt has the facility to install a custom message handler. This facility has been in Qt since Qt 5.0.0.

As a second example, let's install a custom message handler that redirects logging to syslog, so we can achieve the same result as the first example using versions of Qt older than Qt 5.6.0. The complete example (3) is below:

/*

Example of custom logging to syslog.
Logs typically appear in the file /var/log/syslog.

*/

#include <cstdio>
#include <syslog.h>
#include <QtGlobal>
#include <QString>
#include <QByteArray>

// Handler for Qt log messages that sends output to syslog as well as standard error.
void SyslogMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    Q_UNUSED(context)

    QByteArray localMsg = msg.toLocal8Bit();
      switch (type) {
      case QtDebugMsg:
          fprintf(stderr, "Debug: %s\n", localMsg.constData());
          syslog(LOG_DEBUG, "Example2 (debug): %s", localMsg.constData());
          break;
      case QtInfoMsg:
          fprintf(stderr, "Info: %s\n", localMsg.constData());
          syslog(LOG_INFO, "Example2 (info): %s", localMsg.constData());
          break;
      case QtWarningMsg:
          fprintf(stderr, "Warning: %s\n", localMsg.constData());
          syslog(LOG_WARNING, "Example2 (warning): %s", localMsg.constData());
          break;
      case QtCriticalMsg:
          fprintf(stderr, "Critical: %s\n", localMsg.constData());
          syslog(LOG_CRIT, "Example2 (critical): %s", localMsg.constData());
          break;
      case QtFatalMsg:
          fprintf(stderr, "Fatal: %s\n", localMsg.constData());
          syslog(LOG_ALERT, "Example2 (alert): %s", localMsg.constData());
          abort();
      }
}

int main()
{
    // Install our message handler.
    qInstallMessageHandler(SyslogMessageHandler);

    // Send some messages, which should go to syslog.

    qDebug("Debug log message from Qt test program");

    qInfo("Info log message from Qt test program");

    qWarning("Warning log message from Qt test program");

    qCritical("Critical log message from Qt test program");

    // Causes program to abort. Uncomment to test.
    //qFatal("Fatal log message from Qt test program");
}


We need to implement a message handler function that accepts the message type, log context, and message string. In our case we call it SyslogMessageHandler and have it send the output to syslog using the appropriate severity level. We also send the message to the standard error output.

Then, in our main program, we need to install it by calling the function qInstallMessageHandler() with a pointer to our custom message handler function.

On my Ubuntu desktop system, after running the example program I see the following entries show up in /var/log/syslog:

Mar 31 08:06:07 delllaptop example2: Example2 (debug): Debug log message from Qt test program
Mar 31 08:06:07 delllaptop example2: Example2 (info): Info log message from Qt test program
Mar 31 08:06:07 delllaptop example2: Example2 (warning): Warning log message from Qt test program
Mar 31 08:06:07 delllaptop example2: Example2 (critical): Critical log message from Qt test program

Summary

I hope you can see from these examples that it is easy to use the logging feature and should be straightforward to implement a custom message handler for a different logging back end or a custom one like appending to a file.

The relevant Qt code that implements logging is in the source file qtbase/src/corelib/global/qlogging.cpp.

Syslog normally writes to a local socket, an operation which is non-blocking and quite fast. If you were to write data faster than the logging system could read it, you could potentially cause the socket's buffer to fill and block the application until the logging system catches up with reading the data. Therefore, you should avoid sending extremely large amounts of data to the logging system.

Syslog can be configured to send logs to different files based on criteria like the severity or source, and even to send messages to another computer. Configuration is documented in the rsyslog.conf man page, but I found that to be a little cryptic. In setting up systems for remote logging, I found these references (4) (5) helpful.

References

  1. Syslog, Wikipedia article, last accessed 4 Apr 2016, en.wikipedia.org/wiki/Syslog
  2. Systemd, Wikipedia article, last accessed 4 Apr 2016, en.wikipedia.org/wiki/Systemd#journald
  3. Source code for two example programs in this blog, ftp://ftp.ics.com/pub/pickup/syslog.zip
  4. Sending Messages to a Remote Syslog Server, rsyslog website, last accessed 4 Apr 2016, www.rsyslog.com/sending-messages-to-a-remote-syslog-server/
  5. Receiving Messages from a Remote System, rsyslog website, last accessed 4 Apr 2016, www.rsyslog.com/receiving-messages-from-a-remote-system/