Skip to content
Snippets Groups Projects
elog.c 38.9 KiB
Newer Older
/*-------------------------------------------------------------------------
 *
 *	  error logging and reporting
 *
 * Some notes about recursion and errors during error processing:
 *
 * We need to be robust about recursive-error scenarios --- for example,
 * if we run out of memory, it's important to be able to report that fact.
 * There are a number of considerations that go into this.
 *
Bruce Momjian's avatar
Bruce Momjian committed
 * First, distinguish between re-entrant use and actual recursion.	It
 * is possible for an error or warning message to be emitted while the
Bruce Momjian's avatar
Bruce Momjian committed
 * parameters for an error message are being computed.	In this case
 * errstart has been called for the outer message, and some field values
Bruce Momjian's avatar
Bruce Momjian committed
 * may have already been saved, but we are not actually recursing.	We handle
 * this by providing a (small) stack of ErrorData records.	The inner message
 * can be computed and sent without disturbing the state of the outer message.
 * (If the inner message is actually an error, this isn't very interesting
 * because control won't come back to the outer message generator ... but
 * if the inner message is only debug or log data, this is critical.)
 *
 * Second, actual recursion will occur if an error is reported by one of
Bruce Momjian's avatar
Bruce Momjian committed
 * the elog.c routines or something they call.	By far the most probable
 * scenario of this sort is "out of memory"; and it's also the nastiest
 * to handle because we'd likely also run out of memory while trying to
 * report this error!  Our escape hatch for this condition is to force any
 * such messages up to ERROR level if they aren't already (so that we will
 * not need to return to the outer elog.c call), and to reset the ErrorContext
 * to empty before trying to process the inner message.  Since ErrorContext
 * is guaranteed to have at least 8K of space in it (see mcxt.c), we should
 * be able to process an "out of memory" message successfully.
 *
 * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
Bruce Momjian's avatar
Bruce Momjian committed
 * Portions Copyright (c) 1994, Regents of the University of California
 *	  $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.129 2004/03/19 02:23:59 tgl Exp $
 *
 *-------------------------------------------------------------------------
 */
#include <time.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
#include <ctype.h>
#ifdef HAVE_SYSLOG
Marc G. Fournier's avatar
 
Marc G. Fournier committed
#endif

#include "libpq/libpq.h"
#include "libpq/pqformat.h"
Marc G. Fournier's avatar
 
Marc G. Fournier committed


/* Global variables */
ErrorContextCallback *error_context_stack = NULL;

/* GUC parameters */
PGErrorVerbosity Log_error_verbosity = PGERROR_VERBOSE;
char       *Log_line_prefix = NULL; /* format for extra log line info */
#ifdef HAVE_SYSLOG
Marc G. Fournier's avatar
 
Marc G. Fournier committed
/*
 * 0 = only stdout/stderr
 * 1 = stdout+stderr and syslog
 * 2 = syslog only
 * ... in theory anyway
Marc G. Fournier's avatar
 
Marc G. Fournier committed
 */
int			Use_syslog = 0;
char	   *Syslog_facility;	/* openlog() parameters */
char	   *Syslog_ident;

static void write_syslog(int level, const char *line);

#else
#define Use_syslog 0
Bruce Momjian's avatar
Bruce Momjian committed
#endif   /* HAVE_SYSLOG */
/*
 * ErrorData holds the data accumulated during any one ereport() cycle.
 * Any non-NULL pointers must point to palloc'd data in ErrorContext.
 * (The const pointers are an exception; we assume they point at non-freeable
 * constant strings.)
 */
typedef struct ErrorData
{
	int			elevel;			/* error level */
Bruce Momjian's avatar
Bruce Momjian committed
	bool		output_to_server;		/* will report to server log? */
	bool		output_to_client;		/* will report to client? */
	bool		show_funcname;	/* true to force funcname inclusion */
	const char *filename;		/* __FILE__ of ereport() call */
	int			lineno;			/* __LINE__ of ereport() call */
	const char *funcname;		/* __func__ of ereport() call */
	int			sqlerrcode;		/* encoded ERRSTATE */
	char	   *message;		/* primary error message */
	char	   *detail;			/* detail error message */
	char	   *hint;			/* hint message */
	char	   *context;		/* context message */
	int			cursorpos;		/* cursor index into query string */
	int			saved_errno;	/* errno at entry */

/* We provide a small stack of ErrorData records for re-entrant cases */
#define ERRORDATA_STACK_SIZE  5

static ErrorData errordata[ERRORDATA_STACK_SIZE];

static int	errordata_stack_depth = -1; /* index of topmost active frame */

Bruce Momjian's avatar
Bruce Momjian committed
static int	recursion_depth = 0;	/* to detect actual recursion */


/* Macro for checking errordata_stack_depth is reasonable */
#define CHECK_STACK_DEPTH() \
	do { \
		if (errordata_stack_depth < 0) \
		{ \
			errordata_stack_depth = -1; \
			ereport(ERROR, (errmsg_internal("errstart was not called"))); \
		} \
	} while (0)


static void log_line_prefix(StringInfo buf);
static void send_message_to_server_log(ErrorData *edata);
static void send_message_to_frontend(ErrorData *edata);
static char *expand_fmt_string(const char *fmt, ErrorData *edata);
static const char *useful_strerror(int errnum);
static const char *error_severity(int elevel);
static void append_with_tabs(StringInfo buf, const char *str);
/*
 * errstart --- begin an error-reporting cycle
 * Create a stack entry and store the given parameters in it.  Subsequently,
 * errmsg() and perhaps other routines will be called to further populate
 * the stack entry.  Finally, errfinish() will be called to actually process
 * the error report.
 * Returns TRUE in normal case.  Returns FALSE to short-circuit the error
 * report (if it's a warning or lower and not to be reported anywhere).
bool
errstart(int elevel, const char *filename, int lineno,
		 const char *funcname)
	bool		output_to_server = false;
	bool		output_to_client = false;
Bruce Momjian's avatar
Bruce Momjian committed
	 * First decide whether we need to process this report at all; if it's
	 * warning or less and not enabled for logging, just return FALSE
	 * without starting up any error logging machinery.
	 * Convert initialization errors into fatal errors. This is probably
	 * redundant, because Warn_restart_ready won't be set anyway.
	if (elevel == ERROR && IsInitProcessingMode())
		elevel = FATAL;
	 * If we are inside a critical section, all errors become PANIC
	/* Determine whether message is enabled for server log output */
		/* Complicated because LOG is sorted out-of-order for this purpose */
		if (elevel == LOG || elevel == COMMERROR)
			if (log_min_messages == LOG)
				output_to_server = true;
			else if (log_min_messages < FATAL)
Loading
Loading full blame...