GnuCash  4.8a-176-g88ecf8dd1
Macros | Typedefs | Enumerations | Functions


Logging and tracing facility. More...

Macros

#define QOF_MOD_ENGINE   "qof.engine"
 
#define PRETTY_FUNC_NAME   qof_log_prettify(G_STRFUNC)
 
#define FATAL(format, args...)
 Log a fatal error. More...
 
#define PERR(format, args...)
 Log a serious error. More...
 
#define PWARN(format, args...)
 Log a warning. More...
 
#define PINFO(format, args...)
 Print an informational note. More...
 
#define DEBUG(format, args...)
 Print a debugging message. More...
 
#define ENTER(format, args...)
 Print a function entry debugging message. More...
 
#define LEAVE(format, args...)
 Print a function exit debugging message. More...
 
#define gnc_leave_return_val_if_fail(test, val)
 Replacement for g_return_val_if_fail, but calls LEAVE if the test fails. More...
 
#define gnc_leave_return_if_fail(test)
 Replacement for g_return_if_fail, but calls LEAVE if the test fails. More...
 

Typedefs

typedef const gchar * QofLogModule
 

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

const char * qof_log_level_to_string (QofLogLevel lvl)
 
QofLogLevel qof_log_level_from_string (const char *str)
 
void qof_log_indent (void)
 Indents one level; see ENTER macro. More...
 
void qof_log_dedent (void)
 De-dent one level, capped at 0; see LEAVE macro.
 
void qof_log_init (void)
 Initialize the error logging subsystem. More...
 
void qof_log_set_level (QofLogModule module, QofLogLevel level)
 Set the logging level of the given log_module. More...
 
void qof_log_set_file (FILE *outfile)
 Specify an alternate log output, to pipe or file. More...
 
void qof_log_init_filename (const gchar *logfilename)
 Specify a filename for log output. More...
 
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*)
 
void qof_log_parse_log_config (const char *filename)
 
Parse a log-configuration file. More...
 
void qof_log_shutdown (void)
 Be nice, close the logfile if possible. More...
 
const gchar * qof_log_prettify (const gchar *name)
 Cleans up subroutine names. More...
 
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. More...
 

Detailed Description


Logging and tracing facility.

See also
"Logging overhaul" announcement https://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*)

Macro Definition Documentation

◆ DEBUG

#define DEBUG (   format,
  args... 
)
Value:
do { \
if (qof_log_check(log_module, QOF_LOG_DEBUG)) { \
g_log (log_module, G_LOG_LEVEL_DEBUG, \
"[%s] " format, PRETTY_FUNC_NAME , ## args); \
} \
} while(0)
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. ...
Definition: qoflog.cpp:328

Print a debugging message.

Definition at line 264 of file qoflog.h.

◆ ENTER

#define ENTER (   format,
  args... 
)
Value:
do { \
if (qof_log_check(log_module, QOF_LOG_DEBUG)) { \
g_log (log_module, G_LOG_LEVEL_DEBUG, \
"[enter %s:%s()] " format, __FILE__, \
PRETTY_FUNC_NAME , ## args); \
qof_log_indent(); \
} \
} while (0)
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. ...
Definition: qoflog.cpp:328

Print a function entry debugging message.

Definition at line 272 of file qoflog.h.

◆ FATAL

#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 238 of file qoflog.h.

◆ gnc_leave_return_if_fail

#define gnc_leave_return_if_fail (   test)
Value:
do { \
if (! (test)) { LEAVE(""); } \
g_return_if_fail(test); \
} while (0);
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282

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

Definition at line 300 of file qoflog.h.

◆ gnc_leave_return_val_if_fail

#define gnc_leave_return_val_if_fail (   test,
  val 
)
Value:
do { \
if (! (test)) { LEAVE(""); } \
g_return_val_if_fail(test, val); \
} while (0);
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282

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

Definition at line 294 of file qoflog.h.

◆ LEAVE

#define LEAVE (   format,
  args... 
)
Value:
do { \
if (qof_log_check(log_module, QOF_LOG_DEBUG)) { \
qof_log_dedent(); \
g_log (log_module, G_LOG_LEVEL_DEBUG, \
"[leave %s()] " format, \
PRETTY_FUNC_NAME , ## args); \
} \
} while (0)
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. ...
Definition: qoflog.cpp:328

Print a function exit debugging message.

Definition at line 282 of file qoflog.h.

◆ PERR

#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 244 of file qoflog.h.

◆ PINFO

#define PINFO (   format,
  args... 
)
Value:
do { \
if (qof_log_check(log_module, QOF_LOG_INFO)) { \
g_log (log_module, G_LOG_LEVEL_INFO, \
"[%s] " format, PRETTY_FUNC_NAME , ## args); \
} \
} while (0)
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. ...
Definition: qoflog.cpp:328

Print an informational note.

Definition at line 256 of file qoflog.h.

◆ PWARN

#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 250 of file qoflog.h.

Function Documentation

◆ qof_log_check()

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 328 of file qoflog.cpp.

329 {
330 // Check the global levels
331  if (level > current_max)
332  return FALSE;
333  if (level <= default_level)
334  return TRUE;
335  auto module = get_modules();
336  // If the level <= the default then no need to look further.
337  if (level <= module->m_level)
338  return TRUE;
339 
340  if (!domain)
341  return FALSE;
342 
343  auto domain_vec = split_domain(domain);
344 
345  for (auto part : domain_vec)
346  {
347  auto iter = std::find_if(module->m_children.begin(),
348  module->m_children.end(),
349  [part](auto& child) {
350  return child && part == child->m_name; });
351 
352  if (iter == module->m_children.end())
353  return FALSE;
354 
355  if (level <= (*iter)->m_level)
356  return TRUE;
357 
358  module = iter->get();
359  }
360  return FALSE;
361 }

◆ qof_log_indent()

void qof_log_indent ( void  )

Indents one level; see ENTER macro.

Definition at line 134 of file qoflog.cpp.

135 {
136  qof_log_num_spaces += QOF_LOG_INDENT_WIDTH;
137 }

◆ qof_log_init()

void qof_log_init ( void  )

Initialize the error logging subsystem.

Defaults to a level-threshold of "warning", and logging to stderr.

Definition at line 160 of file qoflog.cpp.

161 {
162  qof_log_init_filename(NULL);
163 }
void qof_log_init_filename(const gchar *log_filename)
Specify a filename for log output.
Definition: qoflog.cpp:211

◆ qof_log_init_filename()

void qof_log_init_filename ( const gchar *  logfilename)

Specify a filename for log output.

Definition at line 211 of file qoflog.cpp.

212 {
213  gboolean warn_about_missing_permission = FALSE;
214  auto modules = get_modules();
215 
216  if (!qof_logger_format)
217  qof_logger_format = g_strdup ("* %s %*s <%s> %*s%s%s"); //default format
218 
219  if (log_filename)
220  {
221  int fd;
222  gchar *fname;
223 
224  if (fout != NULL && fout != stderr && fout != stdout)
225  fclose(fout);
226 
227  fname = g_strconcat(log_filename, ".XXXXXX.log", nullptr);
228 
229  if ((fd = g_mkstemp(fname)) != -1)
230  {
231 #if PLATFORM(WINDOWS)
232  /* MSVC compiler: Somehow the OS thinks file descriptor from above
233  * still isn't open. So we open normally with the file name and that's it. */
234  fout = g_fopen(fname, "wb");
235 #else
236  /* We must not overwrite /dev/null */
237  g_assert(g_strcmp0(log_filename, "/dev/null") != 0);
238 
239  /* Windows prevents renaming of open files, so the next command silently fails there
240  * No problem, the filename on Winows will simply have the random characters */
241  g_rename(fname, log_filename);
242  fout = fdopen(fd, "w");
243 #endif
244  if (!fout)
245  warn_about_missing_permission = TRUE;
246  }
247  else
248  {
249  warn_about_missing_permission = TRUE;
250  fout = stderr;
251  }
252  g_free(fname);
253  }
254 
255  if (!fout)
256  fout = stderr;
257 
258  if (previous_handler == NULL)
259  previous_handler = g_log_set_default_handler(log4glib_handler, modules);
260 
261  if (warn_about_missing_permission)
262  {
263  g_critical("Cannot open log output file \"%s\", using stderr.", log_filename);
264  }
265 }

◆ qof_log_parse_log_config()

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 419 of file qoflog.cpp.

420 {
421  const gchar *levels_group = "levels", *output_group = "output";
422  GError *err = NULL;
423  GKeyFile *conf = g_key_file_new();
424 
425  if (!g_key_file_load_from_file(conf, filename, G_KEY_FILE_NONE, &err))
426  {
427  g_warning("unable to parse [%s]: %s", filename, err->message);
428  g_error_free(err);
429  return;
430  }
431 
432  DEBUG("parsing log config from [%s]", filename);
433  if (g_key_file_has_group(conf, levels_group))
434  {
435  gsize num_levels;
436  unsigned int key_idx;
437  gchar **levels;
438  gint logger_max_name_length = 12;
439  gchar *str = NULL;
440 
441  levels = g_key_file_get_keys(conf, levels_group, &num_levels, NULL);
442 
443  for (key_idx = 0; key_idx < num_levels && levels[key_idx] != NULL; key_idx++)
444  {
445  QofLogLevel level;
446  gchar *logger_name = NULL, *level_str = NULL;
447 
448  logger_name = g_strdup(levels[key_idx]);
449  logger_max_name_length = MAX (logger_max_name_length, (gint) strlen (logger_name));
450  level_str = g_key_file_get_string(conf, levels_group, logger_name, NULL);
451  level = qof_log_level_from_string(level_str);
452 
453  DEBUG("setting log [%s] to level [%s=%d]", logger_name, level_str, level);
454  qof_log_set_level(logger_name, level);
455 
456  g_free(logger_name);
457  g_free(level_str);
458  }
459 
460  str = g_strdup_printf ("%d", logger_max_name_length);
461  if (qof_logger_format)
462  g_free (qof_logger_format);
463  qof_logger_format = g_strconcat ("* %s %*s <%-", str, ".", str, "s> %*s%s%s", nullptr);
464 
465  g_free (str);
466  g_strfreev(levels);
467  }
468 
469  if (g_key_file_has_group(conf, output_group))
470  {
471  gsize num_outputs;
472  unsigned int output_idx;
473  gchar **outputs;
474 
475  outputs = g_key_file_get_keys(conf, output_group, &num_outputs, NULL);
476  for (output_idx = 0; output_idx < num_outputs && outputs[output_idx] != NULL; output_idx++)
477  {
478  gchar *key = outputs[output_idx];
479  gchar *value;
480 
481  if (g_ascii_strcasecmp("to", key) != 0)
482  {
483  g_warning("unknown key [%s] in [outputs], skipping", key);
484  continue;
485  }
486 
487  value = g_key_file_get_string(conf, output_group, key, NULL);
488  DEBUG("setting [output].to=[%s]", value);
490  g_free(value);
491  }
492  g_strfreev(outputs);
493  }
494 
495  g_key_file_free(conf);
496 }
void qof_log_set_level(QofLogModule log_module, QofLogLevel level)
Set the logging level of the given log_module.
Definition: qoflog.cpp:295
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
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 ...
Definition: qoflog.cpp:400

◆ qof_log_prettify()

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.

◆ qof_log_set_file()

void qof_log_set_file ( FILE *  outfile)

Specify an alternate log output, to pipe or file.

Definition at line 149 of file qoflog.cpp.

150 {
151  if (!outfile)
152  {
153  fout = stderr;
154  return;
155  }
156  fout = outfile;
157 }

◆ qof_log_set_level()

void qof_log_set_level ( QofLogModule  module,
QofLogLevel  level 
)

Set the logging level of the given log_module.

Definition at line 295 of file qoflog.cpp.

296 {
297  if (!log_module || level == QOF_LOG_FATAL)
298  return;
299 
300  if (level > current_max)
301  current_max = level;
302 
303  auto module_parts = split_domain(log_module);
304  auto module = get_modules();
305  for (auto part : module_parts)
306  {
307  auto iter = std::find_if(module->m_children.begin(),
308  module->m_children.end(),
309  [part](auto& child){
310  return child && part == child->m_name;
311  });
312  if (iter == module->m_children.end())
313  {
314  auto child = std::make_unique<ModuleEntry>(part, default_level);
315  module->m_children.emplace_back(std::move(child));
316  module = module->m_children.back().get();
317  }
318  else
319  {
320  module = iter->get();
321  }
322  }
323  module->m_level = level;
324 }

◆ qof_log_shutdown()

void qof_log_shutdown ( void  )

Be nice, close the logfile if possible.

Definition at line 268 of file qoflog.cpp.

269 {
270  if (fout && fout != stderr && fout != stdout)
271  {
272  fclose(fout);
273  fout = NULL;
274  }
275 
276  if (function_buffer)
277  {
278  g_free(function_buffer);
279  function_buffer = NULL;
280  }
281 
282  if (_modules != NULL)
283  {
284  _modules = nullptr;
285  }
286 
287  if (previous_handler != NULL)
288  {
289  g_log_set_default_handler(previous_handler, NULL);
290  previous_handler = NULL;
291  }
292 }