Table of Contents:

Logging debug and informational output from libraries and programs is an open problem, and there are various methods for converting multiple streams of log output into the customary stdout and stderr streams. Below are some suggestions for how to implement logging. However, the most important thing is to ensure that logging is consistent, so that log data can be accessed and searched with a minimum of effort, since that’s what it’s used for. Using different logging mechanisms and formats in different projects is not the right approach.

Summary

GLib logging framework

GLib provides a logging framework based around the g_log() function, with convenience wrappers g_debug(), g_message(), g_warning() and g_error(). The GLib logging framework has a few useful features:

  • Programmatic redirection of log messages using g_log_set_handler().
  • Multiple logging domains, which can be processed separately.
  • Multiple log levels, which can be processed separately. For example, this allows debug messages to be turned on and off at runtime.
  • Support for automatically aborting a program on ‘fatal’ messages.

These should be used in preference to functions like printf(), g_print() and g_printerr(), due to their enhanced flexibility. The logging functions allow log processing to be done in code, rather than by external shell scripting, which simplifies everything.

A key reason to use the logging framework is that it is used in GLib and other related libraries already; by using it, all log messages are then going through the same system and can be processed similarly.

To use the GLib logging framework, define G_LOG_DOMAIN for each library so they’re all unique. Call g_debug("Message") to log a debug message.

If the default GLib log handlers are not sufficient, for example if log messages need to be in a custom format or journald integration is needed, set up a log handler with the following code. Note that ‘‘consistency of logging should be considered very important’’, so if one module uses a custom log handler, all other modules should use the same one, linked in from a common utility library.

The following code should not be copied and pasted, it is an example for reference only.

static const gchar *
log_level_to_string (GLogLevelFlags level)
{
  switch (level)
    {
      case G_LOG_LEVEL_ERROR: return "ERROR";
      case G_LOG_LEVEL_CRITICAL: return "CRITICAL";
      case G_LOG_LEVEL_WARNING: return "WARNING";
      case G_LOG_LEVEL_MESSAGE: return "MESSAGE";
      case G_LOG_LEVEL_INFO: return "INFO";
      case G_LOG_LEVEL_DEBUG: return "DEBUG";
      default: return "UNKNOWN";
    }
}

static void
log_handler_cb (const gchar *log_domain,
                GLogLevelFlags log_level,
                const gchar *message,
                gpointer user_data)
{
  const gchar *log_level_str;

  /* Ignore debug messages if disabled. */
  if (!debug_enabled && (log_level & G_LOG_LEVEL_DEBUG))
    {
      return;
    }

  log_level_str = log_level_to_string (log_level & G_LOG_LEVEL_MASK);

  /* Use g_printerr() for warnings and g_print() otherwise. */
  if (flags <= G_LOG_LEVEL_WARNING)
    {
      g_printerr ("%s: %s: %s\n", log_domain, log_level_str, message);
    }
  else
    {
      g_print ("%s: %s: %s\n", log_domain, log_level_str, message);
    }
}

g_log_set_handler ("log-domain", G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL
                   | G_LOG_FLAG_RECURSION, log_handler_cb, NULL);

Exceptions

  • Do not use g_message() in normal code to print output. Printing output should be done at the top level of an application, using g_print(), and should be quite rare; i.e. only done in command line applications.
  • Do not use g_warning() in library code. Use GError instead.
  • Similarly, do not set up log handlers in library code. Log messages should propagate through library code and be handled in a log handler at the top level of an application.

journald integration

Compared to conventional syslog-style logs, journald supports storage of structured logging data, which can make post-hoc analysis of logs much easier. If it’s possible to add systemd-journal as a dependency to a project, the project’s log handling function could be extended to use sd_journal_print() and sd_journal_send() instead of g_print() and g_printerr().

For more information, see this article on logging to the journal.

Log rotation

Log file rotation is one feature which is out of scope of the GLib logging system. It should be handled by the normal system logging mechanisms. On the Apertis platform, systemd-journald automatically carries out log rotation.