/* qof-log.h
 * Author: Rob Clark <rclark@cs.hmc.edu>
 * Copyright (C) 1998-2003 Linas Vepstas <linas@linas.org>
 * Copyright 2005 Neil Williams <linux@codehelp.co.uk>
 * Copyright 2007 Joshua Sled <jsled@asynchronous.org>
 */

/*
 *  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 <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:
 * @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. <tt>PINFO("...\n", ...)</tt>) are removed; the logger
 * will newline separate output.
 *
 * @section best Best Practices
 *
 * Code should:
 *
 * @li Define both <tt>static QofLogModule log_module</tt> and <tt>#define
 *   G_LOG_DOMAIN</tt> 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 <stdarg.h>
#include <stdio.h>
#include <glib.h>
#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 */

/** @} */
