简介
PostgreSQL服务器发出的所有消息都分配了五个字符的错误代码, 这些代码遵循 SQL 的"SQLSTATE"代码的约定。 需要知道发生了什么错误条件的应用程序通常应该检测错误代码,而不是查看文本错误消息。
根据标准,错误代码的前两个字符表示错误类别,而后三个字符表示在该类别内特定的条件。 因此,那些不能识别特定错误代码的应用仍然可以从错误类别中推断要做什么。
错误代码规则
参考:https://www.postgresql.org/docs/9.5/static/errcodes-appendix.html
应用
postgresql.conf文件中的配置如下:
logging_collector = on # Enable capturing of stderr and csvlog
# into log files. Required to be on for
# csvlogs.
# (change requires restart)
log_error_verbosity = verbose # terse, default, or verbose messages
配置文件修改完成后重启数据库,查看data目录下的pg_log内的文件内容:
[postgres@localhost pg_log]$ cat postgresql-2017-03-29_094442.log
LOG: 00000: database system was shut down at 2017-03-29 09:44:41 CST
LOCATION: StartupXLOG, xlog.c:5909
LOG: 00000: MultiXact member wraparound protections are now enabled
LOCATION: SetOffsetVacuumLimit, multixact.c:2629
LOG: 00000: database system is ready to accept connections
LOCATION: reaper, postmaster.c:2788
LOG: 00000: autovacuum launcher started
LOCATION: AutoVacLauncherMain, autovacuum.c:413
在数据库中执行如下操作:
postgres=# create tablespace tbs1 location '/home1/aa';
查看data目录下的pg_log内的文件内容:
ERROR: 58P01: directory "/home1/aa" does not exist
LOCATION: create_tablespace_directories, tablespace.c:586
STATEMENT: create tablespace tbs1 location '/home1/aa';
综上,当启用参数log_error_verbosity=VERBOSE时会在日志中打印错误代码。
相关代码
/*
* Write error report to server's log
*/
static void
send_message_to_server_log(ErrorData *edata)
{
StringInfoData buf;
initStringInfo(&buf);
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);
else
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 && !edata->hide_ctx)
{
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)
{
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);
}
#endif /* HAVE_SYSLOG */
#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 */
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)
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
else
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 */
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.
*/
pfree(buf.data);
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);
}
}
else
{
pfree(buf.data);
}
}