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 + -
显示快捷键?