*** a/src/backend/postmaster/postmaster.c
--- b/src/backend/postmaster/postmaster.c
***************
*** 1410,1416 **** ServerLoop(void)
  		}
  
  		/* If we have lost the log collector, try to start a new one */
! 		if (SysLoggerPID == 0 && Logging_collector)
  			SysLoggerPID = SysLogger_Start();
  
  		/*
--- 1410,1416 ----
  		}
  
  		/* If we have lost the log collector, try to start a new one */
! 		if (SysLoggerPID == 0)
  			SysLoggerPID = SysLogger_Start();
  
  		/*
*** a/src/backend/postmaster/syslogger.c
--- b/src/backend/postmaster/syslogger.c
***************
*** 64,73 ****
  
  
  /*
!  * GUC parameters.	Logging_collector cannot be changed after postmaster
!  * start, but the rest can change at SIGHUP.
   */
- bool		Logging_collector = false;
  int			Log_RotationAge = HOURS_PER_DAY * MINS_PER_HOUR;
  int			Log_RotationSize = 10 * 1024;
  char	   *Log_directory = NULL;
--- 64,71 ----
  
  
  /*
!  * GUC parameters. All parameters can change at SIGHUP.
   */
  int			Log_RotationAge = HOURS_PER_DAY * MINS_PER_HOUR;
  int			Log_RotationSize = 10 * 1024;
  char	   *Log_directory = NULL;
***************
*** 135,140 **** static void syslogger_parseArgs(int argc, char *argv[]);
--- 133,139 ----
  static void process_pipe_input(char *logbuffer, int *bytes_in_logbuffer);
  static void flush_pipe_input(char *logbuffer, int *bytes_in_logbuffer);
  static void open_csvlogfile(void);
+ static void write_syslogger_message(char *buffer, int len, bool partial);
  
  #ifdef WIN32
  static unsigned int __stdcall pipeThread(void *arg);
***************
*** 454,462 **** SysLogger_Start(void)
  	pid_t		sysloggerPid;
  	char	   *filename;
  
- 	if (!Logging_collector)
- 		return 0;
- 
  	/*
  	 * If first time through, create the pipe which will receive stderr
  	 * output.
--- 453,458 ----
***************
*** 777,784 **** process_pipe_input(char *logbuffer, int *bytes_in_logbuffer)
  					 * chances and write out a partial message and hope that
  					 * it's not followed by something from another pid.
  					 */
! 					write_syslogger_file(cursor + PIPE_HEADER_SIZE, p.len,
! 										 dest);
  				}
  			}
  			else
--- 773,780 ----
  					 * chances and write out a partial message and hope that
  					 * it's not followed by something from another pid.
  					 */
! 					write_syslogger_message(cursor + PIPE_HEADER_SIZE, p.len,
! 											true);
  				}
  			}
  			else
***************
*** 805,819 **** process_pipe_input(char *logbuffer, int *bytes_in_logbuffer)
  					appendBinaryStringInfo(str,
  										   cursor + PIPE_HEADER_SIZE,
  										   p.len);
! 					write_syslogger_file(str->data, str->len, dest);
  					saved_chunks[existing_slot].pid = 0;
  					pfree(str->data);
  				}
  				else
  				{
  					/* The whole message was one chunk, evidently. */
! 					write_syslogger_file(cursor + PIPE_HEADER_SIZE, p.len,
! 										 dest);
  				}
  			}
  
--- 801,815 ----
  					appendBinaryStringInfo(str,
  										   cursor + PIPE_HEADER_SIZE,
  										   p.len);
! 					write_syslogger_message(str->data, str->len, false);
  					saved_chunks[existing_slot].pid = 0;
  					pfree(str->data);
  				}
  				else
  				{
  					/* The whole message was one chunk, evidently. */
! 					write_syslogger_message(cursor + PIPE_HEADER_SIZE, p.len,
! 											false);
  				}
  			}
  
***************
*** 824,829 **** process_pipe_input(char *logbuffer, int *bytes_in_logbuffer)
--- 820,826 ----
  		else
  		{
  			/* Process non-protocol data */
+ 			StringInfo str;
  
  			/*
  			 * Look for the start of a protocol header.  If found, dump data
***************
*** 839,846 **** process_pipe_input(char *logbuffer, int *bytes_in_logbuffer)
  				if (cursor[chunklen] == '\0')
  					break;
  			}
! 			/* fall back on the stderr log as the destination */
! 			write_syslogger_file(cursor, chunklen, LOG_DESTINATION_STDERR);
  			cursor += chunklen;
  			count -= chunklen;
  		}
--- 836,853 ----
  				if (cursor[chunklen] == '\0')
  					break;
  			}
! 
! 			/*
! 			 * send non-protocol data through the error log system, so we can add log_line_prefix and such, and
! 			 * get it send to all the required destinatinos.
! 			 */
! 			str = makeStringInfo();
! 			appendBinaryStringInfo(str, cursor, chunklen);
! 			appendStringInfoChar(str, '\0'); /* ensure zero-termination to be sure */
! 			elog(WARNING, "Non protocol data: %s", str->data);
! 			pfree(str->data);
! 			pfree(str);
! 
  			cursor += chunklen;
  			count -= chunklen;
  		}
***************
*** 870,876 **** flush_pipe_input(char *logbuffer, int *bytes_in_logbuffer)
  		if (saved_chunks[i].pid != 0)
  		{
  			str = &(saved_chunks[i].data);
! 			write_syslogger_file(str->data, str->len, LOG_DESTINATION_STDERR);
  			saved_chunks[i].pid = 0;
  			pfree(str->data);
  		}
--- 877,883 ----
  		if (saved_chunks[i].pid != 0)
  		{
  			str = &(saved_chunks[i].data);
! 			write_syslogger_message(str->data, str->len, true);
  			saved_chunks[i].pid = 0;
  			pfree(str->data);
  		}
***************
*** 881,888 **** flush_pipe_input(char *logbuffer, int *bytes_in_logbuffer)
  	 * remove any protocol headers that may exist in it.
  	 */
  	if (*bytes_in_logbuffer > 0)
! 		write_syslogger_file(logbuffer, *bytes_in_logbuffer,
! 							 LOG_DESTINATION_STDERR);
  	*bytes_in_logbuffer = 0;
  }
  
--- 888,901 ----
  	 * remove any protocol headers that may exist in it.
  	 */
  	if (*bytes_in_logbuffer > 0)
! 	{
! 		str = makeStringInfo();
! 		appendBinaryStringInfo(str, logbuffer, *bytes_in_logbuffer);
! 		appendStringInfoChar(str, '\0');
! 		elog(WARNING, "Unprocessed log data: %s", str->data);
! 		pfree(str->data);
! 		pfree(str);
! 	}
  	*bytes_in_logbuffer = 0;
  }
  
***************
*** 894,927 **** flush_pipe_input(char *logbuffer, int *bytes_in_logbuffer)
  
  /*
   * Write text to the currently open logfile
-  *
-  * This is exported so that elog.c can call it when am_syslogger is true.
-  * This allows the syslogger process to record elog messages of its own,
-  * even though its stderr does not point at the syslog pipe.
   */
  void
! write_syslogger_file(const char *buffer, int count, int destination)
  {
- 	int			rc;
- 	FILE	   *logfile;
- 
- 	if (destination == LOG_DESTINATION_CSVLOG && csvlogFile == NULL)
- 		open_csvlogfile();
- 
  #ifdef WIN32
  	EnterCriticalSection(&sysfileSection);
  #endif
  
! 	logfile = destination == LOG_DESTINATION_CSVLOG ? csvlogFile : syslogFile;
! 	rc = fwrite(buffer, 1, count, logfile);
  
  #ifdef WIN32
  	LeaveCriticalSection(&sysfileSection);
  #endif
  
  	/* can't use ereport here because of possible recursion */
  	if (rc != count)
  		write_stderr("could not write to log file: %s\n", strerror(errno));
  }
  
  #ifdef WIN32
--- 907,988 ----
  
  /*
   * Write text to the currently open logfile
   */
  void
! write_syslogger_file(const char *buffer)
  {
  #ifdef WIN32
  	EnterCriticalSection(&sysfileSection);
  #endif
  
! 	fwrite(buffer, 1, strlen(buffer), syslogFile);
  
  #ifdef WIN32
  	LeaveCriticalSection(&sysfileSection);
  #endif
  
  	/* can't use ereport here because of possible recursion */
+ 	/* FIXME: need to look at this! */
+ 	/*
  	if (rc != count)
  		write_stderr("could not write to log file: %s\n", strerror(errno));
+ 		*/
+ }
+ 
+ /*
+  * Write text to the currently open CSV log file
+  */
+ void
+ write_csvlogger_file(const char *buffer)
+ {
+ 	if (csvlogFile == NULL)
+ 		open_csvlogfile();
+ 
+ #ifdef WIN32
+ 	EnterCriticalSection(&sysfileSection);
+ #endif
+ 
+ 	fwrite(buffer, 1, strlen(buffer), csvlogFile);
+ 
+ #ifdef WIN32
+ 	LeaveCriticalSection(&sysfileSection);
+ #endif
+ 
+ 	/* FIXME: need an error check? */
+ }
+ 
+ 
+ void
+ write_syslogger_message(char *buffer, int count, bool partial)
+ {
+ 	/* Data always starts with a fixed chunk */
+ 	syslogger_header *header = (syslogger_header *)buffer;
+ 
+ 	/* string data */
+ 	syslogger_strings strings;
+ 
+ 	/* pointer to walk through the strings */
+ 	char *cp = buffer + sizeof(syslogger_header);
+ 
+ 	/* FIXME: overflow handling! */
+ #define NEXT_STRING(var) strings.var = cp; cp += strlen(cp)+1; if (strings.var[0] == '\0') strings.var=NULL;
+ 	NEXT_STRING(user_name);
+ 	NEXT_STRING(database_name);
+ 	NEXT_STRING(message);
+ 	NEXT_STRING(detail);
+ 	NEXT_STRING(hint);
+ 	NEXT_STRING(internalquery);
+ 	NEXT_STRING(context);
+ 	NEXT_STRING(debug_query_string);
+ 	NEXT_STRING(funcname);
+ 	NEXT_STRING(filename);
+ 
+ 	/*
+ 	 * Any local destinations (syslog etc) have already received the log message from
+ 	 * the backend process itself. Route the message to any remote destination, which are those
+ 	 * that required the syslogger process in the first place.
+ 	 */
+ 	route_log_message(header, &strings, Log_destination & LOG_DESTINATION_REMOTE_MASK);
  }
  
  #ifdef WIN32
*** a/src/backend/utils/adt/misc.c
--- b/src/backend/utils/adt/misc.c
***************
*** 146,158 **** pg_rotate_logfile(PG_FUNCTION_ARGS)
  				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  				 (errmsg("must be superuser to rotate log files"))));
  
- 	if (!Logging_collector)
- 	{
- 		ereport(WARNING,
- 		(errmsg("rotation not possible because log collection not active")));
- 		PG_RETURN_BOOL(false);
- 	}
- 
  	SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
  	PG_RETURN_BOOL(true);
  }
--- 146,151 ----
*** a/src/backend/utils/error/elog.c
--- b/src/backend/utils/error/elog.c
***************
*** 144,150 **** static char formatted_log_time[FORMATTED_TS_LEN];
  	} while (0)
  
  
! static void log_line_prefix(StringInfo buf, ErrorData *edata);
  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);
--- 144,150 ----
  	} while (0)
  
  
! static void log_line_prefix(StringInfo buf, const syslogger_header *header, const syslogger_strings *str);
  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);
***************
*** 153,161 **** 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 write_pipe_chunks(char *data, int len, int dest);
! static void write_csvlog(ErrorData *edata);
! static void setup_formatted_log_time(void);
! static void setup_formatted_start_time(void);
  
  
  /*
--- 153,161 ----
  static void append_with_tabs(StringInfo buf, const char *str);
  static bool is_log_level_output(int elevel, int log_min_level);
  static void write_pipe_chunks(char *data, int len, int dest);
! static void write_csvlog(const syslogger_header *header, const syslogger_strings *str);
! static void setup_formatted_log_time(const pg_time_t *log_time);
! static void setup_formatted_start_time(const pg_time_t *start_time);
  
  
  /*
***************
*** 1623,1637 **** write_eventlog(int level, const char *line)
   * setup formatted_log_time, for consistent times between CSV and regular logs
   */
  static void
! setup_formatted_log_time(void)
  {
  	struct timeval tv;
  	pg_time_t	stamp_time;
  	pg_tz	   *tz;
  	char		msbuf[8];
  
! 	gettimeofday(&tv, NULL);
! 	stamp_time = (pg_time_t) tv.tv_sec;
  
  	/*
  	 * Normally we print log timestamps in log_timezone, but during startup we
--- 1623,1644 ----
   * setup formatted_log_time, for consistent times between CSV and regular logs
   */
  static void
! setup_formatted_log_time(const pg_time_t *log_time)
  {
  	struct timeval tv;
  	pg_time_t	stamp_time;
  	pg_tz	   *tz;
  	char		msbuf[8];
  
! 	if (log_time == NULL || 0==0)
! 	{
! 		gettimeofday(&tv, NULL);
! 		stamp_time = (pg_time_t) tv.tv_sec;
! 	}
! 	else
! 	{
! 		stamp_time = *log_time;
! 	}
  
  	/*
  	 * Normally we print log timestamps in log_timezone, but during startup we
***************
*** 1655,1665 **** setup_formatted_log_time(void)
   * setup formatted_start_time
   */
  static void
! setup_formatted_start_time(void)
  {
! 	pg_time_t	stamp_time = (pg_time_t) MyStartTime;
  	pg_tz	   *tz;
  
  	/*
  	 * Normally we print log timestamps in log_timezone, but during startup we
  	 * could get here before that's set. If so, fall back to gmt_timezone
--- 1662,1676 ----
   * setup formatted_start_time
   */
  static void
! setup_formatted_start_time(const pg_time_t *start_time)
  {
! 	pg_time_t	stamp_time;
  	pg_tz	   *tz;
  
+     if (start_time == NULL)
+         stamp_time = (pg_time_t) MyStartTime;
+     else
+         stamp_time = *start_time;
  	/*
  	 * Normally we print log timestamps in log_timezone, but during startup we
  	 * could get here before that's set. If so, fall back to gmt_timezone
***************
*** 1677,1683 **** setup_formatted_start_time(void)
   * Format tag info for log lines; append to the provided buffer.
   */
  static void
! log_line_prefix(StringInfo buf, ErrorData *edata)
  {
  	/* static counter for line numbers */
  	static long log_line_number = 0;
--- 1688,1694 ----
   * Format tag info for log lines; append to the provided buffer.
   */
  static void
! log_line_prefix(StringInfo buf, const syslogger_header *header, const syslogger_strings *str)
  {
  	/* static counter for line numbers */
  	static long log_line_number = 0;
***************
*** 1724,1764 **** log_line_prefix(StringInfo buf, ErrorData *edata)
  		switch (Log_line_prefix[i])
  		{
  			case 'u':
! 				if (MyProcPort)
! 				{
! 					const char *username = MyProcPort->user_name;
! 
! 					if (username == NULL || *username == '\0')
! 						username = _("[unknown]");
! 					appendStringInfo(buf, "%s", username);
! 				}
  				break;
  			case 'd':
! 				if (MyProcPort)
! 				{
! 					const char *dbname = MyProcPort->database_name;
! 
! 					if (dbname == NULL || *dbname == '\0')
! 						dbname = _("[unknown]");
! 					appendStringInfo(buf, "%s", dbname);
! 				}
  				break;
  			case 'c':
! 				appendStringInfo(buf, "%lx.%x", (long) (MyStartTime), MyProcPid);
  				break;
  			case 'p':
! 				appendStringInfo(buf, "%d", MyProcPid);
  				break;
  			case 'l':
! 				appendStringInfo(buf, "%ld", log_line_number);
  				break;
  			case 'm':
! 				setup_formatted_log_time();
  				appendStringInfoString(buf, formatted_log_time);
  				break;
  			case 't':
  				{
! 					pg_time_t	stamp_time = (pg_time_t) time(NULL);
  					pg_tz	   *tz;
  					char		strfbuf[128];
  
--- 1735,1761 ----
  		switch (Log_line_prefix[i])
  		{
  			case 'u':
! 				appendStringInfo(buf, "%s", str->user_name?str->user_name:"[unknown]");
  				break;
  			case 'd':
! 				appendStringInfo(buf, "%s", str->database_name?str->database_name:"[unknown]");
  				break;
  			case 'c':
! 				appendStringInfo(buf, "%lx.%x", (long)header->starttime, header->pid);
  				break;
  			case 'p':
! 				appendStringInfo(buf, "%d", header->pid);
  				break;
  			case 'l':
! 				appendStringInfo(buf, "%ld", header->linenum);
  				break;
  			case 'm':
! 				setup_formatted_log_time(&header->timestamp);
  				appendStringInfoString(buf, formatted_log_time);
  				break;
  			case 't':
  				{
! 					pg_time_t	stamp_time = header->timestamp;
  					pg_tz	   *tz;
  					char		strfbuf[128];
  
***************
*** 1772,1781 **** log_line_prefix(StringInfo buf, ErrorData *edata)
  				break;
  			case 's':
  				if (formatted_start_time[0] == '\0')
! 					setup_formatted_start_time();
  				appendStringInfoString(buf, formatted_start_time);
  				break;
  			case 'i':
  				if (MyProcPort)
  				{
  					const char *psdisp;
--- 1769,1779 ----
  				break;
  			case 's':
  				if (formatted_start_time[0] == '\0')
! 					setup_formatted_start_time(&header->starttime);
  				appendStringInfoString(buf, formatted_start_time);
  				break;
  			case 'i':
+ 				/* FIXME */
  				if (MyProcPort)
  				{
  					const char *psdisp;
***************
*** 1786,1821 **** log_line_prefix(StringInfo buf, ErrorData *edata)
  				}
  				break;
  			case 'r':
! 				if (MyProcPort && MyProcPort->remote_host)
  				{
! 					appendStringInfo(buf, "%s", MyProcPort->remote_host);
! 					if (MyProcPort->remote_port &&
! 						MyProcPort->remote_port[0] != '\0')
  						appendStringInfo(buf, "(%s)",
! 										 MyProcPort->remote_port);
  				}
  				break;
  			case 'h':
! 				if (MyProcPort && MyProcPort->remote_host)
! 					appendStringInfo(buf, "%s", MyProcPort->remote_host);
  				break;
  			case 'q':
  				/* in postmaster and friends, stop if %q is seen */
  				/* in a backend, just ignore */
! 				if (MyProcPort == NULL)
  					i = format_len;
  				break;
  			case 'v':
  				/* keep VXID format in sync with lockfuncs.c */
  				if (MyProc != NULL && MyProc->backendId != InvalidBackendId)
  					appendStringInfo(buf, "%d/%u",
  									 MyProc->backendId, MyProc->lxid);
  				break;
  			case 'x':
! 				appendStringInfo(buf, "%u", GetTopTransactionIdIfAny());
  				break;
  			case 'e':
! 				appendStringInfoString(buf, unpack_sql_state(edata->sqlerrcode));
  				break;
  			case '%':
  				appendStringInfoChar(buf, '%');
--- 1784,1820 ----
  				}
  				break;
  			case 'r':
! 				if (str->remote_host != NULL)
  				{
! 					appendStringInfo(buf, "%s", str->remote_host);
! 					if (str->remote_port &&
! 						str->remote_port[0] != '\0')
  						appendStringInfo(buf, "(%s)",
! 										 str->remote_port);
  				}
  				break;
  			case 'h':
! 				if (str->remote_host)
! 					appendStringInfo(buf, "%s", str->remote_host);
  				break;
  			case 'q':
  				/* in postmaster and friends, stop if %q is seen */
  				/* in a backend, just ignore */
! 				if (str->remote_host == NULL)
  					i = format_len;
  				break;
  			case 'v':
+ 				/* FIXME */
  				/* keep VXID format in sync with lockfuncs.c */
  				if (MyProc != NULL && MyProc->backendId != InvalidBackendId)
  					appendStringInfo(buf, "%d/%u",
  									 MyProc->backendId, MyProc->lxid);
  				break;
  			case 'x':
! 				appendStringInfo(buf, "%u", header->xid);
  				break;
  			case 'e':
! 				appendStringInfoString(buf, unpack_sql_state(header->sqlerrcode));
  				break;
  			case '%':
  				appendStringInfoChar(buf, '%');
***************
*** 1857,1863 **** appendCSVLiteral(StringInfo buf, const char *data)
   * format which is described in doc/src/sgml/config.sgml.
   */
  static void
! write_csvlog(ErrorData *edata)
  {
  	StringInfoData buf;
  	bool		print_stmt = false;
--- 1856,1862 ----
   * format which is described in doc/src/sgml/config.sgml.
   */
  static void
! write_csvlog(const syslogger_header *header, const syslogger_strings *str)
  {
  	StringInfoData buf;
  	bool		print_stmt = false;
***************
*** 1891,1917 **** write_csvlog(ErrorData *edata)
  	 * 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, '"');
--- 1890,1914 ----
  	 * to put same timestamp in both syslog and csvlog messages.
  	 */
  	if (formatted_log_time[0] == '\0')
! 		setup_formatted_log_time(&header->timestamp);
  
  	appendStringInfoString(&buf, formatted_log_time);
  	appendStringInfoChar(&buf, ',');
  
  	/* username */
! 	appendCSVLiteral(&buf, str->user_name);
  	appendStringInfoChar(&buf, ',');
  
  	/* database name */
! 	appendCSVLiteral(&buf, str->database_name);
  	appendStringInfoChar(&buf, ',');
  
  	/* Process id  */
! 	appendStringInfo(&buf, "%d", header->pid);
  	appendStringInfoChar(&buf, ',');
  
  	/* Remote host and port */
+ 	/* FIXME 
  	if (MyProcPort && MyProcPort->remote_host)
  	{
  		appendStringInfoChar(&buf, '"');
***************
*** 1921,1929 **** write_csvlog(ErrorData *edata)
  		appendStringInfoChar(&buf, '"');
  	}
  	appendStringInfoChar(&buf, ',');
  
  	/* session id */
! 	appendStringInfo(&buf, "%lx.%x", (long) MyStartTime, MyProcPid);
  	appendStringInfoChar(&buf, ',');
  
  	/* Line number */
--- 1918,1927 ----
  		appendStringInfoChar(&buf, '"');
  	}
  	appendStringInfoChar(&buf, ',');
+ 	*/
  
  	/* session id */
! 	appendStringInfo(&buf, "%lx.%x", (long) header->starttime, header->pid);
  	appendStringInfoChar(&buf, ',');
  
  	/* Line number */
***************
*** 1931,1936 **** write_csvlog(ErrorData *edata)
--- 1929,1935 ----
  	appendStringInfoChar(&buf, ',');
  
  	/* PS display */
+ 	/* FIXME
  	if (MyProcPort)
  	{
  		StringInfoData msgbuf;
***************
*** 1946,2014 **** write_csvlog(ErrorData *edata)
  		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 */
! 	appendStringInfo(&buf, "%s", error_severity(edata->elevel));
  	appendStringInfoChar(&buf, ',');
  
  	/* SQL state code */
! 	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);
  	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 */
--- 1945,2014 ----
  		pfree(msgbuf.data);
  	}
  	appendStringInfoChar(&buf, ',');
+ 	*/
  
  	/* session start timestamp */
  	if (formatted_start_time[0] == '\0')
! 		setup_formatted_start_time(&header->starttime);
  	appendStringInfoString(&buf, formatted_start_time);
  	appendStringInfoChar(&buf, ',');
  
  	/* Virtual transaction id */
  	/* keep VXID format in sync with lockfuncs.c */
+ 	/* FIXME:
  	if (MyProc != NULL && MyProc->backendId != InvalidBackendId)
  		appendStringInfo(&buf, "%d/%u", MyProc->backendId, MyProc->lxid);
  	appendStringInfoChar(&buf, ',');
+ 	*/
  
  	/* Transaction id */
+ 	/* FIXME
  	appendStringInfo(&buf, "%u", GetTopTransactionIdIfAny());
  	appendStringInfoChar(&buf, ',');
+ 	*/
  
  	/* Error severity */
! 	appendStringInfo(&buf, "%s", error_severity(header->elevel));
  	appendStringInfoChar(&buf, ',');
  
  	/* SQL state code */
! 	appendStringInfo(&buf, "%s", unpack_sql_state(header->sqlerrcode));
  	appendStringInfoChar(&buf, ',');
  
  	/* errmessage */
! 	appendCSVLiteral(&buf, str->message);
  	appendStringInfoCharMacro(&buf, ',');
  
  	/* errdetail or errdetail_log */
! 	appendCSVLiteral(&buf, str->detail);
  	appendStringInfoCharMacro(&buf, ',');
  
  	/* errhint */
! 	appendCSVLiteral(&buf, str->hint);
  	appendStringInfoCharMacro(&buf, ',');
  
  	/* internal query */
! 	appendCSVLiteral(&buf, str->internalquery);
  	appendStringInfoCharMacro(&buf, ',');
  
  	/* if printed internal query, print internal pos too */
! 	if (header->internalpos > 0 && str->internalquery != NULL)
! 		appendStringInfo(&buf, "%d", header->internalpos);
  	appendStringInfoCharMacro(&buf, ',');
  
  	/* errcontext */
! 	appendCSVLiteral(&buf, str->context);
  	appendStringInfoCharMacro(&buf, ',');
  
  	/* user query --- only reported if not disabled by the caller */
! 	if (is_log_level_output(header->elevel, header->log_min_error_statement) &&
! 		str->debug_query_string != NULL)
  		print_stmt = true;
  	if (print_stmt)
! 		appendCSVLiteral(&buf, str->debug_query_string);
  	appendStringInfoCharMacro(&buf, ',');
! 	if (print_stmt && header->cursorpos > 0)
! 		appendStringInfo(&buf, "%d", header->cursorpos);
  	appendStringInfoCharMacro(&buf, ',');
  
  	/* file error location */
***************
*** 2018,2041 **** write_csvlog(ErrorData *edata)
  
  		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, '\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);
  
  	pfree(buf.data);
  }
--- 2018,2037 ----
  
  		initStringInfo(&msgbuf);
  
! 		if (str->funcname && str->filename)
  			appendStringInfo(&msgbuf, "%s, %s:%d",
! 							 str->funcname, str->filename,
! 							 header->error_lineno);
! 		else if (str->filename)
  			appendStringInfo(&msgbuf, "%s:%d",
! 							 str->filename, header->error_lineno);
  		appendCSVLiteral(&buf, msgbuf.data);
  		pfree(msgbuf.data);
  	}
  
  	appendStringInfoChar(&buf, '\n');
  
! 	write_csvlogger_file(buf.data);
  
  	pfree(buf.data);
  }
***************
*** 2062,2150 **** unpack_sql_state(int sql_state)
  
  
  /*
!  * 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)
  		{
! 			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);
  			}
  		}
  	}
--- 2058,2281 ----
  
  
  /*
!  * Write error report to pipe to syslogger
   */
+ #define EMPTY_STRING(x) ((x)?(x):"")
+ #define appendStringInfoVariable(n) do { appendStringInfoString(&buf, EMPTY_STRING(str.n));appendStringInfoChar(&buf,'\0'); } while (0);
  static void
  send_message_to_server_log(ErrorData *edata)
  {
  	StringInfoData buf;
+ 	struct timeval tv;
  
! 	/* static counter for line numbers */
! 	static long log_line_number = 0;
! 
! 	/* has counter been reset in current process? */
! 	static int  log_my_pid = 0;
! 
! 	/* header with fixed-size data to send */
! 	syslogger_header header;
! 	/* variable length strings */
! 	syslogger_strings str;
! 
! 	/*
! 	 * 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
! 	 * MyProcPid changes.
! 	 */
! 	if (log_my_pid != MyProcPid)
! 	{
! 		log_line_number = 0;
! 		log_my_pid = MyProcPid;
! 	}
! 	log_line_number++;
  
! 	/* Get the current time for logging */
! 	gettimeofday(&tv, NULL);
  
! 	/* Set up all header values */
! 	memset(&header, 0, sizeof(header));
! 
! 	header.pid = MyProcPid;
! 	header.timestamp = tv.tv_sec;
! 	header.starttime = MyStartTime;
! 	header.linenum = log_line_number;
! 	if (MyProc)
! 	{
! 		header.backendid = MyProc->backendId;
! 		header.lxid = MyProc->lxid;
! 	}
! 	header.xid = GetTopTransactionIdIfAny();
! 	header.elevel = edata->elevel;
! 	header.sqlerrcode = edata->sqlerrcode;
! 	header.internalpos = edata->internalpos;
! 	header.log_min_error_statement = log_min_error_statement;
! 	header.cursorpos = edata->cursorpos;
! 	header.error_lineno = edata->lineno;
! 
! 	/* Set up strings to be sent */
! 	str.user_name = MyProcPort ? MyProcPort->user_name : "";
! 	str.database_name = MyProcPort ? MyProcPort->database_name : "";
! 	str.message = edata->message;
! 	str.detail = edata->detail_log ? edata->detail_log : edata->detail;
! 	str.hint = edata->hint;
! 	str.internalquery = edata->internalquery;
! 	str.context = edata->context;
! 	str.debug_query_string = edata->hide_stmt ? "" : debug_query_string;
! 	str.funcname = edata->funcname;
! 	str.filename = edata->filename;
!     if (MyProcPort) {
!         str.remote_host = MyProcPort->remote_host;
!         if (MyProcPort->remote_port)
!             str.remote_port = MyProcPort->remote_host;
!         else
!             str.remote_port = NULL;
!     }
!     else {
!         str.remote_host = NULL;
!         str.remote_port = NULL;
!     }
! 
! 	if (am_syslogger)
! 	{
! 		/*
! 		 * Running in syslogger process, write data directly to the destination
! 		 */
! 		route_log_message(&header, &str, Log_destination);
! 	}
! 	else
! 	{
! 		if (!redirection_done)
! 		{
! 			/*
! 			 * If redirection hasn't been done yet, there is no way we can write any data
! 			 * to the syslogger, so just skip sending to remote.
! 			 * If any local destinations are configured, send there.
! 			 * If there are no local destinations configured, send to stderr anyway,
! 			 * so we don't loose the data.
! 			 */
! 			if (Log_destination & LOG_DESTINATION_LOCAL_MASK)
! 			{
! 				route_log_message(&header, &str, Log_destination & LOG_DESTINATION_LOCAL_MASK);
! 			}
! 			else
! 			{
! 				route_log_message(&header, &str, LOG_DESTINATION_STDERR);
! 			}
! 		}
! 		else
! 		{
! 			/* Redirection has been done */
! 			if (Log_destination & LOG_DESTINATION_LOCAL_MASK)
! 			{
! 				/*
! 				 * Log destination contains at least one destination that we write directly to,
! 				 * bypassing the syslogger process.
! 				 */
! 				route_log_message(&header, &str, Log_destination & LOG_DESTINATION_LOCAL_MASK);
! 			}
! 
! 			if (Log_destination & LOG_DESTINATION_REMOTE_MASK)
! 			{
! 				/*
! 				 * If our log destination contains any destination that requires the syslogger,
! 				 * send the data there.
! 				 */
! 
! 				initStringInfo(&buf);
! 				appendBinaryStringInfo(&buf, (const char *)&header, sizeof(header));
! 				appendStringInfoVariable(user_name);
! 				appendStringInfoVariable(database_name);
! 				appendStringInfoVariable(message);
! 				appendStringInfoVariable(detail);
! 				appendStringInfoVariable(hint);
! 				appendStringInfoVariable(internalquery);
! 				appendStringInfoVariable(context);
! 				appendStringInfoVariable(debug_query_string);
! 				appendStringInfoVariable(funcname);
! 				appendStringInfoVariable(filename);
! 				appendStringInfoVariable(remote_host);
! 				appendStringInfoVariable(remote_port);
! 
! 				/* Send the data off to the syslogger */
! 				write_pipe_chunks(buf.data, buf.len, LOG_DESTINATION_STDERR);
! 			}
! 		}
! 	}
! }
! 
! /*
!  * Format a log line
!  */
! void
! format_log_line(StringInfoData *buf, const syslogger_header *header, const syslogger_strings *str)
! {
! 	log_line_prefix(buf, header, str);
! 	appendStringInfo(buf, "%s:  ", error_severity(header->elevel));
  
  	if (Log_error_verbosity >= PGERROR_VERBOSE)
! 		appendStringInfo(buf, "%s: ", unpack_sql_state(header->sqlerrcode));
  
! 	if (str->message)
! 		append_with_tabs(buf, str->message);
  	else
! 		append_with_tabs(buf, _("missing error text"));
  
! 	if (header->cursorpos > 0)
! 		appendStringInfo(buf, _(" at character %d"),
! 						 header->cursorpos);
! 	else if (header->internalpos > 0)
! 		appendStringInfo(buf, _(" at character %d"),
! 						 header->internalpos);
  
! 	appendStringInfoChar(buf, '\n');
  
  	if (Log_error_verbosity >= PGERROR_DEFAULT)
  	{
! 		if (str->detail)
  		{
! 			log_line_prefix(buf, header, str);
! 			appendStringInfoString(buf, _("DETAIL:  "));
! 			append_with_tabs(buf, str->detail);
! 			appendStringInfoChar(buf, '\n');
  		}
! 		if (str->hint)
  		{
! 			log_line_prefix(buf, header, str);
! 			appendStringInfoString(buf, _("HINT:  "));
! 			append_with_tabs(buf, str->hint);
! 			appendStringInfoChar(buf, '\n');
  		}
! 		if (str->internalquery)
  		{
! 			log_line_prefix(buf, header, str);
! 			appendStringInfoString(buf, _("QUERY:  "));
! 			append_with_tabs(buf, str->internalquery);
! 			appendStringInfoChar(buf, '\n');
  		}
! 		if (str->context)
  		{
! 			log_line_prefix(buf, header, str);
! 			appendStringInfoString(buf, _("CONTEXT:  "));
! 			append_with_tabs(buf, str->context);
! 			appendStringInfoChar(buf, '\n');
  		}
  		if (Log_error_verbosity >= PGERROR_VERBOSE)
  		{
  			/* assume no newlines in funcname or filename... */
! 			if (str->funcname && str->filename)
  			{
! 				log_line_prefix(buf, header, str);
! 				appendStringInfo(buf, _("LOCATION:  %s, %s:%d\n"),
! 								 str->funcname, str->filename,
! 								 header->error_lineno);
  			}
! 			else if (str->filename)
  			{
! 				log_line_prefix(buf, header, str);
! 				appendStringInfo(buf, _("LOCATION:  %s:%d\n"),
! 								 str->filename, header->error_lineno);
  			}
  		}
  	}
***************
*** 2152,2174 **** send_message_to_server_log(ErrorData *edata)
  	/*
  	 * 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:
--- 2283,2328 ----
  	/*
  	 * If the user wants the query that generated this error logged, do it.
  	 */
! 	if (is_log_level_output(header->elevel, header->log_min_error_statement) &&
! 		str->debug_query_string != NULL)
  	{
! 		log_line_prefix(buf, header, str);
! 		appendStringInfoString(buf, _("STATEMENT:  "));
! 		append_with_tabs(buf, str->debug_query_string);
! 		appendStringInfoChar(buf, '\n');
  	}
+ }
+ 
+ /*
+  * Route the log message to the proper destination. This can either be a local destination
+  * (syslog, stderr etc), or a remote one (the ones that require the syslogger process, such
+  * as logfile).
+  * For remote destinations, this function is only called inside the syslogger, so we write
+  * directly to the destination in this case as well.
+  */
+ void
+ route_log_message(const syslogger_header *header, const syslogger_strings *strings, int destinations)
+ {
+ 	StringInfoData buf;
+ 
+ 	/*
+ 	 * If our destination needs a general format string, set it up here
+ 	 */
+ 	if (destinations & (LOG_DESTINATION_SYSLOG | LOG_DESTINATION_EVENTLOG | LOG_DESTINATION_FILE | LOG_DESTINATION_STDERR))
+ 	{
+ 		initStringInfo(&buf);
+ 		format_log_line(&buf, header, strings);
+ 	}
+ 	else
+ 		buf.data = NULL;
  
  #ifdef HAVE_SYSLOG
  	/* Write to syslog, if enabled */
! 	if (destinations & LOG_DESTINATION_SYSLOG)
  	{
  		int			syslog_level;
  
! 		switch (header->elevel)
  		{
  			case DEBUG5:
  			case DEBUG4:
***************
*** 2204,2275 **** send_message_to_server_log(ErrorData *edata)
  
  #ifdef WIN32
  	/* Write to eventlog, if enabled */
! 	if (Log_destination & LOG_DESTINATION_EVENTLOG)
  	{
! 		write_eventlog(edata->elevel, buf.data);
  	}
  #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);
! #endif
! 		else
! 			write(fileno(stderr), 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
! 		{
! 			const char *msg = _("Not safe to send CSV data\n");
! 
! 			write(fileno(stderr), msg, strlen(msg));
! 			if (!(Log_destination & LOG_DESTINATION_STDERR) &&
! 				whereToSendOutput != DestDebug)
! 			{
! 				/* write message to stderr unless we just sent it above */
! 				write(fileno(stderr), buf.data, buf.len);
! 			}
! 			pfree(buf.data);
! 		}
  	}
! 	else
! 	{
  		pfree(buf.data);
- 	}
  }
  
  /*
--- 2358,2396 ----
  
  #ifdef WIN32
  	/* Write to eventlog, if enabled */
! 	if (destinations & LOG_DESTINATION_EVENTLOG)
  	{
! 		write_eventlog(header->elevel, buf.data);
  	}
  #endif   /* WIN32 */
  
! 	/*
! 	 * Write to stderr if enabled. We are called with this in two cases:
! 	 * - in the syslogger, when configured to write to stderr
! 	 * - in a backend, if the syslogger is not (yet) running. In this case,
! 	 *   just dumping the data right out on our local stderr is the best
! 	 *   thing we can do.
! 	 */
! 	if (destinations & LOG_DESTINATION_STDERR)
  	{
!         /* FIXME: don't ignore write()'s return value */
! 		write(fileno(stderr), buf.data, buf.len);
  	}
  
! 	/* Write to syslogger file, if enabled */
! 	if (destinations & LOG_DESTINATION_FILE)
! 	{
! 		write_syslogger_file(buf.data);
! 	}
  
  	/* Write to CSV log if enabled */
! 	if (destinations & LOG_DESTINATION_CSVLOG)
  	{
! 		write_csvlog(header, strings);
  	}
! 
! 	if (buf.data)
  		pfree(buf.data);
  }
  
  /*
***************
*** 2293,2298 **** write_pipe_chunks(char *data, int len, int dest)
--- 2414,2420 ----
  		p.proto.is_last = (dest == LOG_DESTINATION_CSVLOG ? 'F' : 'f');
  		p.proto.len = PIPE_MAX_PAYLOAD;
  		memcpy(p.proto.data, data, PIPE_MAX_PAYLOAD);
+         /* FIXME: don't ignore write()'s return value */
  		write(fd, &p, PIPE_HEADER_SIZE + PIPE_MAX_PAYLOAD);
  		data += PIPE_MAX_PAYLOAD;
  		len -= PIPE_MAX_PAYLOAD;
***************
*** 2302,2307 **** write_pipe_chunks(char *data, int len, int dest)
--- 2424,2430 ----
  	p.proto.is_last = (dest == LOG_DESTINATION_CSVLOG ? 'T' : 't');
  	p.proto.len = len;
  	memcpy(p.proto.data, data, len);
+     /* FIXME: don't ignore write()'s return value */
  	write(fd, &p, PIPE_HEADER_SIZE + len);
  }
  
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
***************
*** 1089,1102 **** static struct config_bool ConfigureNamesBool[] =
  		false, NULL, NULL
  	},
  	{
- 		{"logging_collector", PGC_POSTMASTER, LOGGING_WHERE,
- 			gettext_noop("Start a subprocess to capture stderr output and/or csvlogs into log files."),
- 			NULL
- 		},
- 		&Logging_collector,
- 		false, NULL, NULL
- 	},
- 	{
  		{"log_truncate_on_rotation", PGC_SIGHUP, LOGGING_WHERE,
  			gettext_noop("Truncate existing log files of same name during log rotation."),
  			NULL
--- 1089,1094 ----
***************
*** 7235,7240 **** assign_log_destination(const char *value, bool doit, GucSource source)
--- 7227,7234 ----
  
  		if (pg_strcasecmp(tok, "stderr") == 0)
  			newlogdest |= LOG_DESTINATION_STDERR;
+ 		else if (pg_strcasecmp(tok, "file") == 0)
+ 			newlogdest |= LOG_DESTINATION_FILE;
  		else if (pg_strcasecmp(tok, "csvlog") == 0)
  			newlogdest |= LOG_DESTINATION_CSVLOG;
  #ifdef HAVE_SYSLOG
*** a/src/backend/utils/misc/postgresql.conf.sample
--- b/src/backend/utils/misc/postgresql.conf.sample
***************
*** 234,250 ****
  # - Where to Log -
  
  #log_destination = 'stderr'		# Valid values are combinations of
! 					# stderr, csvlog, syslog and eventlog,
! 					# depending on platform.  csvlog
! 					# requires logging_collector to be on.
! 
! # This is used when logging to stderr:
! #logging_collector = off		# Enable capturing of stderr and csvlog
! 					# into log files. Required to be on for
! 					# csvlogs.
! 					# (change requires restart)
  
! # These are only used if logging_collector is on:
  #log_directory = 'pg_log'		# directory where log files are written,
  					# can be absolute or relative to PGDATA
  #log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'	# log file name pattern,
--- 234,243 ----
  # - Where to Log -
  
  #log_destination = 'stderr'		# Valid values are combinations of
! 					# stderr, file, csvlog, syslog and eventlog,
! 					# depending on platform.
  
! # These are only used for file and csvlog destinations.
  #log_directory = 'pg_log'		# directory where log files are written,
  					# can be absolute or relative to PGDATA
  #log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'	# log file name pattern,
*** a/src/include/postmaster/syslogger.h
--- b/src/include/postmaster/syslogger.h
***************
*** 60,68 **** typedef union
  #define PIPE_HEADER_SIZE  offsetof(PipeProtoHeader, data)
  #define PIPE_MAX_PAYLOAD  ((int) (PIPE_CHUNK_SIZE - PIPE_HEADER_SIZE))
  
  
  /* GUC options */
- extern bool Logging_collector;
  extern int	Log_RotationAge;
  extern int	Log_RotationSize;
  extern PGDLLIMPORT char *Log_directory;
--- 60,102 ----
  #define PIPE_HEADER_SIZE  offsetof(PipeProtoHeader, data)
  #define PIPE_MAX_PAYLOAD  ((int) (PIPE_CHUNK_SIZE - PIPE_HEADER_SIZE))
  
+ /*
+  * header struct sent over pipe to syslogger
+  */
+ typedef struct
+ {
+ 	int			pid;
+ 	pg_time_t	timestamp;
+ 	pg_time_t	starttime;
+ 	long int	linenum;
+ 	int			backendid;
+ 	LocalTransactionId	lxid;
+ 	TransactionId		xid;
+ 	int					elevel;
+ 	int					sqlerrcode;
+ 	int					internalpos;
+ 	int					log_min_error_statement;
+ 	int					cursorpos;
+ 	int					error_lineno;
+ } syslogger_header;
+ 
+ typedef struct
+ {
+ 	const char		*user_name;
+ 	const char		*database_name;
+ 	const char		*message;
+ 	const char		*detail;
+ 	const char		*hint;
+ 	const char		*internalquery;
+ 	const char		*context;
+ 	const char		*debug_query_string;
+ 	const char		*funcname;
+ 	const char		*filename;
+ 	const char		*remote_host;
+ 	const char		*remote_port;
+ } syslogger_strings;
  
  /* GUC options */
  extern int	Log_RotationAge;
  extern int	Log_RotationSize;
  extern PGDLLIMPORT char *Log_directory;
***************
*** 80,89 **** extern HANDLE syslogPipe[2];
  
  extern int	SysLogger_Start(void);
  
! extern void write_syslogger_file(const char *buffer, int count, int dest);
  
  #ifdef EXEC_BACKEND
  extern void SysLoggerMain(int argc, char *argv[]);
  #endif
  
  #endif   /* _SYSLOGGER_H */
--- 114,128 ----
  
  extern int	SysLogger_Start(void);
  
! extern void route_log_message(const syslogger_header *header, const syslogger_strings *strings, int destinations);
! extern void write_syslogger_file(const char *buffer);
! extern void write_csvlogger_file(const char *buffer);
  
  #ifdef EXEC_BACKEND
  extern void SysLoggerMain(int argc, char *argv[]);
  #endif
  
+ /* this is in elog.c, and probably shouldn't be! */
+ void format_log_line(StringInfoData *buf, const syslogger_header *header, const syslogger_strings *str);
+ 
  #endif   /* _SYSLOGGER_H */
*** a/src/include/utils/elog.h
--- b/src/include/utils/elog.h
***************
*** 337,342 **** extern int	Log_destination;
--- 337,347 ----
  #define LOG_DESTINATION_SYSLOG	 2
  #define LOG_DESTINATION_EVENTLOG 4
  #define LOG_DESTINATION_CSVLOG	 8
+ #define LOG_DESTINATION_FILE	16
+ 
+ /* Remote (=via syslogger) and local (directly from  backend) destination masks */
+ #define LOG_DESTINATION_LOCAL_MASK (LOG_DESTINATION_SYSLOG | LOG_DESTINATION_EVENTLOG)
+ #define LOG_DESTINATION_REMOTE_MASK (~(LOG_DESTINATION_LOCAL_MASK))
  
  /* Other exported functions */
  extern void DebugFileOpen(void);
