📄 elog.c
字号:
/*------------------------------------------------------------------------- * * 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 case is to reset the * ErrorContext to empty before trying to process the inner error. 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. * Since we lose the prior error state due to the reset, we won't be able * to return to processing the original error, but we wouldn't have anyway. * (NOTE: the escape hatch is not used for recursive situations where the * inner message is of less than ERROR severity; in that case we just * try to process it and return normally. Usually this will work, but if * it ends up in infinite recursion, we will PANIC due to error stack * overflow.) * * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.167.2.1 2005/11/22 18:23:23 momjian Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include <fcntl.h>#include <time.h>#include <unistd.h>#include <signal.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 "postmaster/postmaster.h"#include "postmaster/syslogger.h"#include "storage/ipc.h"#include "tcop/tcopprot.h"#include "utils/memutils.h"#include "utils/guc.h"#include "utils/ps_status.h"/* Global variables */ErrorContextCallback *error_context_stack = NULL;sigjmp_buf *PG_exception_stack = NULL;/* GUC parameters */PGErrorVerbosity Log_error_verbosity = PGERROR_VERBOSE;char *Log_line_prefix = NULL; /* format for extra log line info */int Log_destination = LOG_DESTINATION_STDERR;#ifdef HAVE_SYSLOGstatic bool openlog_done = false;static char *syslog_ident = NULL;static int syslog_facility = LOG_LOCAL0;static void write_syslog(int level, const char *line);#endif#ifdef WIN32static void write_eventlog(int level, const char *line);#endif/* 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 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). */boolerrstart(int elevel, const char *filename, int lineno, const char *funcname){ ErrorData *edata; bool output_to_server = false; bool output_to_client = false; int i; /* * Check some cases in which we want to promote an error into a more * severe error. None of this logic applies for non-error messages. */ if (elevel >= ERROR) { /* * If we are inside a critical section, all errors become PANIC * errors. See miscadmin.h. */ if (CritSectionCount > 0) elevel = PANIC; /* * Check reasons for treating ERROR as FATAL: * * 1. we have no handler to pass the error to (implies we are in the * postmaster or in backend startup). * * 2. ExitOnAnyError mode switch is set (initdb uses this). * * 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!) */ if (elevel == ERROR) { if (PG_exception_stack == NULL || ExitOnAnyError || proc_exit_inprogress) elevel = FATAL; } /* * If the error level is ERROR or more, errfinish is 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. */ for (i = 0; i <= errordata_stack_depth; i++) elevel = Max(elevel, errordata[i].elevel); } /* * Now 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. */ /* 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 == DestRemote && 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 && elevel >= ERROR) { /* * Ooops, error during error processing. Clear ErrorContext as * discussed at top of file. We will not return to the original * error's reporter or handler, so we don't need it. */ MemoryContextReset(ErrorContext); /* * 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. We treat this as a PANIC condition * because it suggests an infinite loop of errors during error * recovery. */ errordata_stack_depth = -1; /* make room on stack */ ereport(PANIC, (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); /* * If ERROR (not more nor less) we pass it off to the current handler. * Printing it and popping the stack is the responsibility of the handler. */ if (elevel == ERROR) { /* * We do some minimal cleanup before longjmp'ing so that handlers can * execute in a reasonably sane state. */ /* This is just in case the error came while waiting for input */ ImmediateInterruptOK = false; /* * Reset InterruptHoldoffCount in case we ereport'd from inside an * interrupt holdoff section. (We assume here that no handler will * itself be inside a holdoff section. If necessary, such a handler * could save and restore InterruptHoldoffCount for itself, but this * should make life easier for most.) */ InterruptHoldoffCount = 0; CritSectionCount = 0; /* should be unnecessary, but... */ /* * Note that we leave CurrentMemoryContext set to ErrorContext. The * handler should reset it to something else soon. */ recursion_depth--; PG_RE_THROW(); } /* * If we are doing FATAL or PANIC, abort any old-style COPY OUT in * progress, so that we can report the message before dying. (Without * this, pq_putmessage will refuse to send the message at all, which is * what we want for NOTICE messages, but not for fatal exits.) 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 FATAL, so don't look at output_to_client. */ if (elevel >= FATAL && whereToSendOutput == DestRemote) pq_endcopyout(true); /* Emit the message to the right places */ EmitErrorReport(); /* 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); if (edata->internalquery) pfree(edata->internalquery); errordata_stack_depth--; /* Exit error-handling context */ MemoryContextSwitchTo(oldcontext); recursion_depth--; /* * Perform error recovery action as specified by elevel. */ if (elevel == FATAL) { /* * For a FATAL error, we let proc_exit clean up and exit. */ 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 (PG_exception_stack == NULL && whereToSendOutput == DestRemote) whereToSendOutput = DestNone; /* * 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); /* * If proc_exit is already running, 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). */ proc_exit(proc_exit_inprogress || !IsUnderPostmaster); } 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]; /* we don't bother incrementing recursion_depth */ CHECK_STACK_DEPTH(); edata->sqlerrcode = sqlerrcode; return 0; /* return value does not matter */}/* * errcode_for_file_access --- add SQLSTATE error code to the current error * * The SQLSTATE code is chosen based on the saved errno value. We assume * that the failing operation was some type of disk file access. * * NOTE: the primary error message string should generally include %m * when this is used. */interrcode_for_file_access(void){ ErrorData *edata = &errordata[errordata_stack_depth]; /* we don't bother incrementing recursion_depth */ CHECK_STACK_DEPTH(); switch (edata->saved_errno) { /* Permission-denied failures */ case EPERM: /* Not super-user */ case EACCES: /* Permission denied */#ifdef EROFS case EROFS: /* Read only file system */#endif edata->sqlerrcode = ERRCODE_INSUFFICIENT_PRIVILEGE; break; /* File not found */ case ENOENT: /* No such file or directory */ edata->sqlerrcode = ERRCODE_UNDEFINED_FILE; break; /* Duplicate file */ case EEXIST: /* File exists */ edata->sqlerrcode = ERRCODE_DUPLICATE_FILE; break; /* Wrong object type or state */ case ENOTDIR: /* Not a directory */ case EISDIR: /* Is a directory */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -