GnuCash 2.3.0
Defines | Enumerations | Functions
Logging
Query Object Framework

Logging and tracing facility. More...

Defines

#define QOF_MOD_ENGINE   "qof.engine"
#define LOG_LEVEL_LIST(_)
#define PRETTY_FUNC_NAME   qof_log_prettify(G_STRFUNC)
#define FATAL(format, args...)
#define PERR(format, args...)
#define PWARN(format, args...)
#define PINFO(format, args...)
#define DEBUG(format, args...)
#define ENTER(format, args...)
#define LEAVE(format, args...)
#define gnc_leave_return_val_if_fail(test, val)
#define gnc_leave_return_if_fail(test)

Enumerations

enum  QofLogLevel {
  QOF_LOG_FATAL = G_LOG_LEVEL_ERROR, QOF_LOG_ERROR = G_LOG_LEVEL_CRITICAL, QOF_LOG_WARNING = G_LOG_LEVEL_WARNING, QOF_LOG_MESSAGE = G_LOG_LEVEL_MESSAGE,
  QOF_LOG_INFO = G_LOG_LEVEL_INFO, QOF_LOG_DEBUG = G_LOG_LEVEL_DEBUG
}

Functions

gchar * qof_log_level_to_string (QofLogLevel lvl)
QofLogLevel qof_log_level_from_string (const gchar *str)
void qof_log_indent (void)
void qof_log_dedent (void)
void qof_log_init (void)
void qof_log_set_level (QofLogModule module, QofLogLevel level)
void qof_log_set_file (FILE *outfile)
void qof_log_init_filename (const gchar *logfilename)
void qof_log_init_filename_special (const char *log_to_filename)
void qof_log_parse_log_config (const char *filename)
void qof_log_shutdown (void)
const gchar * qof_log_prettify (const gchar *name)
gboolean qof_log_check (QofLogModule log_module, QofLogLevel log_level)
void qof_log_set_default (QofLogLevel log_level)

Detailed Description

Logging and tracing facility.

See also:
"Logging overhaul" announcement <http://lists.gnucash.org/pipermail/gnucash-devel/2007-February/019836.html>

qof_log_init(void) installs a handler that interprets the "log_domain" as a "."-separated path. Log level thresholds can be set for each level in the tree. When a message is logged, the longest level match is found, and used as the threshold.

For instance, we can set the levels as such:

   "qof"                        = WARN
   "gnc"                        = WARN
   "gnc.ui"                     = INFO
   "gnc.ui.plugin-page.sx-list" = DEBUG
 

When code in the log_module of "gnc.import" attempts to log at DEBUG (let's say), the handler will attempt to match the log domain to successively-longer paths: first "", then "gnc", then "gnc.import". Given the settings above, the path "gnc" will match -- at a level of "WARN" -- and the DEBUG-level log will be rejected. When code in the log domain of "gnc.ui.plugin-page.sx-list" logs at DEBUG, however, it will match at DEBUG, and be allowed.

The current log format is as above:

     * [timestamp] [level] <[log-domain]> [message]
 

The timestamp and level are constant width (level is 5 characters). The log domain is re-iterated, which gives some context, but could become annoying if they get long.

Trailing newlines (e.g. PINFO("...\n", ...)) are removed; the logger will newline separate output.

Best Practices

Code should:

See also:
qof_log_parse_log_config(const char*)

Define Documentation

#define DEBUG (   format,
  args... 
)
Value:
do { \
    g_log (log_module, G_LOG_LEVEL_DEBUG, \
      "[%s] " format, PRETTY_FUNC_NAME , ## args); \
} while (0)

Print a debugging message

Definition at line 250 of file qoflog.h.

#define ENTER (   format,
  args... 
)
Value:
do { \
    if (qof_log_check(log_module, (QofLogLevel)G_LOG_LEVEL_DEBUG)) { \
      g_log (log_module, G_LOG_LEVEL_DEBUG, \
        "[enter %s:%s()] " format, __FILE__, \
        PRETTY_FUNC_NAME , ## args); \
      qof_log_indent(); \
    } \
} while (0)

Print a function entry debugging message

Definition at line 256 of file qoflog.h.

#define FATAL (   format,
  args... 
)
Value:
do { \
    g_log (log_module, G_LOG_LEVEL_ERROR, \
      "[%s()] " format, PRETTY_FUNC_NAME , ## args); \
} while (0)

Log a fatal error

Definition at line 226 of file qoflog.h.

#define gnc_leave_return_if_fail (   test)
Value:
do { \
  if (! (test)) { LEAVE(""); } \
  g_return_if_fail(test); \
} while (0);

Replacement for g_return_if_fail, but calls LEAVE if the test fails.

Definition at line 284 of file qoflog.h.

#define gnc_leave_return_val_if_fail (   test,
  val 
)
Value:
do { \
  if (! (test)) { LEAVE(""); } \
  g_return_val_if_fail(test, val); \
} while (0);

Replacement for g_return_val_if_fail, but calls LEAVE if the test fails.

Definition at line 278 of file qoflog.h.

#define LEAVE (   format,
  args... 
)
Value:
do { \
    if (qof_log_check(log_module, (QofLogLevel)G_LOG_LEVEL_DEBUG)) { \
      qof_log_dedent(); \
      g_log (log_module, G_LOG_LEVEL_DEBUG, \
        "[leave %s()] " format, \
        PRETTY_FUNC_NAME , ## args); \
    } \
} while (0)

Print a function exit debugging message.

Definition at line 266 of file qoflog.h.

#define LOG_LEVEL_LIST (   _)
Value:
_(QOF_LOG_FATAL,   = G_LOG_LEVEL_ERROR)   \
  _(QOF_LOG_ERROR,   = G_LOG_LEVEL_CRITICAL)   \
  _(QOF_LOG_WARNING, = G_LOG_LEVEL_WARNING) \
  _(QOF_LOG_MESSAGE, = G_LOG_LEVEL_MESSAGE) \
  _(QOF_LOG_INFO,    = G_LOG_LEVEL_INFO)    \
  _(QOF_LOG_DEBUG,   = G_LOG_LEVEL_DEBUG)

Definition at line 94 of file qoflog.h.

#define PERR (   format,
  args... 
)
Value:
do { \
    g_log (log_module, G_LOG_LEVEL_CRITICAL, \
      "[%s()] " format, PRETTY_FUNC_NAME , ## args); \
} while (0)

Log a serious error

Definition at line 232 of file qoflog.h.

#define PINFO (   format,
  args... 
)
Value:
do { \
    g_log (log_module, G_LOG_LEVEL_INFO, \
      "[%s] " format, PRETTY_FUNC_NAME , ## args); \
} while (0)

Print an informational note

Definition at line 244 of file qoflog.h.

#define PWARN (   format,
  args... 
)
Value:
do { \
    g_log (log_module, G_LOG_LEVEL_WARNING, \
      "[%s()] " format, PRETTY_FUNC_NAME , ## args); \
} while (0)

Log a warning

Definition at line 238 of file qoflog.h.


Function Documentation

gboolean qof_log_check ( QofLogModule  log_module,
QofLogLevel  log_level 
)

Check to see if the given log_module is configured to log at the given log_level. This implements the "log.path.hierarchy" logic.

Definition at line 357 of file qoflog.c.

{
//#define _QLC_DBG(x) x
#define _QLC_DBG(x)
    GHashTable *log_levels = log_table;
    gchar *domain_copy = g_strdup(log_domain == NULL ? "" : log_domain);
    gchar *dot_pointer = domain_copy;
    static const QofLogLevel default_log_thresh = QOF_LOG_WARNING;
    QofLogLevel longest_match_level = default_log_thresh;

    {
        gpointer match_level;
        if ((match_level = g_hash_table_lookup(log_levels, "")) != NULL)
            longest_match_level = (QofLogLevel)GPOINTER_TO_INT(match_level);
    }

    _QLC_DBG( { printf("trying [%s] (%d):", log_domain, g_hash_table_size(log_levels)); });
    if (G_LIKELY(log_levels))
    {
        // e.g., "a.b.c\0" -> "a\0b.c\0" -> "a.b\0c\0", "a.b.c\0"
        gpointer match_level;
        while ((dot_pointer = g_strstr_len(dot_pointer, strlen(dot_pointer), ".")) != NULL)
        {
            *dot_pointer = '\0';
            _QLC_DBG( { printf(" [%s]", domain_copy); });
            if (g_hash_table_lookup_extended(log_levels, domain_copy, NULL, &match_level))
            {
                longest_match_level = (QofLogLevel)GPOINTER_TO_INT(match_level);
                _QLC_DBG(printf("*"););
            }
            *dot_pointer = '.';
            dot_pointer++;
        }

        _QLC_DBG( { printf(" [%s]", domain_copy); });
        if (g_hash_table_lookup_extended(log_levels, domain_copy, NULL, &match_level))
        {
            longest_match_level = (QofLogLevel)GPOINTER_TO_INT(match_level);
            _QLC_DBG( { printf("*"); });
        }
    }
    _QLC_DBG( { printf(" found [%d]\n", longest_match_level); });
    g_free(domain_copy);

    return log_level <= longest_match_level;
}
void qof_log_dedent ( void  )

De-dent one level, capped at 0; see LEAVE macro.

Definition at line 70 of file qoflog.c.

{
    qof_log_num_spaces
    = (qof_log_num_spaces < QOF_LOG_INDENT_WIDTH)
      ? 0
      : qof_log_num_spaces - QOF_LOG_INDENT_WIDTH;
}
void qof_log_indent ( void  )

Indents one level; see ENTER macro.

Definition at line 64 of file qoflog.c.

{
    qof_log_num_spaces += QOF_LOG_INDENT_WIDTH;
}
void qof_log_init ( void  )

Initialize the error logging subsystem. Defaults to a level-threshold of "warning", and logging to stderr.

Definition at line 90 of file qoflog.c.

void qof_log_init_filename ( const gchar *  logfilename)

Specify a filename for log output.

Definition at line 140 of file qoflog.c.

{
    gboolean warn_about_missing_permission = FALSE;
    if (log_table == NULL)
        log_table = g_hash_table_new_full(g_str_hash, g_str_equal,
                                          g_free, NULL);

    if (log_filename)
    {
        int fd;
        gchar *fname;

        if (fout != NULL && fout != stderr && fout != stdout)
            fclose(fout);

        fname = g_strconcat(log_filename, ".XXXXXX.log", NULL);

        if ((fd = g_mkstemp(fname)) != -1)
        {
#ifdef _MSC_VER
            /* MSVC compiler: Somehow the OS thinks file descriptor from above
             * still isn't open. So we open normally with the file name and that's it. */
            fout = fopen(fname, "wb");
#else
            /* We must not overwrite /dev/null */
            g_assert(safe_strcmp(log_filename, "/dev/null") != 0);

            /* Windows prevents renaming of open files, so the next command silently fails there
             * No problem, the filename on Winows will simply have the random characters */
            g_rename(fname, log_filename);
            fout = fdopen(fd, "w");
#endif
            if (!fout)
                warn_about_missing_permission = TRUE;
        }
        else
        {
            warn_about_missing_permission = TRUE;
            fout = stderr;
        }
        g_free(fname);
    }

    if (!fout)
        fout = stderr;

    // @@fixme really, the userdata is a struct { log_table, fout, previous_handler }
    if (previous_handler == NULL)
        previous_handler = g_log_set_default_handler(log4glib_handler, log_table);

    if (warn_about_missing_permission)
    {
        g_critical("Cannot open log output file \"%s\", using stderr.", log_filename);
    }
}
void qof_log_init_filename_special ( const char *  log_to_filename)

If log_to_filename is "stderr" or "stdout" (exactly, case-insensitive), then those special files are used; otherwise, the literal filename as given, as qof_log_init_filename(gchar*)

Definition at line 268 of file qoflog.c.

{
    if (g_ascii_strcasecmp("stderr", log_to_filename) == 0)
    {
        qof_log_init();
        qof_log_set_file(stderr);
    }
    else if (g_ascii_strcasecmp("stdout", log_to_filename) == 0)
    {
        qof_log_init();
        qof_log_set_file(stdout);
    }
    else
    {
        qof_log_init_filename(log_to_filename);
    }
}
void qof_log_parse_log_config ( const char *  filename)

Parse a log-configuration file. A GKeyFile-format file of the schema:

    [levels]
    # log.ger.path=level
    gnc.engine.sx=debug
    gnc.gui.sx=debug
    gnc.import-export.qif.parse=debug
    [output]
    # to=["stderr"|"stdout"|filename]
    to=stderr
 

Definition at line 287 of file qoflog.c.

{
    const gchar *levels_group = "levels", *output_group = "output";
    GError *err = NULL;
    GKeyFile *conf = g_key_file_new();

    if (!g_key_file_load_from_file(conf, filename, G_KEY_FILE_NONE, &err))
    {
        g_warning("unable to parse [%s]: %s", filename, err->message);
        g_error_free(err);
        return;
    }

    g_debug("parsing log config from [%s]", filename);
    if (g_key_file_has_group(conf, levels_group))
    {
        gsize num_levels;
        int key_idx;
        gchar **levels;

        levels = g_key_file_get_keys(conf, levels_group, &num_levels, NULL);

        for (key_idx = 0; key_idx < num_levels && levels[key_idx] != NULL; key_idx++)
        {
            QofLogLevel level;
            gchar *logger_name = NULL, *level_str = NULL;

            logger_name = g_strdup(levels[key_idx]);
            level_str = g_key_file_get_string(conf, levels_group, logger_name, NULL);
            level = qof_log_level_from_string(level_str);

            g_debug("setting log [%s] to level [%s=%d]", logger_name, level_str, level);
            qof_log_set_level(logger_name, level);

            g_free(logger_name);
            g_free(level_str);
        }
        g_strfreev(levels);
    }

    if (g_key_file_has_group(conf, output_group))
    {
        gsize num_outputs;
        int output_idx;
        gchar **outputs;

        outputs = g_key_file_get_keys(conf, output_group, &num_outputs, NULL);
        for (output_idx = 0; output_idx < num_outputs && outputs[output_idx] != NULL; output_idx++)
        {
            gchar *key = outputs[output_idx];
            gchar *value;

            if (g_ascii_strcasecmp("to", key) != 0)
            {
                g_warning("unknown key [%s] in [outputs], skipping", key);
                continue;
            }

            value = g_key_file_get_string(conf, output_group, key, NULL);
            g_debug("setting [output].to=[%s]", value);
            qof_log_init_filename_special(value);
            g_free(value);
        }
        g_strfreev(outputs);
    }

    g_key_file_free(conf);
}
const gchar* qof_log_prettify ( const gchar *  name)

Cleans up subroutine names. AIX/xlC has the habit of printing signatures not names; clean this up. On other operating systems, truncate name to QOF_LOG_MAX_CHARS chars.

void qof_log_set_default ( QofLogLevel  log_level)

Set the default level for QOF-related log paths.

Definition at line 405 of file qoflog.c.

{
    qof_log_set_level("", log_level);
    qof_log_set_level("qof", log_level);
}
void qof_log_set_file ( FILE *  outfile)

Specify an alternate log output, to pipe or file.

Definition at line 79 of file qoflog.c.

{
    if (!outfile)
    {
        fout = stderr;
        return;
    }
    fout = outfile;
}
void qof_log_set_level ( QofLogModule  module,
QofLogLevel  level 
)

Set the logging level of the given log_module.

Definition at line 225 of file qoflog.c.

{
    if (!log_module || level == 0)
    {
        return;
    }
    if (!log_table)
    {
        log_table = g_hash_table_new(g_str_hash, g_str_equal);
    }
    g_hash_table_insert(log_table, g_strdup((gchar*)log_module), GINT_TO_POINTER((gint)level));
}
void qof_log_shutdown ( void  )

Be nice, close the logfile if possible.

Definition at line 197 of file qoflog.c.

{
    if (fout && fout != stderr && fout != stdout)
    {
        fclose(fout);
        fout = NULL;
    }

    if (function_buffer)
    {
        g_free(function_buffer);
        function_buffer = NULL;
    }

    if (log_table != NULL)
    {
        g_hash_table_destroy(log_table);
        log_table = NULL;
    }

    if (previous_handler != NULL)
    {
        g_log_set_default_handler(previous_handler, NULL);
        previous_handler = NULL;
    }
}
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines