Hi,

I've been working on the following TODO item and attached is an initial patch. 
(It is only partial and not yet completely functional)

"Allow server log information to be output as INSERT statements 
This would allow server log information to be easily loaded into a database for 
analysis. "

I want to confirm, if what I have done so far is what community is looking for 
and also want to clear some doubts.

What is done so far
---------------

Two postgresql.conf variables

#log_output_type = 'text'               #Valid values are 'SQL' or 'text'
#log_output_table_name = 'auditlogs'

These control how to output the log. Defaults to 'text' which is status quo. If 
it is set to 'SQL' log will be output as INSERT commands.

The second variable is of interest. We need to specify a table in the insert 
command. My preferred option is for the user to give one and he can create it 
if and when he wants to. The alternative is we decide the table name and make 
initdb to create one.  

The proposed log output structure
------------------
INSERT INTO user_defined_table values( timestamp_with_milliseconds,  timestamp, 
username,  databasename, sessionid,  host_and_port, host, proc_id, command_tag, 
 session_start, transaction_id,  error_severity,  SQL_State_Code, 
error_message);

All these columns will follow the current rules of log output. ie, unless 
explicity requested by the user, these columns will have NULL. User can still 
give log_line_prefix in any order he wants, and logger will output it in 
appropriate columns. The code has been modified to do 
this.

Issues/Questions are:
- How about 'Statement duration log'.  This will come to the logger as a single 
string and after the query execution. In the existing log we can make sense of 
the duration log by matching it with the statement above it or by the statement 
which gets printed besides it (Again as 
a single string). But when this is loaded onto a table doesn't make much sense 
untless everything is in a single row. (My preferred option is to add another 
column to the table structure defined above as 'duration'. But haven't figured 
out how to achieve this, because the 
statement is printed first and then the duration as another log.)

- If the SQL log output is to the syslog, then it becomes pretty awkward and 
possibly useless because our current syslog writer function breaks up the log 
into several lines to accomodate various platforms. Syslog also then adds other 
information before outputting it, which 
cannot be loaded onto a table. The preferred option is to educate the user 
through documentation that SQL type log output is best served when it is output 
to stderr and redirected to a file? Same goes with other aspects such as 
verbose and various other statistics log. 

- There are also other minor issues such as, the actual query currently gets 
output in log as 'Statement: CREATE ........'. For sql type log we may not need 
the 'Statement:' part as it will be in a column ? Do we remove this in both 
text and SQL outputs ?

Rgds,
Arul Shaji

This is an email from Fujitsu Australia Software Technology Pty Ltd, ABN 27 003 
693 481. It is confidential to the ordinary user of the email address to which 
it was addressed and may contain copyright and/or legally privileged 
information. No one else may read, print, store, copy or forward all or any of 
it or its attachments. If you receive this email in error, please return to 
sender. Thank you.

If you do not wish to receive commercial email messages from Fujitsu Australia 
Software Technology Pty Ltd, please email [EMAIL PROTECTED]
*** pgsql/src/backend/utils/misc/postgresql.conf.sample	2007-02-01 11:38:25.000000000 +1100
--- workingpgsql/src/backend/utils/misc/postgresql.conf.sample	2007-02-14 15:30:45.000000000 +1100
***************
*** 257,262 ****
--- 257,267 ----
  #syslog_facility = 'LOCAL0'
  #syslog_ident = 'postgres'
  
+ # - How to Log -
+ 
+ #log_output_type = 'text'		#Valid values are 'SQL' or 'text'
+ #log_output_table_name = 'auditlogs'
+ 
  
  # - When to Log -
  
*** pgsql/src/backend/utils/misc/guc.c	2007-02-14 15:16:24.000000000 +1100
--- workingpgsql/src/backend/utils/misc/guc.c	2007-02-14 16:14:03.000000000 +1100
***************
*** 153,158 ****
--- 153,159 ----
  static const char *show_tcp_keepalives_idle(void);
  static const char *show_tcp_keepalives_interval(void);
  static const char *show_tcp_keepalives_count(void);
+ static const char *check_logtype_combination(const char *facility, bool doit, GucSource source);
  
  /*
   * GUC option variables that are exported from this module
***************
*** 210,215 ****
--- 211,217 ----
  static char *log_statement_str;
  static char *log_min_error_statement_str;
  static char *log_destination_string;
+ static char *log_output_type_string;
  
  #ifdef HAVE_SYSLOG
  static char *syslog_facility_str;
***************
*** 324,329 ****
--- 326,333 ----
  	gettext_noop("Reporting and Logging"),
  	/* LOGGING_WHERE */
  	gettext_noop("Reporting and Logging / Where to Log"),
+ 	/* LOGGING_HOW */
+ 	gettext_noop("Reporting and Logging / How to Log"),
  	/* LOGGING_WHEN */
  	gettext_noop("Reporting and Logging / When to Log"),
  	/* LOGGING_WHAT */
***************
*** 2158,2163 ****
--- 2162,2187 ----
  	},
  #endif
  
+ 	/*
+ 	 * For the logs to be output as INSERT statements
+ 	 */
+ 	{
+ 		{"log_output_table_name", PGC_POSTMASTER, LOGGING_HOW,
+ 			gettext_noop("The name of the table where the logs will be uploaded to."),
+ 			NULL
+ 		},
+ 		&Log_output_table_name,
+ 		"", NULL, NULL
+ 	},
+ 	{
+ 		{"log_output_type", PGC_POSTMASTER, LOGGING_HOW,
+ 			gettext_noop("Decides the output type of the log. Text or SQL."),
+ 			gettext_noop("Valid values are text, SQL.")
+ 		},
+ 		&log_output_type_string,
+ 		"text", check_logtype_combination, NULL
+ 	},
+ 
  	{
  		{"TimeZone", PGC_USERSET, CLIENT_CONN_LOCALE,
  			gettext_noop("Sets the time zone for displaying and interpreting time stamps."),
***************
*** 6022,6027 ****
--- 6046,6068 ----
  	return value;
  }
  
+ /*
+  * Check and set the log output type. SQL or text.
+  * Defaults to text.
+  */
+ static const char *
+ check_logtype_combination(const char *value, bool doit, GucSource source)
+ {
+ 	Log_output_sql = false;
+ 
+ 	if ( (value != NULL) && (pg_strcasecmp(value, "SQL") == 0) )
+ 	{
+ 			Log_output_sql = true;
+ 	}
+ 
+ 	return value;
+ }
+ 
  #ifdef HAVE_SYSLOG
  
  static const char *
*** pgsql/src/include/utils/elog.h	2007-01-16 16:29:39.000000000 +1100
--- workingpgsql/src/include/utils/elog.h	2007-02-14 16:21:21.000000000 +1100
***************
*** 273,278 ****
--- 273,280 ----
  extern PGErrorVerbosity Log_error_verbosity;
  extern char *Log_line_prefix;
  extern int	Log_destination;
+ extern bool Log_output_sql;
+ extern char *Log_output_table_name;
  
  /* Log destination bitmap */
  #define LOG_DESTINATION_STDERR	 1
*** pgsql/src/include/utils/guc_tables.h	2007-01-16 16:29:40.000000000 +1100
--- workingpgsql/src/include/utils/guc_tables.h	2007-02-14 16:22:41.000000000 +1100
***************
*** 59,64 ****
--- 59,65 ----
  	QUERY_TUNING_OTHER,
  	LOGGING,
  	LOGGING_WHERE,
+ 	LOGGING_HOW,
  	LOGGING_WHEN,
  	LOGGING_WHAT,
  	STATS,
*** pgsql/src/backend/utils/error/elog.c	2007-02-14 15:16:22.000000000 +1100
--- workingpgsql/src/backend/utils/error/elog.c	2007-02-15 11:36:57.000000000 +1100
***************
*** 83,88 ****
--- 83,91 ----
  char	   *Log_line_prefix = NULL;		/* format for extra log line info */
  int			Log_destination = LOG_DESTINATION_STDERR;
  
+ bool	Log_output_sql = false;
+ char	*Log_output_table_name = NULL;
+ 
  #ifdef HAVE_SYSLOG
  static bool openlog_done = false;
  static char *syslog_ident = NULL;
***************
*** 116,122 ****
  	} 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);
--- 119,125 ----
  	} while (0)
  
  
! static void log_line_prefix(char *loglineprefix, 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);
***************
*** 124,130 ****
  static const char *error_severity(int elevel);
  static void append_with_tabs(StringInfo buf, const char *str);
  static bool is_log_level_output(int elevel, int log_min_level);
! 
  
  /*
   * errstart --- begin an error-reporting cycle
--- 127,135 ----
  static const char *error_severity(int elevel);
  static void append_with_tabs(StringInfo buf, const char *str);
  static bool is_log_level_output(int elevel, int log_min_level);
! static void get_SQL_type_server_log(StringInfo buf, ErrorData *edata);
! static void get_timestamp(StringInfo buf);
! static void get_error_message(StringInfo buf, ErrorData *edata);
  
  /*
   * errstart --- begin an error-reporting cycle
***************
*** 1330,1336 ****
   * Format tag info for log lines; append to the provided buffer.
   */
  static void
! log_line_prefix(StringInfo buf)
  {
  	/* static counter for line numbers */
  	static long log_line_number = 0;
--- 1335,1341 ----
   * Format tag info for log lines; append to the provided buffer.
   */
  static void
! log_line_prefix(char *loglineprefix, StringInfo buf)
  {
  	/* static counter for line numbers */
  	static long log_line_number = 0;
***************
*** 1353,1369 ****
  	}
  	log_line_number++;
  
! 	if (Log_line_prefix == NULL)
  		return;					/* in case guc hasn't run yet */
  
! 	format_len = strlen(Log_line_prefix);
  
  	for (i = 0; i < format_len; i++)
  	{
! 		if (Log_line_prefix[i] != '%')
  		{
  			/* literal char, just copy */
! 			appendStringInfoChar(buf, Log_line_prefix[i]);
  			continue;
  		}
  		/* go to char after '%' */
--- 1358,1374 ----
  	}
  	log_line_number++;
  
! 	if (loglineprefix == NULL)
  		return;					/* in case guc hasn't run yet */
  
! 	format_len = strlen(loglineprefix);
  
  	for (i = 0; i < format_len; i++)
  	{
! 		if (loglineprefix[i] != '%')
  		{
  			/* literal char, just copy */
! 			appendStringInfoChar(buf, loglineprefix[i]);
  			continue;
  		}
  		/* go to char after '%' */
***************
*** 1372,1378 ****
  			break;				/* format error - ignore it */
  
  		/* process the option */
! 		switch (Log_line_prefix[i])
  		{
  			case 'u':
  				if (MyProcPort)
--- 1377,1383 ----
  			break;				/* format error - ignore it */
  
  		/* process the option */
! 		switch (loglineprefix[i])
  		{
  			case 'u':
  				if (MyProcPort)
***************
*** 1557,1641 ****
  
  	initStringInfo(&buf);
  
! 	log_line_prefix(&buf);
! 	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_line_prefix(&buf);
! 			appendStringInfoString(&buf, _("DETAIL:  "));
! 			append_with_tabs(&buf, edata->detail);
! 			appendStringInfoChar(&buf, '\n');
! 		}
! 		if (edata->hint)
! 		{
! 			log_line_prefix(&buf);
! 			appendStringInfoString(&buf, _("HINT:  "));
! 			append_with_tabs(&buf, edata->hint);
! 			appendStringInfoChar(&buf, '\n');
! 		}
! 		if (edata->internalquery)
! 		{
! 			log_line_prefix(&buf);
! 			appendStringInfoString(&buf, _("QUERY:  "));
! 			append_with_tabs(&buf, edata->internalquery);
! 			appendStringInfoChar(&buf, '\n');
! 		}
! 		if (edata->context)
! 		{
! 			log_line_prefix(&buf);
! 			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);
! 				appendStringInfo(&buf, _("LOCATION:  %s, %s:%d\n"),
! 								 edata->funcname, edata->filename,
! 								 edata->lineno);
  			}
! 			else if (edata->filename)
  			{
! 				log_line_prefix(&buf);
! 				appendStringInfo(&buf, _("LOCATION:  %s:%d\n"),
! 								 edata->filename, edata->lineno);
  			}
  		}
! 	}
! 
! 	/*
! 	 * If the user wants the query that generated this error logged, do it.
! 	 */
! 	if (edata->elevel >= log_min_error_statement && debug_query_string != NULL)
! 	{
! 		log_line_prefix(&buf);
! 		appendStringInfoString(&buf, _("STATEMENT:  "));
! 		append_with_tabs(&buf, debug_query_string);
! 		appendStringInfoChar(&buf, '\n');
! 	}
  
  #ifdef HAVE_SYSLOG
  	/* Write to syslog, if enabled */
--- 1562,1646 ----
  
  	initStringInfo(&buf);
  
! 	if ((Log_output_sql == true) &&
! 		(Log_output_table_name != NULL) &&
! 			(pg_strcasecmp(Log_output_table_name, "") != 0))
! 	{
! 		get_SQL_type_server_log(&buf, edata);					  	
! 	}
  	else
  	{
! 		log_line_prefix(Log_line_prefix, &buf);
! 		appendStringInfo(&buf, "%s:  ", error_severity(edata->elevel));
!   
  		if (Log_error_verbosity >= PGERROR_VERBOSE)
+ 			appendStringInfo(&buf, "%s: ", unpack_sql_state(edata->sqlerrcode));
+   
+		/* Get the error message and cursor position if any. */
+ 		get_error_message(&buf, edata);
+   
+ 		appendStringInfoChar(&buf, '\n');
+ 
+ 		if (Log_error_verbosity >= PGERROR_DEFAULT)
  		{
! 			if (edata->detail)
! 			{
! 			log_line_prefix(Log_line_prefix, &buf);
! 				appendStringInfoString(&buf, _("DETAIL:  "));
! 				append_with_tabs(&buf, edata->detail);
! 				appendStringInfoChar(&buf, '\n');
! 			}
! 			if (edata->hint)
! 			{
! 				log_line_prefix(Log_line_prefix, &buf);
! 				appendStringInfoString(&buf, _("HINT:  "));
! 				append_with_tabs(&buf, edata->hint);
! 				appendStringInfoChar(&buf, '\n');
! 			}
! 			if (edata->internalquery)
  			{
! 				log_line_prefix(Log_line_prefix, &buf);
! 				appendStringInfoString(&buf, _("QUERY:  "));
! 				append_with_tabs(&buf, edata->internalquery);
! 				appendStringInfoChar(&buf, '\n');
  			}
! 			if (edata->context)
  			{
! 				log_line_prefix(Log_line_prefix, &buf);
! 				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(Log_line_prefix, &buf);
! 					appendStringInfo(&buf, _("LOCATION:  %s, %s:%d\n"),
! 									 edata->funcname, edata->filename,
! 									 edata->lineno);
! 				}
! 				else if (edata->filename)
! 				{
! 					log_line_prefix(Log_line_prefix, &buf);
! 					appendStringInfo(&buf, _("LOCATION:  %s:%d\n"),
! 									 edata->filename, edata->lineno);
! 				}
  			}
  		}
! 	
! 		/*
! 		 * If the user wants the query that generated this error logged, do it.
! 		 */
! 		if (edata->elevel >= log_min_error_statement && debug_query_string != NULL)
! 		{
! 			log_line_prefix(Log_line_prefix, &buf);
! 			appendStringInfoString(&buf, _("STATEMENT:  "));
! 			append_with_tabs(&buf, debug_query_string);
! 			appendStringInfoChar(&buf, '\n');
! 		}
! }
  
  #ifdef HAVE_SYSLOG
  	/* Write to syslog, if enabled */
***************
*** 2079,2081 ****
--- 2084,2323 ----
  
  	return false;
  }
+ 
+ /*
+  * This function is to construct the log string into a SQL format.
+  * For text based log, the output string is based on the user's input whereas
+  * for SQL we need a fixed format. So a seperate method to simplify.
+  * Benefit to the user is that he can continue to specify arbitrary
+  * log_line_prefix.
+  */
+ static void
+ get_SQL_type_server_log(StringInfo buf, ErrorData *edata)
+ {
+	int format_len = 0;
+ 	int i;
+	int proc_id = 0;
+ 	char **loglineprefixvalues = NULL;
+ 
+ 	StringInfoData msgbuf;
+ 
+ 	initStringInfo(&msgbuf);
+ 
+ 	loglineprefixvalues = (char **) palloc(10 * sizeof(char *));
+ 
+ 	for (i=0; i < 10; i++)
+ 		 strcpy(loglineprefixvalues[i], "NULL");
+ 
+ 	if (Log_line_prefix != NULL)
+ 		format_len = strlen(Log_line_prefix);
+ 
+ 	for (i = 0; i < format_len; i++)
+ 	{
+ 		if (Log_line_prefix[i] != '%')
+ 		{
+ 			/* literal char, ignore for SQL style output */
+ 			continue;
+ 		}
+ 		/* go to char after '%' */
+ 		i++;
+ 		if (i >= format_len)
+ 			break;				/* format error - ignore it */
+ 
+ 		/* process the option */
+ 
+ 		switch (Log_line_prefix[i])
+ 		{
+ 			case 'm':
+ 				{
+ 					initStringInfo(&msgbuf);
+ 					log_line_prefix("%m", &msgbuf);
+ 					loglineprefixvalues[0] = pstrdup(msgbuf.data);
+ 				}
+ 				break;
+ 			case 't':
+ 				{
+ 					initStringInfo(&msgbuf);			
+ 					log_line_prefix("%t", &msgbuf);
+ 					loglineprefixvalues[1] = pstrdup(msgbuf.data);
+ 				}
+ 				break;
+ 			case 'u':
+ 				{
+ 			  	initStringInfo(&msgbuf);
+ 					log_line_prefix("%u", &msgbuf);
+ 					loglineprefixvalues[2] = pstrdup(msgbuf.data);
+ 				}
+ 				break;
+ 			case 'd':
+ 				{
+ 					initStringInfo(&msgbuf);			
+ 					log_line_prefix("%d", &msgbuf);
+ 					loglineprefixvalues[3] = pstrdup(msgbuf.data);
+ 				}
+ 				break;
+ 			case 'c':
+ 				{
+ 					initStringInfo(&msgbuf);			
+ 					log_line_prefix("%c", &msgbuf);
+  					loglineprefixvalues[4] = pstrdup(msgbuf.data);
+ 				}
+ 				break;
+ 			case 'r':
+ 				{
+ 					initStringInfo(&msgbuf);			
+ 					log_line_prefix("%r", &msgbuf);
+ 					loglineprefixvalues[5] = pstrdup(msgbuf.data);
+ 				}
+ 				break;
+ 			case 'h':
+ 				{
+ 					initStringInfo(&msgbuf);			
+ 					log_line_prefix("%h", &msgbuf);
+ 					loglineprefixvalues[6] = pstrdup(msgbuf.data);
+ 				}
+ 				break;
+ 
+ 			case 'p':
+ 				proc_id = MyProcPid;
+ 				break;
+ 			case 'i':
+ 				{
+ 					initStringInfo(&msgbuf);			
+ 					log_line_prefix("%i", &msgbuf);
+ 					loglineprefixvalues[7] = pstrdup(msgbuf.data);
+ 				}
+ 				break;
+ 			case 's':
+ 				{
+ 					initStringInfo(&msgbuf);			
+ 					log_line_prefix("%s", &msgbuf);
+ 					loglineprefixvalues[8] = pstrdup(msgbuf.data);
+ 				}
+ 				break;
+ 			case 'x':
+ 				{
+ 					initStringInfo(&msgbuf);			
+ 					log_line_prefix("%x", &msgbuf);				
+ 					loglineprefixvalues[9] = pstrdup(msgbuf.data);
+ 				}
+ 				break;
+ 			default:
+ 				/* Nothing else for SQL type. ignore it */
+ 				break;
+ 		}
+ 	}
+ 	
+ 	/*
+ 	 * We need timestamp as the unique column. So we get it even if the user
+ 	 *  hasn't given it as an option.
+ 	 */
+ 	if ( (loglineprefixvalues[0] == NULL) || (strcasecmp(loglineprefixvalues[0],"NULL") == 0) )
+ 	{
+ 		initStringInfo(&msgbuf);
+ 		get_timestamp(&msgbuf);
+ 		loglineprefixvalues[0] = pstrdup(msgbuf.data);
+	}
+ 
+ 	/* Construct the SQL type log string. */
+ 	initStringInfo(&msgbuf);
+ 	appendStringInfo(&msgbuf, "INSERT INTO %s VALUES (" , Log_output_table_name);
+ 
+	appendStringInfo(&msgbuf, "%s,%s,%s,%s,%s,%s,%s,%d,%s,%s, %s,",
+ 						loglineprefixvalues[0],
+ 						loglineprefixvalues[1],
+ 						loglineprefixvalues[2],
+ 						loglineprefixvalues[3],
+ 						loglineprefixvalues[4],
+ 						loglineprefixvalues[5],
+ 						loglineprefixvalues[6],
+ 						proc_id,
+ 						loglineprefixvalues[7],
+ 						loglineprefixvalues[8],
+ 						loglineprefixvalues[9]
+ 									);
+ 	appendStringInfo(&msgbuf, "%s,", error_severity(edata->elevel));
+ 	appendStringInfo(&msgbuf, "%s,", unpack_sql_state(edata->sqlerrcode));
+	get_error_message(&msgbuf, edata);
+ 
+ 	/*
+ 	 * If the user wants the query that generated this error logged, do it.
+ 	 */
+ 	if (edata->elevel >= log_min_error_statement && debug_query_string != NULL)
+ 	{
+ 		appendStringInfoString(&msgbuf, _(",STATEMENT:  "));
+ 		append_with_tabs(&msgbuf, debug_query_string);
+ 	}
+ 	appendStringInfoString(&msgbuf, _(")"));
+ 
+ 	appendStringInfo(buf, pstrdup(msgbuf.data));
+ 
+ 	pfree(loglineprefixvalues);
+ 	pfree (msgbuf.data); 
+ }
+ 
+ /*
+  * Appends the buffer with the error message and the cursor position.
+  */
+ static void
+ get_error_message(StringInfo buf, ErrorData *edata)
+ {
+ 	StringInfoData msgbuf;
+ 
+ 	initStringInfo(&msgbuf);
+ 
+ 	if (edata->message)
+ 		append_with_tabs(&msgbuf, edata->message);
+ 	else
+ 		append_with_tabs(&msgbuf, _("missing error text"));
+ 
+ 	if (edata->cursorpos > 0)
+ 		appendStringInfo(&msgbuf, _(" at character %d"),
+ 						 edata->cursorpos);
+ 	else if (edata->internalpos > 0)
+ 		appendStringInfo(&msgbuf, _(" at character %d"),
+ 						 edata->internalpos);
+ 	appendStringInfo(buf, "%s", pstrdup(msgbuf.data));
+ }
+ 
+ /*
+  * Calculates and returns the timestamp
+  */
+ static void
+ get_timestamp(StringInfo buf)
+ {
+ 	/*
+ 	* Note: for %m, %t, and %s we deliberately use the C
+ 	* library's strftime/localtime, and not the equivalent
+ 	* functions from src/timezone.  This ensures that all
+ 	* backends will report log entries in the same timezone,
+ 	* namely whatever C-library setting they inherit from the
+ 	* postmaster.	If we used src/timezone then local
+ 	* settings of the TimeZone GUC variable would confuse the
+ 	* log.
+ 	*/
+ 	time_t		stamp_time;
+ 	char		strfbuf[128],
+ 			msbuf[8];
+ 	struct timeval tv;
+ 
+ 	gettimeofday(&tv, NULL);
+ 	stamp_time = tv.tv_sec;
+ 
+ 	strftime(strfbuf, sizeof(strfbuf),
+ 	/* leave room for milliseconds... */
+ 	/* Win32 timezone names are too long so don't print them. */
+ #ifndef WIN32
+ 	"%Y-%m-%d %H:%M:%S     %Z",
+ #else
+ 	"%Y-%m-%d %H:%M:%S     ",
+ #endif
+ 	localtime(&stamp_time));
+ 
+ 	/* 'paste' milliseconds into place... */
+ 	sprintf(msbuf, ".%03d", (int) (tv.tv_usec / 1000));
+ 	strncpy(strfbuf + 19, msbuf, 4);
+ 
+ 	appendStringInfoString(buf, strfbuf);
+ }
---------------------------(end of broadcast)---------------------------
TIP 9: In versions below 8.0, the planner will ignore your desire to
       choose an index scan if your joining column's datatypes do not
       match

Reply via email to