|
GnuCash 2.3.0
|
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) |
Logging and tracing facility.
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.
Code should:
static QofLogModule log_module and #define G_LOG_DOMAIN to the same value. "gnc.gui.plugin-pages.sx-list" or "gnc.register.gnome.cell.quickfill" are good examples. g_debug(...), g_message(...), g_warning(...), g_critical(...) and g_error(...) functions in preference to the historical qof/gnc PINFO, PERR (&c.) macros| #define DEBUG | ( | format, | |
| args... | |||
| ) |
| #define ENTER | ( | format, | |
| args... | |||
| ) |
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
| #define FATAL | ( | format, | |
| args... | |||
| ) |
| #define gnc_leave_return_if_fail | ( | test | ) |
| #define gnc_leave_return_val_if_fail | ( | test, | |
| val | |||
| ) |
| #define LEAVE | ( | format, | |
| args... | |||
| ) |
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.
| #define LOG_LEVEL_LIST | ( | _ | ) |
| #define PERR | ( | format, | |
| args... | |||
| ) |
| #define PINFO | ( | format, | |
| args... | |||
| ) |
| #define PWARN | ( | format, | |
| args... | |||
| ) |
| 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 | ) |
| void qof_log_indent | ( | void | ) |
| 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.
{
qof_log_init_filename(NULL);
}
| 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 | ) |
| 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;
}
}
1.7.4