elog.c

来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 1,486 行 · 第 1/3 页

C
1,486
字号
/*------------------------------------------------------------------------- * * elog.c *	  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. * * First, distinguish between re-entrant use and actual recursion.	It * is possible for an error or warning message to be emitted while the * parameters for an error message are being computed.	In this case * errstart has been called for the outer message, and some field values * 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 * 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 * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION *	  $Header: /cvsroot/pgsql/src/backend/utils/error/elog.c,v 1.125 2003/10/17 16:49:03 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#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#include <syslog.h>#endif#include "libpq/libpq.h"#include "libpq/pqformat.h"#include "mb/pg_wchar.h"#include "miscadmin.h"#include "storage/ipc.h"#include "tcop/tcopprot.h"#include "utils/memutils.h"#include "utils/guc.h"/* Global variables */ErrorContextCallback *error_context_stack = NULL;/* GUC parameters */PGErrorVerbosity Log_error_verbosity = PGERROR_VERBOSE;bool		Log_timestamp = false;		/* show timestamps in stderr										 * output */bool		Log_pid = false;	/* show PIDs in stderr output */#ifdef HAVE_SYSLOG/* * 0 = only stdout/stderr * 1 = stdout+stderr and syslog * 2 = syslog only * ... in theory anyway */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#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 */	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 */} ErrorData;/* We provide a small stack of ErrorData records for re-entrant cases */#define ERRORDATA_STACK_SIZE  5static ErrorData errordata[ERRORDATA_STACK_SIZE];static int	errordata_stack_depth = -1; /* index of topmost active frame */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 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 const char *print_timestamp(void);static const char *print_pid(void);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). */boolerrstart(int elevel, const char *filename, int lineno,		 const char *funcname){	ErrorData  *edata;	bool		output_to_server = false;	bool		output_to_client = false;	/*	 * 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	 * errors.	See miscadmin.h.	 */	if (elevel >= ERROR)	{		if (CritSectionCount > 0)			elevel = PANIC;	}	/* Determine whether message is enabled for server log output */	if (IsPostmasterEnvironment)	{		/* 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)				output_to_server = true;		}		else		{			/* elevel != LOG */			if (log_min_messages == LOG)			{				if (elevel >= FATAL)					output_to_server = true;			}			/* Neither is LOG */			else if (elevel >= log_min_messages)				output_to_server = true;		}	}	else	{		/* In bootstrap/standalone case, do not sort LOG out-of-order */		output_to_server = (elevel >= log_min_messages);	}	/* Determine whether message is enabled for client output */	if (whereToSendOutput == Remote && elevel != COMMERROR)	{		/*		 * client_min_messages is honored only after we complete the		 * authentication handshake.  This is required both for security		 * reasons and because many clients can't handle NOTICE messages		 * during authentication.		 */		if (ClientAuthInProgress)			output_to_client = (elevel >= ERROR);		else			output_to_client = (elevel >= client_min_messages ||								elevel == INFO);	}	/* Skip processing effort if non-error message will not be output */	if (elevel < ERROR && !output_to_server && !output_to_client)		return false;	/*	 * Okay, crank up a stack entry to store the info in.	 */	if (recursion_depth++ > 0)	{		/*		 * Ooops, error during error processing.  Clear ErrorContext and		 * force level up to ERROR or greater, as discussed at top of		 * file.  Adjust output decisions too.		 */		MemoryContextReset(ErrorContext);		output_to_server = true;		if (whereToSendOutput == Remote && elevel != COMMERROR)			output_to_client = true;		elevel = Max(elevel, ERROR);		/*		 * If we recurse more than once, the problem might be something		 * broken in a context traceback routine.  Abandon them too.		 */		if (recursion_depth > 2)			error_context_stack = NULL;	}	if (++errordata_stack_depth >= ERRORDATA_STACK_SIZE)	{		/* Wups, stack not big enough */		int			i;		elevel = Max(elevel, ERROR);		/*		 * Don't forget any FATAL/PANIC status on the stack (see comments		 * in errfinish)		 */		for (i = 0; i < errordata_stack_depth; i++)			elevel = Max(elevel, errordata[i].elevel);		/* Clear the stack and try again */		errordata_stack_depth = -1;		ereport(elevel, (errmsg_internal("ERRORDATA_STACK_SIZE exceeded")));	}	/* Initialize data for this error frame */	edata = &errordata[errordata_stack_depth];	MemSet(edata, 0, sizeof(ErrorData));	edata->elevel = elevel;	edata->output_to_server = output_to_server;	edata->output_to_client = output_to_client;	edata->filename = filename;	edata->lineno = lineno;	edata->funcname = funcname;	/* Select default errcode based on elevel */	if (elevel >= ERROR)		edata->sqlerrcode = ERRCODE_INTERNAL_ERROR;	else if (elevel == WARNING)		edata->sqlerrcode = ERRCODE_WARNING;	else		edata->sqlerrcode = ERRCODE_SUCCESSFUL_COMPLETION;	/* errno is saved here so that error parameter eval can't change it */	edata->saved_errno = errno;	recursion_depth--;	return true;}/* * errfinish --- end an error-reporting cycle * * Produce the appropriate error report(s) and pop the error stack. * * If elevel is ERROR or worse, control does not return to the caller. * See elog.h for the error level definitions. */voiderrfinish(int dummy,...){	ErrorData  *edata = &errordata[errordata_stack_depth];	int			elevel = edata->elevel;	MemoryContext oldcontext;	ErrorContextCallback *econtext;	recursion_depth++;	CHECK_STACK_DEPTH();	/*	 * Do processing in ErrorContext, which we hope has enough reserved	 * space to report an error.	 */	oldcontext = MemoryContextSwitchTo(ErrorContext);	/*	 * Call any context callback functions.  Errors occurring in callback	 * functions will be treated as recursive errors --- this ensures we	 * will avoid infinite recursion (see errstart).	 */	for (econtext = error_context_stack;		 econtext != NULL;		 econtext = econtext->previous)		(*econtext->callback) (econtext->arg);	/* Send to server log, if enabled */	if (edata->output_to_server)		send_message_to_server_log(edata);	/*	 * Abort any old-style COPY OUT in progress when an error is detected.	 * This hack is necessary because of poor design of old-style copy	 * protocol.  Note we must do this even if client is fool enough to	 * have set client_min_messages above ERROR, so don't look at	 * output_to_client.	 */	if (elevel >= ERROR && whereToSendOutput == Remote)		pq_endcopyout(true);	/* Send to client, if enabled */	if (edata->output_to_client)		send_message_to_frontend(edata);	/* Now free up subsidiary data attached to stack entry, and release it */	if (edata->message)		pfree(edata->message);	if (edata->detail)		pfree(edata->detail);	if (edata->hint)		pfree(edata->hint);	if (edata->context)		pfree(edata->context);	MemoryContextSwitchTo(oldcontext);	errordata_stack_depth--;	recursion_depth--;	/*	 * If the error level is ERROR or more, we are not going to return to	 * caller; therefore, if there is any stacked error already in	 * progress it will be lost.  This is more or less okay, except we do	 * not want to have a FATAL or PANIC error downgraded because the	 * reporting process was interrupted by a lower-grade error.  So check	 * the stack and make sure we panic if panic is warranted.	 */	if (elevel >= ERROR)	{		int			i;		for (i = 0; i <= errordata_stack_depth; i++)			elevel = Max(elevel, errordata[i].elevel);		/*		 * Also, be sure to reset the stack to empty.  We do not clear		 * ErrorContext here, though; PostgresMain does that later on.		 */		errordata_stack_depth = -1;		recursion_depth = 0;		error_context_stack = NULL;	}	/*	 * Perform error recovery action as specified by elevel.	 */	if (elevel == ERROR || elevel == FATAL)	{		/* Prevent immediate interrupt while entering error recovery */		ImmediateInterruptOK = false;		/*		 * If we just reported a startup failure, the client will		 * disconnect on receiving it, so don't send any more to the		 * client.		 */		if (!Warn_restart_ready && whereToSendOutput == Remote)			whereToSendOutput = None;		/*		 * For a FATAL error, we let proc_exit clean up and exit.		 *		 * There are several other cases in which we treat ERROR as FATAL and		 * go directly to proc_exit:		 *		 * 1. ExitOnAnyError mode switch is set (initdb uses this).		 *		 * 2. we have not yet entered the main backend loop (ie, we are in		 * the postmaster or in backend startup); we have noplace to		 * recover.		 *		 * 3. the error occurred after proc_exit has begun to run.	(It's		 * proc_exit's responsibility to see that this doesn't turn into		 * infinite recursion!)		 *		 * In the last case, we exit with nonzero exit code to indicate that		 * something's pretty wrong.  We also want to exit with nonzero		 * exit code if not running under the postmaster (for example, if		 * we are being run from the initdb script, we'd better return an		 * error status).		 */		if (elevel == FATAL ||			ExitOnAnyError ||			!Warn_restart_ready ||			proc_exit_inprogress)		{			/*			 * fflush here is just to improve the odds that we get to see			 * the error message, in case things are so hosed that			 * proc_exit crashes.  Any other code you might be tempted to			 * add here should probably be in an on_proc_exit callback			 * instead.			 */			fflush(stdout);			fflush(stderr);			proc_exit(proc_exit_inprogress || !IsUnderPostmaster);		}		/*		 * Guard against infinite loop from errors during error recovery.		 */		if (InError)			ereport(PANIC, (errmsg("error during error recovery, giving up")));		InError = true;		/*		 * Otherwise we can return to the main loop in postgres.c.		 */		siglongjmp(Warn_restart, 1);	}	if (elevel >= PANIC)	{		/*		 * Serious crash time. Postmaster will observe nonzero process		 * exit status and kill the other backends too.		 *		 * XXX: what if we are *in* the postmaster?  abort() won't kill our		 * children...		 */		ImmediateInterruptOK = false;		fflush(stdout);		fflush(stderr);		abort();	}	/* We reach here if elevel <= WARNING. OK to return to caller. */}/* * errcode --- add SQLSTATE error code to the current error * * The code is expected to be represented as per MAKE_SQLSTATE(). */interrcode(int sqlerrcode){	ErrorData  *edata = &errordata[errordata_stack_depth];

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?