/* qof-log.h * Author: Rob Clark * Copyright (C) 1998-2003 Linas Vepstas * Copyright 2005 Neil Williams * Copyright 2007 Joshua Sled */ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ /** * @addtogroup Logging * @{ * @ingroup QOF * @brief Logging and tracing facility. * @sa "Logging overhaul" announcement * * 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: * @verbatim "qof" = WARN "gnc" = WARN "gnc.ui" = INFO "gnc.ui.plugin-page.sx-list" = DEBUG @endverbatim * * 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: * * @verbatim * [timestamp] [level] <[log-domain]> [message] @endverbatim * * 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. * * @section best Best Practices * * Code should: * * @li Define both static QofLogModule log_module and #define * G_LOG_DOMAIN to the same value. * @li Define a logical, specific path as the log domain; * @c "gnc.gui.plugin-pages.sx-list" or * @c "gnc.register.gnome.cell.quickfill" are * good examples. * @li Prefer the macros defined here (PERR, PWARN, PINFO, etc.) to * the GLib-provided functions that they wrap because it allows us to * more easily replace the GLib logging functinos with another * implementation and besides our macros are able to short-circuit * GLib's rather slow domain and level matching. * * @see qof_log_parse_log_config(const char*) **/ #ifndef _QOF_LOG_H #define _QOF_LOG_H #include #include #include #include "qofutil.h" #ifdef __cplusplus extern "C" { #endif typedef const gchar* QofLogModule; #define QOF_MOD_ENGINE "qof.engine" typedef enum { 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 } QofLogLevel; const char* qof_log_level_to_string(QofLogLevel lvl); QofLogLevel qof_log_level_from_string(const char *str); /** Indents one level; see ENTER macro. **/ void qof_log_indent(void); /** * De-dent one level, capped at 0; see LEAVE macro. **/ void qof_log_dedent(void); /** * Initialize the error logging subsystem. Defaults to a level-threshold of * "warning", and logging to stderr. **/ void qof_log_init (void); /** Set the logging level of the given log_module. **/ void qof_log_set_level(QofLogModule module, QofLogLevel level); /** Specify an alternate log output, to pipe or file. **/ void qof_log_set_file (FILE *outfile); /** Specify a filename for log output. **/ void qof_log_init_filename (const gchar* logfilename); /** * If @a 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_init_filename_special(const char *log_to_filename); /** * Parse a log-configuration file. A GKeyFile-format file of the schema: * @verbatim [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 @endverbatim **/ void qof_log_parse_log_config(const char *filename); /** Be nice, close the logfile if possible. */ void qof_log_shutdown (void); /** * 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. **/ const gchar * qof_log_prettify (const gchar *name); /** Check to see if the given @a log_module is configured to log at the given * @a log_level. This implements the "log.path.hierarchy" logic. **/ gboolean qof_log_check(QofLogModule log_module, QofLogLevel log_level); #define PRETTY_FUNC_NAME qof_log_prettify(G_STRFUNC) #ifdef _MSC_VER /* Microsoft Visual Studio: MSVC compiler has a different syntax for * macros with variadic argument list. */ /* TODO: After the C++2a feature __VA_OPT__ gets implemented in both * flavors, it should be inserted before __VA_ARGS__ and the else branch * gets obsolete and should be removed. */ /** Log a fatal error */ #define FATAL(format, ...) do { \ g_log (log_module, G_LOG_LEVEL_ERROR, \ "[%s()] " format, PRETTY_FUNC_NAME , __VA_ARGS__); \ } while (0) /** Log a serious error */ #define PERR(format, ...) do { \ g_log (log_module, G_LOG_LEVEL_CRITICAL, \ "[%s()] " format, PRETTY_FUNC_NAME , __VA_ARGS__); \ } while (0) /** Log a warning */ #define PWARN(format, ...) do { \ g_log (log_module, G_LOG_LEVEL_WARNING, \ "[%s()] " format, PRETTY_FUNC_NAME , __VA_ARGS__); \ } while (0) /** Print an informational note */ #define PINFO(format, ...) \ if (qof_log_check(log_module, QOF_LOG_INFO) { \ g_log (log_module, G_LOG_LEVEL_INFO, \ "[%s] " format, PRETTY_FUNC_NAME , __VA_ARGS__); \ } /** Print a debugging message */ #define DEBUG(format, ...) \ if (qof_log_check(log_module, QOF_LOG_DEBUG) { \ g_log (log_module, G_LOG_LEVEL_DEBUG, \ "[%s] " format, PRETTY_FUNC_NAME , __VA_ARGS__); \ } /** Print a function entry debugging message */ #define ENTER(format, ...) \ if (qof_log_check(log_module, QOFLOG_DEBUG)) { \ g_log (log_module, G_LOG_LEVEL_DEBUG, \ "[enter %s:%s()] " format, __FILE__, \ PRETTY_FUNC_NAME , __VA_ARGS__); \ qof_log_indent(); \ } /** Print a function exit debugging message. **/ #define LEAVE(format, ...) \ 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 , __VA_ARGS__); \ } #else /* _MSC_VER */ /** Log a fatal error */ #define FATAL(format, args...) do { \ g_log (log_module, G_LOG_LEVEL_ERROR, \ "[%s()] " format, PRETTY_FUNC_NAME , ## args); \ } while (0) /** Log a serious error */ #define PERR(format, args...) do { \ g_log (log_module, G_LOG_LEVEL_CRITICAL, \ "[%s()] " format, PRETTY_FUNC_NAME , ## args); \ } while (0) /** Log a warning */ #define PWARN(format, args...) do { \ g_log (log_module, G_LOG_LEVEL_WARNING, \ "[%s()] " format, PRETTY_FUNC_NAME , ## args); \ } while (0) /** Print an informational note */ #define PINFO(format, args...) 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) /** Print a debugging message */ #define DEBUG(format, args...) 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) /** Print a function entry debugging message */ #define ENTER(format, args...) 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) /** Print a function exit debugging message. **/ #define LEAVE(format, args...) 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) #endif /* _MSC_VER */ /** Replacement for @c g_return_val_if_fail, but calls LEAVE if the test fails. **/ #define gnc_leave_return_val_if_fail(test, val) do { \ if (! (test)) { LEAVE(""); } \ g_return_val_if_fail(test, val); \ } while (0); /** Replacement for @c g_return_if_fail, but calls LEAVE if the test fails. **/ #define gnc_leave_return_if_fail(test) do { \ if (! (test)) { LEAVE(""); } \ g_return_if_fail(test); \ } while (0); #ifdef __cplusplus } #endif #endif /* _QOF_LOG_H */ /** @} */