Newer
Older
Andrew Dunstan
committed
appendStringInfoChar(&buf, ',');
Alvaro Herrera
committed
/* Remote host and port */
Andrew Dunstan
committed
if (MyProcPort && MyProcPort->remote_host)
{
Alvaro Herrera
committed
appendStringInfoChar(&buf, '"');
Andrew Dunstan
committed
appendStringInfo(&buf, "%s", MyProcPort->remote_host);
if (MyProcPort->remote_port && MyProcPort->remote_port[0] != '\0')
appendStringInfo(&buf, ":%s", MyProcPort->remote_port);
Alvaro Herrera
committed
appendStringInfoChar(&buf, '"');
Andrew Dunstan
committed
}
appendStringInfoChar(&buf, ',');
/* session id */
appendStringInfo(&buf, "%lx.%x", (long) MyStartTime, MyProcPid);
Andrew Dunstan
committed
appendStringInfoChar(&buf, ',');
/* Line number */
appendStringInfo(&buf, "%ld", log_line_number);
appendStringInfoChar(&buf, ',');
/* PS display */
if (MyProcPort)
{
Alvaro Herrera
committed
StringInfoData msgbuf;
Andrew Dunstan
committed
const char *psdisp;
Andrew Dunstan
committed
Alvaro Herrera
committed
initStringInfo(&msgbuf);
Andrew Dunstan
committed
psdisp = get_ps_display(&displen);
appendBinaryStringInfo(&msgbuf, psdisp, displen);
Andrew Dunstan
committed
appendCSVLiteral(&buf, msgbuf.data);
Alvaro Herrera
committed
pfree(msgbuf.data);
Andrew Dunstan
committed
}
appendStringInfoChar(&buf, ',');
/* session start timestamp */
if (formatted_start_time[0] == '\0')
setup_formatted_start_time();
Andrew Dunstan
committed
appendStringInfoString(&buf, formatted_start_time);
appendStringInfoChar(&buf, ',');
/* Virtual transaction id */
/* keep VXID format in sync with lockfuncs.c */
Alvaro Herrera
committed
if (MyProc != NULL && MyProc->backendId != InvalidBackendId)
appendStringInfo(&buf, "%d/%u", MyProc->backendId, MyProc->lxid);
appendStringInfoChar(&buf, ',');
Andrew Dunstan
committed
/* Transaction id */
appendStringInfo(&buf, "%u", GetTopTransactionIdIfAny());
Andrew Dunstan
committed
appendStringInfoChar(&buf, ',');
/* Error severity */
Alvaro Herrera
committed
appendStringInfo(&buf, "%s", error_severity(edata->elevel));
appendStringInfoChar(&buf, ',');
Andrew Dunstan
committed
/* SQL state code */
Alvaro Herrera
committed
appendStringInfo(&buf, "%s", unpack_sql_state(edata->sqlerrcode));
appendStringInfoChar(&buf, ',');
/* errmessage */
appendCSVLiteral(&buf, edata->message);
appendStringInfoCharMacro(&buf, ',');
/* errdetail or errdetail_log */
if (edata->detail_log)
appendCSVLiteral(&buf, edata->detail_log);
else
appendCSVLiteral(&buf, edata->detail);
Alvaro Herrera
committed
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
appendStringInfoCharMacro(&buf, ',');
/* errhint */
appendCSVLiteral(&buf, edata->hint);
appendStringInfoCharMacro(&buf, ',');
/* internal query */
appendCSVLiteral(&buf, edata->internalquery);
appendStringInfoCharMacro(&buf, ',');
/* if printed internal query, print internal pos too */
if (edata->internalpos > 0 && edata->internalquery != NULL)
appendStringInfo(&buf, "%d", edata->internalpos);
appendStringInfoCharMacro(&buf, ',');
/* errcontext */
appendCSVLiteral(&buf, edata->context);
appendStringInfoCharMacro(&buf, ',');
/* user query --- only reported if not disabled by the caller */
if (is_log_level_output(edata->elevel, log_min_error_statement) &&
debug_query_string != NULL &&
!edata->hide_stmt)
print_stmt = true;
if (print_stmt)
appendCSVLiteral(&buf, debug_query_string);
appendStringInfoCharMacro(&buf, ',');
if (print_stmt && edata->cursorpos > 0)
appendStringInfo(&buf, "%d", edata->cursorpos);
appendStringInfoCharMacro(&buf, ',');
/* file error location */
if (Log_error_verbosity >= PGERROR_VERBOSE)
{
StringInfoData msgbuf;
Alvaro Herrera
committed
initStringInfo(&msgbuf);
if (edata->funcname && edata->filename)
appendStringInfo(&msgbuf, "%s, %s:%d",
edata->funcname, edata->filename,
edata->lineno);
else if (edata->filename)
appendStringInfo(&msgbuf, "%s:%d",
edata->filename, edata->lineno);
appendCSVLiteral(&buf, msgbuf.data);
pfree(msgbuf.data);
}
appendStringInfoCharMacro(&buf, ',');
/* application name */
if (application_name)
appendCSVLiteral(&buf, application_name);
Andrew Dunstan
committed
appendStringInfoChar(&buf, '\n');
/* If in the syslogger process, try to write messages direct to file */
if (am_syslogger)
write_syslogger_file(buf.data, buf.len, LOG_DESTINATION_CSVLOG);
else
write_pipe_chunks(buf.data, buf.len, LOG_DESTINATION_CSVLOG);
Andrew Dunstan
committed
pfree(buf.data);
}
/*
* Unpack MAKE_SQLSTATE code. Note that this returns a pointer to a
* static buffer.
*/
char *
unpack_sql_state(int sql_state)
{
int i;
for (i = 0; i < 5; i++)
{
buf[i] = PGUNSIXBIT(sql_state);
sql_state >>= 6;
}
buf[i] = '\0';
return buf;
}
/*
* Write error report to server's log
*/
static void
send_message_to_server_log(ErrorData *edata)
initStringInfo(&buf);
Andrew Dunstan
committed
formatted_log_time[0] = '\0';
log_line_prefix(&buf, edata);
appendStringInfo(&buf, "%s: ", error_severity(edata->elevel));
if (Log_error_verbosity >= PGERROR_VERBOSE)
appendStringInfo(&buf, "%s: ", unpack_sql_state(edata->sqlerrcode));
if (edata->message)
append_with_tabs(&buf, edata->message);
append_with_tabs(&buf, _("missing error text"));
if (edata->cursorpos > 0)
appendStringInfo(&buf, _(" at character %d"),
edata->cursorpos);
else if (edata->internalpos > 0)
appendStringInfo(&buf, _(" at character %d"),
edata->internalpos);
appendStringInfoChar(&buf, '\n');
if (Log_error_verbosity >= PGERROR_DEFAULT)
{
if (edata->detail_log)
{
log_line_prefix(&buf, edata);
appendStringInfoString(&buf, _("DETAIL: "));
append_with_tabs(&buf, edata->detail_log);
appendStringInfoChar(&buf, '\n');
}
else if (edata->detail)
log_line_prefix(&buf, edata);
appendStringInfoString(&buf, _("DETAIL: "));
append_with_tabs(&buf, edata->detail);
appendStringInfoChar(&buf, '\n');
}
if (edata->hint)
log_line_prefix(&buf, edata);
appendStringInfoString(&buf, _("HINT: "));
append_with_tabs(&buf, edata->hint);
appendStringInfoChar(&buf, '\n');
}
if (edata->internalquery)
{
log_line_prefix(&buf, edata);
appendStringInfoString(&buf, _("QUERY: "));
append_with_tabs(&buf, edata->internalquery);
appendStringInfoChar(&buf, '\n');
}
if (edata->context)
log_line_prefix(&buf, edata);
appendStringInfoString(&buf, _("CONTEXT: "));
append_with_tabs(&buf, edata->context);
appendStringInfoChar(&buf, '\n');
}
if (Log_error_verbosity >= PGERROR_VERBOSE)
{
/* assume no newlines in funcname or filename... */
if (edata->funcname && edata->filename)
log_line_prefix(&buf, edata);
appendStringInfo(&buf, _("LOCATION: %s, %s:%d\n"),
edata->funcname, edata->filename,
edata->lineno);
else if (edata->filename)
log_line_prefix(&buf, edata);
appendStringInfo(&buf, _("LOCATION: %s:%d\n"),
edata->filename, edata->lineno);
}
}
* If the user wants the query that generated this error logged, do it.
if (is_log_level_output(edata->elevel, log_min_error_statement) &&
debug_query_string != NULL &&
!edata->hide_stmt)
log_line_prefix(&buf, edata);
appendStringInfoString(&buf, _("STATEMENT: "));
append_with_tabs(&buf, debug_query_string);
appendStringInfoChar(&buf, '\n');
}
#ifdef HAVE_SYSLOG
/* Write to syslog, if enabled */
if (Log_destination & LOG_DESTINATION_SYSLOG)
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
{
int syslog_level;
switch (edata->elevel)
{
case DEBUG5:
case DEBUG4:
case DEBUG3:
case DEBUG2:
case DEBUG1:
syslog_level = LOG_DEBUG;
break;
case LOG:
case COMMERROR:
case INFO:
syslog_level = LOG_INFO;
break;
case NOTICE:
case WARNING:
syslog_level = LOG_NOTICE;
break;
case ERROR:
syslog_level = LOG_WARNING;
break;
case FATAL:
syslog_level = LOG_ERR;
break;
case PANIC:
default:
syslog_level = LOG_CRIT;
break;
}
write_syslog(syslog_level, buf.data);
}
#ifdef WIN32
/* Write to eventlog, if enabled */
if (Log_destination & LOG_DESTINATION_EVENTLOG)
{
write_eventlog(edata->elevel, buf.data, buf.len);
}
#endif /* WIN32 */
/* Write to stderr, if enabled */
Alvaro Herrera
committed
if ((Log_destination & LOG_DESTINATION_STDERR) || whereToSendOutput == DestDebug)
{
* Use the chunking protocol if we know the syslogger should be
* catching stderr output, and we are not ourselves the syslogger.
* Otherwise, just do a vanilla write to stderr.
*/
if (redirection_done && !am_syslogger)
Andrew Dunstan
committed
write_pipe_chunks(buf.data, buf.len, LOG_DESTINATION_STDERR);
#ifdef WIN32
/*
* In a win32 service environment, there is no usable stderr. Capture
* anything going there and write it to the eventlog instead.
*
* If stderr redirection is active, it was OK to write to stderr above
* because that's really a pipe to the syslogger process.
else if (pgwin32_is_service())
write_eventlog(edata->elevel, buf.data, buf.len);
#endif
write_console(buf.data, buf.len);
}
/* If in the syslogger process, try to write messages direct to file */
if (am_syslogger)
write_syslogger_file(buf.data, buf.len, LOG_DESTINATION_STDERR);
/* Write to CSV log if enabled */
Andrew Dunstan
committed
if (Log_destination & LOG_DESTINATION_CSVLOG)
{
if (redirection_done || am_syslogger)
{
/*
* send CSV data if it's safe to do so (syslogger doesn't need the
* pipe). First get back the space in the message buffer.
Andrew Dunstan
committed
*/
pfree(buf.data);
Andrew Dunstan
committed
write_csvlog(edata);
}
else
{
/*
* syslogger not up (yet), so just dump the message to stderr,
* unless we already did so above.
*/
if (!(Log_destination & LOG_DESTINATION_STDERR) &&
whereToSendOutput != DestDebug)
write_console(buf.data, buf.len);
pfree(buf.data);
Andrew Dunstan
committed
}
}
else
{
pfree(buf.data);
}
Andrew Dunstan
committed
/*
* Send data to the syslogger using the chunked protocol
*/
static void
Andrew Dunstan
committed
write_pipe_chunks(char *data, int len, int dest)
Andrew Dunstan
committed
{
PipeProtoChunk p;
Andrew Dunstan
committed
Andrew Dunstan
committed
Assert(len > 0);
p.proto.nuls[0] = p.proto.nuls[1] = '\0';
p.proto.pid = MyProcPid;
/* write all but the last chunk */
while (len > PIPE_MAX_PAYLOAD)
{
Andrew Dunstan
committed
p.proto.is_last = (dest == LOG_DESTINATION_CSVLOG ? 'F' : 'f');
Andrew Dunstan
committed
p.proto.len = PIPE_MAX_PAYLOAD;
memcpy(p.proto.data, data, PIPE_MAX_PAYLOAD);
write(fd, &p, PIPE_HEADER_SIZE + PIPE_MAX_PAYLOAD);
data += PIPE_MAX_PAYLOAD;
len -= PIPE_MAX_PAYLOAD;
}
/* write the last chunk */
Andrew Dunstan
committed
p.proto.is_last = (dest == LOG_DESTINATION_CSVLOG ? 'T' : 't');
Andrew Dunstan
committed
p.proto.len = len;
memcpy(p.proto.data, data, len);
write(fd, &p, PIPE_HEADER_SIZE + len);
}
/*
* Append a text string to the error report being built for the client.
*
* This is ordinarily identical to pq_sendstring(), but if we are in
* error recursion trouble we skip encoding conversion, because of the
* possibility that the problem is a failure in the encoding conversion
* subsystem itself. Code elsewhere should ensure that the passed-in
* strings will be plain 7-bit ASCII, and thus not in need of conversion,
* in such cases. (In particular, we disable localization of error messages
* to help ensure that's true.)
*/
static void
err_sendstring(StringInfo buf, const char *str)
{
if (in_error_recursion_trouble())
pq_send_ascii_string(buf, str);
else
pq_sendstring(buf, str);
}
/*
* Write error report to client
*/
static void
send_message_to_frontend(ErrorData *edata)
{
StringInfoData msgbuf;
/* 'N' (Notice) is for nonfatal conditions, 'E' is for errors */
pq_beginmessage(&msgbuf, (edata->elevel < ERROR) ? 'N' : 'E');
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
{
/* New style with separate fields */
Peter Eisentraut
committed
pq_sendbyte(&msgbuf, PG_DIAG_SEVERITY);
err_sendstring(&msgbuf, error_severity(edata->elevel));
/* unpack MAKE_SQLSTATE code */
ssval = edata->sqlerrcode;
for (i = 0; i < 5; i++)
{
tbuf[i] = PGUNSIXBIT(ssval);
ssval >>= 6;
}
tbuf[i] = '\0';
Peter Eisentraut
committed
pq_sendbyte(&msgbuf, PG_DIAG_SQLSTATE);
err_sendstring(&msgbuf, tbuf);
/* M field is required per protocol, so always send something */
Peter Eisentraut
committed
pq_sendbyte(&msgbuf, PG_DIAG_MESSAGE_PRIMARY);
if (edata->message)
err_sendstring(&msgbuf, edata->message);
err_sendstring(&msgbuf, _("missing error text"));
if (edata->detail)
{
Peter Eisentraut
committed
pq_sendbyte(&msgbuf, PG_DIAG_MESSAGE_DETAIL);
err_sendstring(&msgbuf, edata->detail);
/* detail_log is intentionally not used here */
if (edata->hint)
{
Peter Eisentraut
committed
pq_sendbyte(&msgbuf, PG_DIAG_MESSAGE_HINT);
err_sendstring(&msgbuf, edata->hint);
}
if (edata->context)
{
Peter Eisentraut
committed
pq_sendbyte(&msgbuf, PG_DIAG_CONTEXT);
err_sendstring(&msgbuf, edata->context);
}
if (edata->cursorpos > 0)
{
snprintf(tbuf, sizeof(tbuf), "%d", edata->cursorpos);
Peter Eisentraut
committed
pq_sendbyte(&msgbuf, PG_DIAG_STATEMENT_POSITION);
err_sendstring(&msgbuf, tbuf);
if (edata->internalpos > 0)
{
snprintf(tbuf, sizeof(tbuf), "%d", edata->internalpos);
pq_sendbyte(&msgbuf, PG_DIAG_INTERNAL_POSITION);
err_sendstring(&msgbuf, tbuf);
}
if (edata->internalquery)
{
pq_sendbyte(&msgbuf, PG_DIAG_INTERNAL_QUERY);
err_sendstring(&msgbuf, edata->internalquery);
if (edata->filename)
{
Peter Eisentraut
committed
pq_sendbyte(&msgbuf, PG_DIAG_SOURCE_FILE);
err_sendstring(&msgbuf, edata->filename);
}
if (edata->lineno > 0)
{
snprintf(tbuf, sizeof(tbuf), "%d", edata->lineno);
Peter Eisentraut
committed
pq_sendbyte(&msgbuf, PG_DIAG_SOURCE_LINE);
err_sendstring(&msgbuf, tbuf);
}
if (edata->funcname)
{
Peter Eisentraut
committed
pq_sendbyte(&msgbuf, PG_DIAG_SOURCE_FUNCTION);
err_sendstring(&msgbuf, edata->funcname);
}
else
{
/* Old style --- gin up a backwards-compatible message */
StringInfoData buf;
initStringInfo(&buf);
appendStringInfo(&buf, "%s: ", error_severity(edata->elevel));
if (edata->show_funcname && edata->funcname)
appendStringInfo(&buf, "%s: ", edata->funcname);
if (edata->message)
appendStringInfoString(&buf, edata->message);
appendStringInfoString(&buf, _("missing error text"));
if (edata->cursorpos > 0)
appendStringInfo(&buf, _(" at character %d"),
edata->cursorpos);
else if (edata->internalpos > 0)
appendStringInfo(&buf, _(" at character %d"),
edata->internalpos);
appendStringInfoChar(&buf, '\n');
err_sendstring(&msgbuf, buf.data);
pfree(buf.data);
}
pq_endmessage(&msgbuf);
/*
* This flush is normally not necessary, since postgres.c will flush out
* waiting data when control returns to the main loop. But it seems best
* to leave it here, so that the client has some clue what happened if the
* backend dies before getting back to the main loop ... error/notice
* messages should not be a performance-critical path anyway, so an extra
* flush won't hurt much ...
*/
pq_flush();
}
/*
* Support routines for formatting error messages.
*/
/*
* expand_fmt_string --- process special format codes in a format string
*
* We must replace %m with the appropriate strerror string, since vsnprintf
* won't know what to do with it.
*
* The result is a palloc'd string.
*/
static char *
expand_fmt_string(const char *fmt, ErrorData *edata)
const char *cp;
initStringInfo(&buf);
for (cp = fmt; *cp; cp++)
{
if (cp[0] == '%' && cp[1] != '\0')
{
cp++;
if (*cp == 'm')
{
/*
* Replace %m by system error string. If there are any %'s in
* the string, we'd better double them so that vsnprintf won't
* misinterpret.
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
*/
const char *cp2;
cp2 = useful_strerror(edata->saved_errno);
for (; *cp2; cp2++)
{
if (*cp2 == '%')
appendStringInfoCharMacro(&buf, '%');
appendStringInfoCharMacro(&buf, *cp2);
}
}
else
{
/* copy % and next char --- this avoids trouble with %%m */
appendStringInfoCharMacro(&buf, '%');
appendStringInfoCharMacro(&buf, *cp);
}
}
else
appendStringInfoCharMacro(&buf, *cp);
}
return buf.data;
}
/*
* A slightly cleaned-up version of strerror()
*/
static const char *
useful_strerror(int errnum)
{
/* this buffer is only used if errno has a bogus value */
static char errorstr_buf[48];
#ifdef WIN32
/* Winsock error code range, per WinError.h */
if (errnum >= 10000 && errnum <= 11999)
return pgwin32_socket_strerror(errnum);
#endif
str = strerror(errnum);
/*
* Some strerror()s return an empty string for out-of-range errno. This is
* ANSI C spec compliant, but not exactly useful.
*/
if (str == NULL || *str == '\0')
{
snprintf(errorstr_buf, sizeof(errorstr_buf),
/*------
translator: This string will be truncated at 47
characters expanded. */
_("operating system error %d"), errnum);
str = errorstr_buf;
}
return str;
}
/*
* error_severity --- get localized string representing elevel
*/
static const char *
error_severity(int elevel)
{
const char *prefix;
switch (elevel)
{
case DEBUG1:
case DEBUG2:
case DEBUG3:
case DEBUG4:
case DEBUG5:
prefix = _("DEBUG");
break;
case LOG:
case COMMERROR:
prefix = _("LOG");
break;
case INFO:
prefix = _("INFO");
break;
case NOTICE:
prefix = _("NOTICE");
break;
prefix = _("WARNING");
case ERROR:
prefix = _("ERROR");
break;
case FATAL:
prefix = _("FATAL");
break;
case PANIC:
prefix = _("PANIC");
break;
default:
prefix = "???";
break;
}
return prefix;
}
/*
* append_with_tabs
* Append the string to the StringInfo buffer, inserting a tab after any
* newline.
*/
static void
append_with_tabs(StringInfo buf, const char *str)
while ((ch = *str++) != '\0')
appendStringInfoCharMacro(buf, ch);
if (ch == '\n')
appendStringInfoCharMacro(buf, '\t');
}
}
* Write errors to stderr (or by equal means when stderr is
* not available). Used before ereport/elog can be used
*/
void
write_stderr(const char *fmt,...)
{
#ifdef WIN32
#endif
fmt = _(fmt);
va_start(ap, fmt);
#ifndef WIN32
/* On Unix, we just fprintf to stderr */
vfprintf(stderr, fmt, ap);
Andrew Dunstan
committed
fflush(stderr);
#else
vsnprintf(errbuf, sizeof(errbuf), fmt, ap);
/*
* On Win32, we print to stderr if running on a console, or write to
* eventlog if running as a service
*/
if (pgwin32_is_service()) /* Running as a service */
write_eventlog(ERROR, errbuf, strlen(errbuf));
Andrew Dunstan
committed
{
/* Not running as service, write to stderr */
write_console(errbuf, strlen(errbuf));
Andrew Dunstan
committed
fflush(stderr);
}
#endif
va_end(ap);
}
/*
* is_log_level_output -- is elevel logically >= log_min_level?
*
* We use this for tests that should consider LOG to sort out-of-order,
* between ERROR and FATAL. Generally this is the right thing for testing
* whether a message should go to the postmaster log, whereas a simple >=
* test is correct for testing whether the message should go to the client.
*/
static bool
is_log_level_output(int elevel, int log_min_level)
{
if (elevel == LOG || elevel == COMMERROR)
{
if (log_min_level == LOG || log_min_level <= ERROR)
return true;
}
else if (log_min_level == LOG)
{
/* elevel != LOG */
if (elevel >= FATAL)
return true;
}
/* Neither is LOG */
else if (elevel >= log_min_level)
return true;
return false;
}
/*
* Adjust the level of a recovery-related message per trace_recovery_messages.
*
* The argument is the default log level of the message, eg, DEBUG2. (This
* should only be applied to DEBUGn log messages, otherwise it's a no-op.)
* If the level is >= trace_recovery_messages, we return LOG, causing the
* message to be logged unconditionally (for most settings of
* log_min_messages). Otherwise, we return the argument unchanged.
* The message will then be shown based on the setting of log_min_messages.
* Intention is to keep this for at least the whole of the 9.0 production
* release, so we can more easily diagnose production problems in the field.
* It should go away eventually, though, because it's an ugly and
* hard-to-explain kluge.
*/
int
trace_recovery(int trace_level)
{
if (trace_level < LOG &&
trace_level >= trace_recovery_messages)
return trace_level;
}