log_csv_options_20110206.patch
text/x-diff
Filename: log_csv_options_20110206.patch
Type: text/x-diff
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: context
| File | + | − |
|---|---|---|
| doc/src/sgml/config.sgml | 184 | 0 |
| src/backend/commands/variable.c | 10 | 0 |
| src/backend/utils/error/elog.c | 440 | 0 |
| src/backend/utils/misc/guc.c | 21 | 0 |
| src/backend/utils/misc/postgresql.conf.sample | 4 | 0 |
| src/include/utils/elog.h | 34 | 0 |
| src/tools/pgindent/typedefs.list | 1 | 0 |
*** a/doc/src/sgml/config.sgml
--- b/doc/src/sgml/config.sgml
***************
*** 3519,3525 **** local0.* /var/log/postgresql
</row>
<row>
<entry><literal>%u</literal></entry>
! <entry>User name</entry>
<entry>yes</entry>
</row>
<row>
--- 3519,3538 ----
</row>
<row>
<entry><literal>%u</literal></entry>
! <entry>Session user name, typically the user name which was used
! to authenticate to <productname>PostgreSQL</productname> with,
! but can be changed by a superuser, see <command>SET SESSION
! AUTHORIZATION</></entry>
! <entry>yes</entry>
! </row>
! <row>
! <entry><literal>%U</literal></entry>
! <entry>Current role name, when set with <command>SET ROLE</>;
! the current role identifier is relevant for permission checking;
! Returns 'none' if the current role matches the session user.
! Note: Log messages from inside <literal>SECURITY DEFINER</>
! functions will show the calling role, not the effective role
! inside the <literal>SECURITY DEFINER</> function</entry>
<entry>yes</entry>
</row>
<row>
***************
*** 3636,3641 **** FROM pg_stat_activity;
--- 3649,3677 ----
</listitem>
</varlistentry>
+ <varlistentry id="guc-log-csv-fields" xreflabel="log_csv_fields">
+ <term><varname>log_csv_fields</varname> (<type>string</type>)</term>
+ <indexterm>
+ <primary><varname>log_csv_fields</> configuration parameter</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ Controls the set and order of the fields which are written out in
+ the CSV-format log file.
+
+ The default is: log_time, user_name, database_name, process_id,
+ connection_from, session_id, session_line_num, command_tag,
+ session_start_time, virtual_transaction_id, transaction_id,
+ error_severity, sql_state_code, message, detail, hint,
+ internal_query, internal_query_pos, context, query, query_pos,
+ location, application_name
+
+ For details on what these fields are, refer to the log_line_prefix
+ and CSV logging documentation.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="guc-log-lock-waits" xreflabel="log_lock_waits">
<term><varname>log_lock_waits</varname> (<type>boolean</type>)</term>
<indexterm>
***************
*** 3743,3776 **** FROM pg_stat_activity;
Including <literal>csvlog</> in the <varname>log_destination</> list
provides a convenient way to import log files into a database table.
This option emits log lines in comma-separated-values
! (<acronym>CSV</>) format,
! with these columns:
! timestamp with milliseconds,
! user name,
! database name,
! process ID,
! client host:port number,
! session ID,
! per-session line number,
! command tag,
! session start time,
! virtual transaction ID,
! regular transaction ID,
! error severity,
! SQLSTATE code,
! error message,
! error message detail,
! hint,
! internal query that led to the error (if any),
! character count of the error position therein,
! error context,
! user query that led to the error (if any and enabled by
! <varname>log_min_error_statement</>),
! character count of the error position therein,
! location of the error in the PostgreSQL source code
! (if <varname>log_error_verbosity</> is set to <literal>verbose</>),
! and application name.
! Here is a sample table definition for storing CSV-format log output:
<programlisting>
CREATE TABLE postgres_log
--- 3779,3931 ----
Including <literal>csvlog</> in the <varname>log_destination</> list
provides a convenient way to import log files into a database table.
This option emits log lines in comma-separated-values
! (<acronym>CSV</>) format. The following table defines the fields
! which can be included in the CSV output, their meanings, and if they
! are included in the default CSV layout (the default ordering matches
! the order of this table).
!
! <informaltable>
! <tgroup cols="3">
! <thead>
! <row>
! <entry>CSV Field Name</entry>
! <entry>Definition</entry>
! <entry>Included by Default</entry>
! </row>
! </thead>
! <tbody>
! <row>
! <entry><literal>log_time</literal></entry>
! <entry>timestamp with milliseconds</entry>
! <entry>yes</entry>
! </row>
! <row>
! <entry><literal>user_name</literal></entry>
! <entry>session user name</entry>
! <entry>yes</entry>
! </row>
! <row>
! <entry><literal>role_name</literal></entry>
! <entry>current role name</entry>
! <entry>no</entry>
! </row>
! <row>
! <entry><literal>database_name</literal></entry>
! <entry>name of database connected to</entry>
! <entry>yes</entry>
! </row>
! <row>
! <entry><literal>process_id</literal></entry>
! <entry>process ID of the backend PG process</entry>
! <entry>yes</entry>
! </row>
! <row>
! <entry><literal>connection_from</literal></entry>
! <entry>client host/IP and port number</entry>
! <entry>yes</entry>
! </row>
! <row>
! <entry><literal>session_id</literal></entry>
! <entry>ID of the session</entry>
! <entry>yes</entry>
! </row>
! <row>
! <entry><literal>session_line_number</literal></entry>
! <entry>per-session line number</entry>
! <entry>yes</entry>
! </row>
! <row>
! <entry><literal>command_tag</literal></entry>
! <entry>Command tag of the logged command</entry>
! <entry>yes</entry>
! </row>
! <row>
! <entry><literal>session_start_time</literal></entry>
! <entry>Start time of the current session</entry>
! <entry>yes</entry>
! </row>
! <row>
! <entry><literal>virtual_transaction_id</literal></entry>
! <entry>Virtual Transaction ID</entry>
! <entry>yes</entry>
! </row>
! <row>
! <entry><literal>transaction_id</literal></entry>
! <entry>Regular Transaction ID</entry>
! <entry>yes</entry>
! </row>
! <row>
! <entry><literal>error_severity</literal></entry>
! <entry>Error severity code of the log message</entry>
! <entry>yes</entry>
! </row>
! <row>
! <entry><literal>sql_state_code</literal></entry>
! <entry>SQLSTATE code of the command being logged</entry>
! <entry>yes</entry>
! </row>
! <row>
! <entry><literal>message</literal></entry>
! <entry>Error message</entry>
! <entry>yes</entry>
! </row>
! <row>
! <entry><literal>detail</literal></entry>
! <entry>Error message detail</entry>
! <entry>yes</entry>
! </row>
! <row>
! <entry><literal>hint</literal></entry>
! <entry>Error message hint</entry>
! <entry>yes</entry>
! </row>
! <row>
! <entry><literal>internal_query</literal></entry>
! <entry>internal query that led to the error (if any)</entry>
! <entry>yes</entry>
! </row>
! <row>
! <entry><literal>internal_query_pos</literal></entry>
! <entry>character count of the error position of the internal query</entry>
! <entry>yes</entry>
! </row>
! <row>
! <entry><literal>context</literal></entry>
! <entry>error context</entry>
! <entry>yes</entry>
! </row>
! <row>
! <entry><literal>query</literal></entry>
! <entry>user query that led to the error (if any and enabled by <varname>log_min_error_statement</varname>)</entry>
! <entry>yes</entry>
! </row>
! <row>
! <entry><literal>query_pos</literal></entry>
! <entry>character count of the error position of the user query</entry>
! <entry>yes</entry>
! </row>
! <row>
! <entry><literal>location</literal></entry>
! <entry>location of the error in the PostgreSQL source code (if <varname>log_error_verbosity</varname> is set to <literal>verbose</literal>)</entry>
! <entry>yes</entry>
! </row>
! <row>
! <entry><literal>application_name</literal></entry>
! <entry>Name of the connecting application, if provided by the application</entry>
! <entry>yes</entry>
! </row>
! </tbody>
! </tgroup>
! </informaltable>
!
! The set of columns to be included, and their order, in the CSV
! output can be controlled using the <varname>log_csv_fields</varname> option.
!
! For additional details on the definition of the above columns, refer
! to the documentation for <varname>log_line_prefix</varname>.
!
! Here is a sample table definition for storing the default CSV-format
! log output:
<programlisting>
CREATE TABLE postgres_log
*** a/src/backend/commands/variable.c
--- b/src/backend/commands/variable.c
***************
*** 809,814 **** assign_session_authorization(const char *value, bool doit, GucSource source)
--- 809,819 ----
return result;
}
+ /*
+ * function to return the stored session username, needed because we
+ * can't do catalog lookups when possibly being called after an error,
+ * eg: from elog.c or part of GUC handling.
+ */
const char *
show_session_authorization(void)
{
***************
*** 934,939 **** assign_role(const char *value, bool doit, GucSource source)
--- 939,949 ----
return result;
}
+ /*
+ * function to return the stored role username, needed because we
+ * can't do catalog lookups when possibly being called after an error,
+ * eg: from elog.c or part of GUC handling.
+ */
const char *
show_role(void)
{
*** a/src/backend/utils/error/elog.c
--- b/src/backend/utils/error/elog.c
***************
*** 3,8 ****
--- 3,17 ----
* elog.c
* error logging and reporting
*
+ * A few comments about situations where error processing is called:
+ *
+ * We need to be cautious of both a performance hit when logging, since
+ * log messages can be generated at a huge rate if every command is being
+ * logged and we also need to watch out for what can happen when we are
+ * trying to log from an aborted transaction. Specifically, attempting to
+ * do SysCache lookups and possibly use other usually available backend
+ * systems will fail badly when logging from an aborted transaction.
+ *
* Some notes about recursion and errors during error processing:
*
* We need to be robust about recursive-error scenarios --- for example,
***************
*** 59,73 ****
--- 68,85 ----
#include "access/transam.h"
#include "access/xact.h"
+ #include "commands/variable.h"
#include "libpq/libpq.h"
#include "libpq/pqformat.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
+ #include "nodes/pg_list.h"
#include "postmaster/postmaster.h"
#include "postmaster/syslogger.h"
#include "storage/ipc.h"
#include "storage/proc.h"
#include "tcop/tcopprot.h"
+ #include "utils/builtins.h"
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/ps_status.h"
***************
*** 93,98 **** extern bool redirection_done;
--- 105,113 ----
int Log_error_verbosity = PGERROR_VERBOSE;
char *Log_line_prefix = NULL; /* format for extra log line info */
int Log_destination = LOG_DESTINATION_STDERR;
+ char *Log_csv_fields = NULL;
+
+ static List *csv_log_fields = NIL;
#ifdef HAVE_SYSLOG
***************
*** 161,166 **** static void write_csvlog(ErrorData *edata);
--- 176,186 ----
static void setup_formatted_log_time(void);
static void setup_formatted_start_time(void);
+ /* extern'd and used from guc.c... */
+ const char *
+ assign_log_csv_fields(const char *newval, bool doit, GucSource source);
+
+
/*
* in_error_recursion_trouble --- are we at risk of infinite error recursion?
***************
*** 1817,1831 **** log_line_prefix(StringInfo buf, ErrorData *edata)
}
break;
case 'u':
- if (MyProcPort)
{
! const char *username = MyProcPort->user_name;
!
! if (username == NULL || *username == '\0')
! username = _("[unknown]");
! appendStringInfoString(buf, username);
}
break;
case 'd':
if (MyProcPort)
{
--- 1837,1860 ----
}
break;
case 'u':
{
! const char *session_auth = show_session_authorization();
! if (*session_auth != '\0')
! appendStringInfoString(buf, session_auth);
! else
! if (MyProcPort)
! {
! const char *username = MyProcPort->user_name;
!
! if (username == NULL || *username == '\0')
! username = _("[unknown]");
! appendStringInfoString(buf, username);
! }
}
break;
+ case 'U':
+ appendStringInfoString(buf, show_role());
+ break;
case 'd':
if (MyProcPort)
{
***************
*** 1921,1926 **** log_line_prefix(StringInfo buf, ErrorData *edata)
--- 1950,2112 ----
}
/*
+ * Build up the default set of CSV fields to output, in case we need it before
+ * GUC processing is done.
+ *
+ * This is more of a 'safety valve' than anything else,
+ * since GUC processing really should happen before we do any error logging.
+ * We might even want to change this eventually to just not log CSV format logs
+ * if this ever happens, to avoid a discrepency in the CSV log file which would
+ * make it difficult to load into PG.
+ */
+ void
+ build_default_csvlog_list(void)
+ {
+ List *new_csv_fields = NIL;
+ MemoryContext oldcontext;
+
+ oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_LOG_TIME);
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_USER_NAME);
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_DATABASE_NAME);
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_PROCESS_ID);
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_CONNECTION_FROM);
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_SESSION_ID);
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_SESSION_LINE_NUM);
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_COMMAND_TAG);
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_SESSION_START_TIME);
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_VIRTUAL_TRANSACTION_ID);
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_TRANSACTION_ID);
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_ERROR_SEVERITY);
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_SQL_STATE_CODE);
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_MESSAGE);
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_DETAIL);
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_HINT);
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_INTERNAL_QUERY);
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_INTERNAL_QUERY_POS);
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_CONTEXT);
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_QUERY);
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_QUERY_POS);
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_LOCATION);
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_APPLICATION_NAME);
+
+ /* put new list in place */
+ csv_log_fields = new_csv_fields;
+
+ MemoryContextSwitchTo(oldcontext);
+
+ return;
+ }
+
+
+ /*
+ * Process the list of fields to be sent to the CSV log file
+ */
+ const char *
+ assign_log_csv_fields(const char *newval, bool doit, GucSource source)
+ {
+ /* Verify the list is valid */
+ List *new_csv_fields = NIL;
+ List *column_list = NIL;
+ ListCell *l;
+ char *rawstring;
+ MemoryContext oldcontext;
+
+ /* Need a modifyable version to pass to SplitIdentifierString */
+ rawstring = pstrdup(newval);
+
+ /* Parse string into list of identifiers */
+ if (!SplitIdentifierString(rawstring, ',', &column_list))
+ {
+ list_free(column_list);
+ return NULL;
+ }
+
+ /*
+ * We need the allocations done for the csv_log_fields list to
+ * be preserved, so allocate them in TopMemoryContext.
+ */
+ oldcontext = MemoryContextSwitchTo(TopMemoryContext);
+
+ /*
+ * Loop through all of the fields provided by the user and build
+ * up our new_csv_fields list which will be processed by write_csvlog
+ */
+ foreach(l, column_list)
+ {
+ if (pg_strcasecmp(lfirst(l),"log_time") == 0)
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_LOG_TIME);
+ else if (pg_strcasecmp(lfirst(l),"user_name") == 0)
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_USER_NAME);
+ else if (pg_strcasecmp(lfirst(l),"role_name") == 0)
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_ROLE_NAME);
+ else if (pg_strcasecmp(lfirst(l),"database_name") == 0)
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_DATABASE_NAME);
+ else if (pg_strcasecmp(lfirst(l),"process_id") == 0)
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_PROCESS_ID);
+ else if (pg_strcasecmp(lfirst(l),"connection_from") == 0)
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_CONNECTION_FROM);
+ else if (pg_strcasecmp(lfirst(l),"session_id") == 0)
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_SESSION_ID);
+ else if (pg_strcasecmp(lfirst(l),"session_line_num") == 0)
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_SESSION_LINE_NUM);
+ else if (pg_strcasecmp(lfirst(l),"command_tag") == 0)
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_COMMAND_TAG);
+ else if (pg_strcasecmp(lfirst(l),"session_start_time") == 0)
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_SESSION_START_TIME);
+ else if (pg_strcasecmp(lfirst(l),"virtual_transaction_id") == 0)
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_VIRTUAL_TRANSACTION_ID);
+ else if (pg_strcasecmp(lfirst(l),"transaction_id") == 0)
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_TRANSACTION_ID);
+ else if (pg_strcasecmp(lfirst(l),"error_severity") == 0)
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_ERROR_SEVERITY);
+ else if (pg_strcasecmp(lfirst(l),"sql_state_code") == 0)
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_SQL_STATE_CODE);
+ else if (pg_strcasecmp(lfirst(l),"message") == 0)
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_MESSAGE);
+ else if (pg_strcasecmp(lfirst(l),"detail") == 0)
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_DETAIL);
+ else if (pg_strcasecmp(lfirst(l),"hint") == 0)
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_HINT);
+ else if (pg_strcasecmp(lfirst(l),"internal_query") == 0)
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_INTERNAL_QUERY);
+ else if (pg_strcasecmp(lfirst(l),"internal_query_pos") == 0)
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_INTERNAL_QUERY_POS);
+ else if (pg_strcasecmp(lfirst(l),"context") == 0)
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_CONTEXT);
+ else if (pg_strcasecmp(lfirst(l),"query") == 0)
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_QUERY);
+ else if (pg_strcasecmp(lfirst(l),"query_pos") == 0)
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_QUERY_POS);
+ else if (pg_strcasecmp(lfirst(l),"location") == 0)
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_LOCATION);
+ else if (pg_strcasecmp(lfirst(l),"application_name") == 0)
+ new_csv_fields = lappend_int(new_csv_fields,CSVLOG_APPLICATION_NAME);
+ else
+ {
+ /* handle error, might need to do better than this */
+ return NULL;
+ }
+ }
+
+ if (doit)
+ {
+ /* put new list in place */
+ List *old_list = csv_log_fields;
+
+ csv_log_fields = new_csv_fields;
+
+ list_free(old_list);
+ }
+
+ /* Switch back to the calling context */
+ MemoryContextSwitchTo(oldcontext);
+
+ return newval;
+ }
+
+ /*
* append a CSV'd version of a string to a StringInfo
* We use the PostgreSQL defaults for CSV, i.e. quote = escape = '"'
* If it's NULL, append nothing.
***************
*** 1946,1957 **** appendCSVLiteral(StringInfo buf, const char *data)
}
/*
! * Constructs the error message, depending on the Errordata it gets, in a CSV
! * format which is described in doc/src/sgml/config.sgml.
*/
static void
write_csvlog(ErrorData *edata)
{
StringInfoData buf;
bool print_stmt = false;
--- 2132,2145 ----
}
/*
! * Constructs the error message, depending on the Errordata it gets, in the CSV
! * format requested by the user, based on the log_csv_fields GUC.
*/
static void
write_csvlog(ErrorData *edata)
{
+ int num_fields;
+ int curr_field = 0;
StringInfoData buf;
bool print_stmt = false;
***************
*** 1961,1966 **** write_csvlog(ErrorData *edata)
--- 2149,2158 ----
/* has counter been reset in current process? */
static int log_my_pid = 0;
+ ListCell *l;
+
+ const char *session_auth = show_session_authorization();
+
/*
* This is one of the few places where we'd rather not inherit a static
* variable's value from the postmaster. But since we will, reset it when
***************
*** 1977,2134 **** write_csvlog(ErrorData *edata)
initStringInfo(&buf);
/*
! * timestamp with milliseconds
! *
! * Check if the timestamp is already calculated for the syslog message,
! * and use it if so. Otherwise, get the current timestamp. This is done
! * to put same timestamp in both syslog and csvlog messages.
*/
! if (formatted_log_time[0] == '\0')
! setup_formatted_log_time();
! appendStringInfoString(&buf, formatted_log_time);
! appendStringInfoChar(&buf, ',');
! /* username */
! if (MyProcPort)
! appendCSVLiteral(&buf, MyProcPort->user_name);
! appendStringInfoChar(&buf, ',');
! /* database name */
! if (MyProcPort)
! appendCSVLiteral(&buf, MyProcPort->database_name);
! appendStringInfoChar(&buf, ',');
! /* Process id */
! if (MyProcPid != 0)
! appendStringInfo(&buf, "%d", MyProcPid);
! appendStringInfoChar(&buf, ',');
! /* Remote host and port */
! if (MyProcPort && MyProcPort->remote_host)
! {
! appendStringInfoChar(&buf, '"');
! appendStringInfoString(&buf, MyProcPort->remote_host);
! if (MyProcPort->remote_port && MyProcPort->remote_port[0] != '\0')
! {
! appendStringInfoChar(&buf, ':');
! appendStringInfoString(&buf, MyProcPort->remote_port);
! }
! appendStringInfoChar(&buf, '"');
! }
! appendStringInfoChar(&buf, ',');
! /* session id */
! appendStringInfo(&buf, "%lx.%x", (long) MyStartTime, MyProcPid);
! appendStringInfoChar(&buf, ',');
! /* Line number */
! appendStringInfo(&buf, "%ld", log_line_number);
! appendStringInfoChar(&buf, ',');
! /* PS display */
! if (MyProcPort)
! {
! StringInfoData msgbuf;
! const char *psdisp;
! int displen;
! initStringInfo(&msgbuf);
! psdisp = get_ps_display(&displen);
! appendBinaryStringInfo(&msgbuf, psdisp, displen);
! appendCSVLiteral(&buf, msgbuf.data);
! pfree(msgbuf.data);
! }
! appendStringInfoChar(&buf, ',');
! /* session start timestamp */
! if (formatted_start_time[0] == '\0')
! setup_formatted_start_time();
! appendStringInfoString(&buf, formatted_start_time);
! appendStringInfoChar(&buf, ',');
! /* Virtual transaction id */
! /* keep VXID format in sync with lockfuncs.c */
! if (MyProc != NULL && MyProc->backendId != InvalidBackendId)
! appendStringInfo(&buf, "%d/%u", MyProc->backendId, MyProc->lxid);
! appendStringInfoChar(&buf, ',');
! /* Transaction id */
! appendStringInfo(&buf, "%u", GetTopTransactionIdIfAny());
! appendStringInfoChar(&buf, ',');
! /* Error severity */
! appendStringInfoString(&buf, error_severity(edata->elevel));
! appendStringInfoChar(&buf, ',');
! /* SQL state code */
! appendStringInfoString(&buf, unpack_sql_state(edata->sqlerrcode));
! appendStringInfoChar(&buf, ',');
! /* errmessage */
! appendCSVLiteral(&buf, edata->message);
! appendStringInfoChar(&buf, ',');
! /* errdetail or errdetail_log */
! if (edata->detail_log)
! appendCSVLiteral(&buf, edata->detail_log);
! else
! appendCSVLiteral(&buf, edata->detail);
! appendStringInfoChar(&buf, ',');
! /* errhint */
! appendCSVLiteral(&buf, edata->hint);
! appendStringInfoChar(&buf, ',');
! /* internal query */
! appendCSVLiteral(&buf, edata->internalquery);
! appendStringInfoChar(&buf, ',');
! /* if printed internal query, print internal pos too */
! if (edata->internalpos > 0 && edata->internalquery != NULL)
! appendStringInfo(&buf, "%d", edata->internalpos);
! appendStringInfoChar(&buf, ',');
! /* errcontext */
! appendCSVLiteral(&buf, edata->context);
! appendStringInfoChar(&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);
! appendStringInfoChar(&buf, ',');
! if (print_stmt && edata->cursorpos > 0)
! appendStringInfo(&buf, "%d", edata->cursorpos);
! appendStringInfoChar(&buf, ',');
!
! /* file error location */
! if (Log_error_verbosity >= PGERROR_VERBOSE)
! {
! StringInfoData msgbuf;
!
! 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);
! }
! appendStringInfoChar(&buf, ',');
! /* application name */
! if (application_name)
! appendCSVLiteral(&buf, application_name);
appendStringInfoChar(&buf, '\n');
--- 2169,2437 ----
initStringInfo(&buf);
/*
! * Get the number of fields, so we make sure to *not* include a comma
! * after the last field.
*/
! num_fields = list_length(csv_log_fields);
! /*
! * Loop through the fields requested by the user, in the order requested, in
! * the log_csv_fields GUC.
! */
! foreach(l, csv_log_fields)
! {
! /* Update which field we are on, needed to check if it's the last field */
! curr_field++;
! switch (lfirst_int(l))
! {
! case CSVLOG_LOG_TIME:
! {
! /*
! * timestamp with milliseconds
! *
! * Check if the timestamp is already calculated for the syslog message,
! * and use it if so. Otherwise, get the current timestamp. This is done
! * to put same timestamp in both syslog and csvlog messages.
! */
! if (formatted_log_time[0] == '\0')
! setup_formatted_log_time();
!
! appendStringInfoString(&buf, formatted_log_time);
! if (curr_field != num_fields) appendStringInfoChar(&buf, ',');
! }
! break;
! case CSVLOG_USER_NAME:
! {
! /* session username, as done for %u */
! if (*session_auth != '\0')
! appendCSVLiteral(&buf, session_auth);
! else
! /* username */
! if (MyProcPort)
! {
! const char *username = MyProcPort->user_name;
! if (username == NULL || *username == '\0')
! username = _("[unknown]");
! appendCSVLiteral(&buf, MyProcPort->user_name);
! }
! if (curr_field != num_fields) appendStringInfoChar(&buf, ',');
! }
! break;
! case CSVLOG_ROLE_NAME:
! /* current role, not updated if someone renames it in another
! * session, of course */
! appendCSVLiteral(&buf, show_role());
! if (curr_field != num_fields) appendStringInfoChar(&buf, ',');
! break;
! case CSVLOG_DATABASE_NAME:
! {
! /* database name */
! if (MyProcPort)
! appendCSVLiteral(&buf, MyProcPort->database_name);
! if (curr_field != num_fields) appendStringInfoChar(&buf, ',');
! }
! break;
! case CSVLOG_PROCESS_ID:
! {
! /* Process id */
! if (MyProcPid != 0)
! appendStringInfo(&buf, "%d", MyProcPid);
! if (curr_field != num_fields) appendStringInfoChar(&buf, ',');
! }
! break;
! case CSVLOG_CONNECTION_FROM:
! {
! /* Remote host and port */
! if (MyProcPort && MyProcPort->remote_host)
! {
! appendStringInfoChar(&buf, '"');
! appendStringInfoString(&buf, MyProcPort->remote_host);
! if (MyProcPort->remote_port && MyProcPort->remote_port[0] != '\0')
! {
! appendStringInfoChar(&buf, ':');
! appendStringInfoString(&buf, MyProcPort->remote_port);
! }
! appendStringInfoChar(&buf, '"');
! }
! if (curr_field != num_fields) appendStringInfoChar(&buf, ',');
! }
! break;
! case CSVLOG_SESSION_ID:
! /* session id */
! appendStringInfo(&buf, "%lx.%x", (long) MyStartTime, MyProcPid);
! if (curr_field != num_fields) appendStringInfoChar(&buf, ',');
! break;
! case CSVLOG_SESSION_LINE_NUM:
! /* Line number */
! appendStringInfo(&buf, "%ld", log_line_number);
! if (curr_field != num_fields) appendStringInfoChar(&buf, ',');
! break;
! case CSVLOG_COMMAND_TAG:
! {
! /* PS display */
! if (MyProcPort)
! {
! StringInfoData msgbuf;
! const char *psdisp;
! int displen;
!
! initStringInfo(&msgbuf);
!
! psdisp = get_ps_display(&displen);
! appendBinaryStringInfo(&msgbuf, psdisp, displen);
! appendCSVLiteral(&buf, msgbuf.data);
!
! pfree(msgbuf.data);
! }
! if (curr_field != num_fields) appendStringInfoChar(&buf, ',');
! }
! break;
! case CSVLOG_SESSION_START_TIME:
! {
! /* session start timestamp */
! if (formatted_start_time[0] == '\0')
! setup_formatted_start_time();
! appendStringInfoString(&buf, formatted_start_time);
! if (curr_field != num_fields) appendStringInfoChar(&buf, ',');
! }
! break;
! case CSVLOG_VIRTUAL_TRANSACTION_ID:
! {
! /* Virtual transaction id */
! /* keep VXID format in sync with lockfuncs.c */
! if (MyProc != NULL && MyProc->backendId != InvalidBackendId)
! appendStringInfo(&buf, "%d/%u", MyProc->backendId, MyProc->lxid);
! if (curr_field != num_fields) appendStringInfoChar(&buf, ',');
! }
! break;
! case CSVLOG_TRANSACTION_ID:
! /* Transaction id */
! appendStringInfo(&buf, "%u", GetTopTransactionIdIfAny());
! if (curr_field != num_fields) appendStringInfoChar(&buf, ',');
! break;
! case CSVLOG_ERROR_SEVERITY:
! /* Error severity */
! appendStringInfoString(&buf, error_severity(edata->elevel));
! if (curr_field != num_fields) appendStringInfoChar(&buf, ',');
! break;
! case CSVLOG_SQL_STATE_CODE:
! /* SQL state code */
! appendStringInfoString(&buf, unpack_sql_state(edata->sqlerrcode));
! if (curr_field != num_fields) appendStringInfoChar(&buf, ',');
! break;
! case CSVLOG_MESSAGE:
! /* errmessage */
! appendCSVLiteral(&buf, edata->message);
! if (curr_field != num_fields) appendStringInfoChar(&buf, ',');
! break;
! case CSVLOG_DETAIL:
! {
! /* errdetail or errdetail_log */
! if (edata->detail_log)
! appendCSVLiteral(&buf, edata->detail_log);
! else
! appendCSVLiteral(&buf, edata->detail);
! if (curr_field != num_fields) appendStringInfoChar(&buf, ',');
! }
! break;
! case CSVLOG_HINT:
! /* errhint */
! appendCSVLiteral(&buf, edata->hint);
! if (curr_field != num_fields) appendStringInfoChar(&buf, ',');
! break;
! case CSVLOG_INTERNAL_QUERY:
! /* internal query */
! appendCSVLiteral(&buf, edata->internalquery);
! if (curr_field != num_fields) appendStringInfoChar(&buf, ',');
! break;
! case CSVLOG_INTERNAL_QUERY_POS:
! {
! /* if printed internal query, print internal pos too */
! if (edata->internalpos > 0 && edata->internalquery != NULL)
! appendStringInfo(&buf, "%d", edata->internalpos);
! if (curr_field != num_fields) appendStringInfoChar(&buf, ',');
! }
! break;
! case CSVLOG_CONTEXT:
! /* errcontext */
! appendCSVLiteral(&buf, edata->context);
! if (curr_field != num_fields) appendStringInfoChar(&buf, ',');
! break;
! case CSVLOG_QUERY:
! {
! /* 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);
! if (curr_field != num_fields) appendStringInfoChar(&buf, ',');
! }
! break;
! case CSVLOG_QUERY_POS:
! {
! if (print_stmt && edata->cursorpos > 0)
! appendStringInfo(&buf, "%d", edata->cursorpos);
! if (curr_field != num_fields) appendStringInfoChar(&buf, ',');
! }
! break;
! case CSVLOG_LOCATION:
! {
! /* file error location */
! if (Log_error_verbosity >= PGERROR_VERBOSE)
! {
! StringInfoData msgbuf;
!
! 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);
! }
! if (curr_field != num_fields) appendStringInfoChar(&buf, ',');
! }
! break;
!
! case CSVLOG_APPLICATION_NAME:
! {
! /* application name */
! if (application_name)
! appendCSVLiteral(&buf, application_name);
! if (curr_field != num_fields) appendStringInfoChar(&buf, ',');
! }
! break;
! }
! }
appendStringInfoChar(&buf, '\n');
***************
*** 2139,2144 **** write_csvlog(ErrorData *edata)
--- 2442,2449 ----
write_pipe_chunks(buf.data, buf.len, LOG_DESTINATION_CSVLOG);
pfree(buf.data);
+
+ return;
}
/*
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
***************
*** 63,68 ****
--- 63,69 ----
#include "tsearch/ts_cache.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+ #include "utils/elog.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
#include "utils/pg_locale.h"
***************
*** 189,194 **** static char *config_enum_get_options(struct config_enum * record,
--- 190,198 ----
const char *prefix, const char *suffix,
const char *separator);
+ /* Needs to be defined here because elog.h can't #include guc.h */
+ extern const char *assign_log_csv_fields(const char *newval,
+ bool doit, GucSource source);
/*
* Options for enum values defined in this module.
***************
*** 2286,2291 **** static struct config_string ConfigureNamesString[] =
--- 2290,2306 ----
},
{
+ {"log_csv_fields", PGC_POSTMASTER, LOGGING_WHAT,
+ gettext_noop("Controls fields logged to CSV logfiles."),
+ gettext_noop("If blank, the default set of fields is used."),
+ GUC_LIST_INPUT
+ },
+ &Log_csv_fields,
+ "log_time, user_name, database_name, process_id, connection_from, session_id, session_line_num, command_tag, session_start_time, virtual_transaction_id, transaction_id, error_severity, sql_state_code, message, detail, hint, internal_query, internal_query_pos, context, query, query_pos, location, application_name",
+ assign_log_csv_fields, NULL
+ },
+
+ {
{"log_timezone", PGC_SIGHUP, LOGGING_WHAT,
gettext_noop("Sets the time zone to use in log messages."),
NULL
***************
*** 3435,3440 **** InitializeGUCOptions(void)
--- 3450,3461 ----
pg_timezone_pre_initialize();
/*
+ * Ditto for log_csv_fields, have to set it to something before we get
+ * too far along.
+ */
+ build_default_csvlog_list();
+
+ /*
* Build sorted array of all GUC variables.
*/
build_guc_variables();
*** a/src/backend/utils/misc/postgresql.conf.sample
--- b/src/backend/utils/misc/postgresql.conf.sample
***************
*** 377,382 ****
--- 377,386 ----
# processes
# %% = '%'
# e.g. '<%u%%%d> '
+
+ # fields to include in the CSV log output
+ #log_csv_fields = 'log_time, user_name, database_name, process_id, connection_from, session_id, session_line_num, command_tag, session_start_time, virtual_transaction_id, transaction_id, error_severity, sql_state_code, message, detail, hint, internal_query, internal_query_pos, context, query, query_pos, location, application_name'
+
#log_lock_waits = off # log lock waits >= deadlock_timeout
#log_statement = 'none' # none, ddl, mod, all
#log_temp_files = -1 # log temporary files equal or larger
*** a/src/include/utils/elog.h
--- b/src/include/utils/elog.h
***************
*** 330,337 **** typedef enum
--- 330,366 ----
extern int Log_error_verbosity;
extern char *Log_line_prefix;
+ extern char *Log_csv_fields;
extern int Log_destination;
+ typedef enum LogCSVFields
+ {
+ CSVLOG_LOG_TIME,
+ CSVLOG_USER_NAME,
+ CSVLOG_ROLE_NAME,
+ CSVLOG_DATABASE_NAME,
+ CSVLOG_PROCESS_ID,
+ CSVLOG_CONNECTION_FROM,
+ CSVLOG_SESSION_ID,
+ CSVLOG_SESSION_LINE_NUM,
+ CSVLOG_COMMAND_TAG,
+ CSVLOG_SESSION_START_TIME,
+ CSVLOG_VIRTUAL_TRANSACTION_ID,
+ CSVLOG_TRANSACTION_ID,
+ CSVLOG_ERROR_SEVERITY,
+ CSVLOG_SQL_STATE_CODE,
+ CSVLOG_MESSAGE,
+ CSVLOG_DETAIL,
+ CSVLOG_HINT,
+ CSVLOG_INTERNAL_QUERY,
+ CSVLOG_INTERNAL_QUERY_POS,
+ CSVLOG_CONTEXT,
+ CSVLOG_QUERY,
+ CSVLOG_QUERY_POS,
+ CSVLOG_LOCATION,
+ CSVLOG_APPLICATION_NAME
+ } LogCSVFields;
+
/* Log destination bitmap */
#define LOG_DESTINATION_STDERR 1
#define LOG_DESTINATION_SYSLOG 2
***************
*** 343,348 **** extern void DebugFileOpen(void);
--- 372,382 ----
extern char *unpack_sql_state(int sql_state);
extern bool in_error_recursion_trouble(void);
+ /* Used by guc.c to set up the default set of
+ * csv fields to log
+ */
+ extern void build_default_csvlog_list(void);
+
#ifdef HAVE_SYSLOG
extern void set_syslog_parameters(const char *ident, int facility);
#endif
*** a/src/tools/pgindent/typedefs.list
--- b/src/tools/pgindent/typedefs.list
***************
*** 854,859 **** LockTagType
--- 854,860 ----
LockTupleMode
LockingClause
LogStmtLevel
+ LogCSVFields
LogicalTape
LogicalTapeSet
MAGIC