Here is my latest version of this patch. The good news is that it now seems to work on Windows. Please review carefully (esp Magnus, Dave, Tom).


cheers

andrew
Index: src/backend/postmaster/autovacuum.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/postmaster/autovacuum.c,v
retrieving revision 1.55
diff -c -r1.55 autovacuum.c
*** src/backend/postmaster/autovacuum.c	1 Jul 2007 18:30:54 -0000	1.55
--- src/backend/postmaster/autovacuum.c	22 Jul 2007 22:18:31 -0000
***************
*** 385,390 ****
--- 385,392 ----
  	/* reset MyProcPid */
  	MyProcPid = getpid();
  
+ 	MyStartTime = time(NULL);       /* get start time for logger session id */
+ 
  	/* Identify myself via ps */
  	init_ps_display("autovacuum launcher process", "", "", "");
  
***************
*** 1403,1408 ****
--- 1405,1412 ----
  	/* reset MyProcPid */
  	MyProcPid = getpid();
  
+ 	MyStartTime = time(NULL);       /* get start time for logger session id */
+ 
  	/* Identify myself via ps */
  	init_ps_display("autovacuum worker process", "", "", "");
  
Index: src/backend/postmaster/pgarch.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/postmaster/pgarch.c,v
retrieving revision 1.29
diff -c -r1.29 pgarch.c
*** src/backend/postmaster/pgarch.c	10 Feb 2007 14:58:54 -0000	1.29
--- src/backend/postmaster/pgarch.c	22 Jul 2007 22:18:31 -0000
***************
*** 223,228 ****
--- 223,230 ----
  
  	MyProcPid = getpid();		/* reset MyProcPid */
  
+ 	MyStartTime = time(NULL);       /* get start time for logger session id */
+ 
  	/*
  	 * If possible, make this process a group leader, so that the postmaster
  	 * can signal any child processes too.
Index: src/backend/postmaster/pgstat.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/postmaster/pgstat.c,v
retrieving revision 1.161
diff -c -r1.161 pgstat.c
*** src/backend/postmaster/pgstat.c	8 Jul 2007 22:23:16 -0000	1.161
--- src/backend/postmaster/pgstat.c	22 Jul 2007 22:18:33 -0000
***************
*** 2168,2173 ****
--- 2168,2175 ----
  
  	MyProcPid = getpid();		/* reset MyProcPid */
  
+ 	MyStartTime = time(NULL);       /* get start time for logger session id */
+ 
  	/*
  	 * If possible, make this process a group leader, so that the postmaster
  	 * can signal any child processes too.  (pgstat probably never has
Index: src/backend/postmaster/postmaster.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/postmaster/postmaster.c,v
retrieving revision 1.533
diff -c -r1.533 postmaster.c
*** src/backend/postmaster/postmaster.c	19 Jul 2007 19:13:43 -0000	1.533
--- src/backend/postmaster/postmaster.c	22 Jul 2007 22:18:37 -0000
***************
*** 339,346 ****
--- 339,348 ----
  	HANDLE		PostmasterHandle;
  	HANDLE		initial_signal_pipe;
  	HANDLE		syslogPipe[2];
+ 	HANDLE		csvlogPipe[2];
  #else
  	int			syslogPipe[2];
+ 	int		    csvlogPipe[2];
  #endif
  	char		my_exec_path[MAXPGPATH];
  	char		pkglib_path[MAXPGPATH];
***************
*** 384,389 ****
--- 386,393 ----
  
  	MyProcPid = PostmasterPid = getpid();
  
+ 	MyStartTime = time(NULL);       /* get start time for logger session id */
+ 
  	IsPostmasterEnvironment = true;
  
  	/*
***************
*** 1228,1234 ****
  		}
  
  		/* If we have lost the system logger, try to start a new one */
! 		if (SysLoggerPID == 0 && Redirect_stderr)
  			SysLoggerPID = SysLogger_Start();
  
  		/*
--- 1232,1239 ----
  		}
  
  		/* If we have lost the system logger, try to start a new one */
! 		if ( SysLoggerPID == 0 &&  
! 		     (Redirect_stderr || (Log_destination & LOG_DESTINATION_CSVLOG) ) )
  			SysLoggerPID = SysLogger_Start();
  
  		/*
***************
*** 1795,1804 ****
--- 1800,1817 ----
  		if (syslogPipe[0] >= 0)
  			close(syslogPipe[0]);
  		syslogPipe[0] = -1;
+ 
+ 		if (csvlogPipe[0] >= 0)
+ 			close(csvlogPipe[0]);
+ 		csvlogPipe[0] = -1;
  #else
  		if (syslogPipe[0])
  			CloseHandle(syslogPipe[0]);
  		syslogPipe[0] = 0;
+ 
+ 		if (csvlogPipe[0])
+ 			CloseHandle(csvlogPipe[0]);
+ 		csvlogPipe[0] = 0;
  #endif
  	}
  }
***************
*** 3966,3971 ****
--- 3979,3986 ----
  #endif
  
  	memcpy(&param->syslogPipe, &syslogPipe, sizeof(syslogPipe));
+ 	memcpy(&param->csvlogPipe, &csvlogPipe, sizeof(csvlogPipe));
+ 
  
  	strlcpy(param->my_exec_path, my_exec_path, MAXPGPATH);
  
***************
*** 4169,4174 ****
--- 4184,4200 ----
  #endif
  
  	memcpy(&syslogPipe, &param->syslogPipe, sizeof(syslogPipe));
+ 	memcpy(&csvlogPipe, &param->csvlogPipe, sizeof(csvlogPipe));
+ 
+ 
+ #ifdef WIN32
+ 
+ 	if (csvlogPipe[1] > 0)
+ 		csvlog_collector = _open_osfhandle((long) csvlogPipe[1],
+ 						_O_APPEND | _O_TEXT);
+ #else
+ 	csvlog_collector = csvlogPipe[1];
+ #endif
  
  	strlcpy(my_exec_path, param->my_exec_path, MAXPGPATH);
  
Index: src/backend/postmaster/syslogger.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/postmaster/syslogger.c,v
retrieving revision 1.33
diff -c -r1.33 syslogger.c
*** src/backend/postmaster/syslogger.c	19 Jul 2007 19:13:43 -0000	1.33
--- src/backend/postmaster/syslogger.c	22 Jul 2007 22:18:38 -0000
***************
*** 86,92 ****
   */
  static pg_time_t next_rotation_time;
  static bool pipe_eof_seen = false;
! static FILE *syslogFile = NULL;
  static char *last_file_name = NULL;
  
  /* 
--- 86,92 ----
   */
  static pg_time_t next_rotation_time;
  static bool pipe_eof_seen = false;
! static FILE *syslogFile = NULL, *csvlogFile = NULL;
  static char *last_file_name = NULL;
  
  /* 
***************
*** 104,121 ****
  } save_buffer;
  
  #define CHUNK_SLOTS 20
! static save_buffer saved_chunks[CHUNK_SLOTS];
  
  /* These must be exported for EXEC_BACKEND case ... annoying */
  #ifndef WIN32
  int			syslogPipe[2] = {-1, -1};
  #else
  HANDLE		syslogPipe[2] = {0, 0};
  #endif
  
  #ifdef WIN32
! static HANDLE threadHandle = 0;
! static CRITICAL_SECTION sysfileSection;
  #endif
  
  /*
--- 104,126 ----
  } save_buffer;
  
  #define CHUNK_SLOTS 20
! static save_buffer stderr_saved_chunks[CHUNK_SLOTS]; 
! static save_buffer csvlog_saved_chunks[CHUNK_SLOTS];
  
  /* These must be exported for EXEC_BACKEND case ... annoying */
  #ifndef WIN32
  int			syslogPipe[2] = {-1, -1};
+ int			csvlogPipe[2] = {-1, -1};
  #else
  HANDLE		syslogPipe[2] = {0, 0};
+ HANDLE		csvlogPipe[2] = {0, 0};
  #endif
  
+ int csvlog_collector = -1; /* dup csv pipe ends into this */
+ 
  #ifdef WIN32
! static HANDLE stderrThreadHandle = 0, csvThreadHandle = 0;
! static CRITICAL_SECTION sysfileSection, csvfileSection;
  #endif
  
  /*
***************
*** 130,143 ****
  static pid_t syslogger_forkexec(void);
  static void syslogger_parseArgs(int argc, char *argv[]);
  #endif
! static void process_pipe_input(char *logbuffer, int *bytes_in_logbuffer);
! static void flush_pipe_input(char *logbuffer, int *bytes_in_logbuffer);
  
  #ifdef WIN32
  static unsigned int __stdcall pipeThread(void *arg);
  #endif
  static void logfile_rotate(bool time_based_rotation);
! static char *logfile_getname(pg_time_t timestamp);
  static void set_next_rotation_time(void);
  static void sigHupHandler(SIGNAL_ARGS);
  static void sigUsr1Handler(SIGNAL_ARGS);
--- 135,152 ----
  static pid_t syslogger_forkexec(void);
  static void syslogger_parseArgs(int argc, char *argv[]);
  #endif
! static void process_pipe_input(char *logbuffer, int *bytes_in_logbuffer,
! 							   int log_type);
! static void flush_pipe_input(char *logbuffer, int *bytes_in_logbuffer,
! 							 int log_type);
  
  #ifdef WIN32
  static unsigned int __stdcall pipeThread(void *arg);
  #endif
  static void logfile_rotate(bool time_based_rotation);
! static char *logfile_getname(pg_time_t timestamp, int log_type);
! static void logfile_rotate_worker(bool overwrite_logfile, int log_type, 
! 								  char* filename, FILE **dest_file);
  static void set_next_rotation_time(void);
  static void sigHupHandler(SIGNAL_ARGS);
  static void sigUsr1Handler(SIGNAL_ARGS);
***************
*** 151,158 ****
  SysLoggerMain(int argc, char *argv[])
  {
  #ifndef WIN32
! 	char		logbuffer[READ_BUF_SIZE];
! 	int			bytes_in_logbuffer = 0;
  #endif
  	char	   *currentLogDir;
  	char	   *currentLogFilename;
--- 160,169 ----
  SysLoggerMain(int argc, char *argv[])
  {
  #ifndef WIN32
! 	char		stderr_logbuffer[READ_BUF_SIZE], csv_logbuffer[READ_BUF_SIZE];
! 	int			bytes_in_stderr_logbuffer = 0, bytes_in_csv_logbuffer = 0;
! 	int         maxfd = 0;
! 
  #endif
  	char	   *currentLogDir;
  	char	   *currentLogFilename;
***************
*** 162,167 ****
--- 173,180 ----
  
  	MyProcPid = getpid();		/* reset MyProcPid */
  
+ 	MyStartTime = time(NULL);       /* get start time for logger session id */
+ 
  #ifdef EXEC_BACKEND
  	syslogger_parseArgs(argc, argv);
  #endif   /* EXEC_BACKEND */
***************
*** 177,183 ****
  	 * assumes that all interesting messages generated in the syslogger will
  	 * come through elog.c and will be sent to write_syslogger_file.
  	 */
! 	if (redirection_done)
  	{
  		int			fd = open(NULL_DEV, O_WRONLY, 0);
  
--- 190,196 ----
  	 * assumes that all interesting messages generated in the syslogger will
  	 * come through elog.c and will be sent to write_syslogger_file.
  	 */
! 	if (Redirect_stderr && redirection_done)
  	{
  		int			fd = open(NULL_DEV, O_WRONLY, 0);
  
***************
*** 203,212 ****
--- 216,244 ----
  	if (syslogPipe[1] >= 0)
  		close(syslogPipe[1]);
  	syslogPipe[1] = -1;
+ 	
+ 	if (csvlogPipe[1] >= 0)
+ 		close(csvlogPipe[1]);
+ 	csvlog_collector = -1;
+ 	csvlogPipe[1] = -1;
+ 
+ 	/* also set the max fd number for non-windows select use */
+ 	if (Redirect_stderr)
+ 		maxfd = syslogPipe[0];
+ 
+ 	if ((Log_destination & LOG_DESTINATION_CSVLOG) && 
+ 		(maxfd < csvlogPipe[0]))
+ 		maxfd = csvlogPipe[0];
+ 
  #else
  	if (syslogPipe[1])
  		CloseHandle(syslogPipe[1]);
  	syslogPipe[1] = 0;
+ 	
+ 	if (csvlogPipe[1])
+ 		CloseHandle(csvlogPipe[1]);
+ 	csvlogPipe[1] = 0;
+ 	csvlog_collector = -1;
  #endif
  
  	/*
***************
*** 249,261 ****
  	PG_SETMASK(&UnBlockSig);
  
  #ifdef WIN32
! 	/* Fire up separate data transfer thread */
! 	InitializeCriticalSection(&sysfileSection);
  
  	{
  		unsigned int tid;
  
! 		threadHandle = (HANDLE) _beginthreadex(0, 0, pipeThread, 0, 0, &tid);
  	}
  #endif   /* WIN32 */
  
--- 281,306 ----
  	PG_SETMASK(&UnBlockSig);
  
  #ifdef WIN32
! 	/* Fire up separate data transfer thread for syslog*/
! 	if (Redirect_stderr)
! 	{
! 		unsigned int tid;
! 		int logtype = STDERR_LOGFILE;
  
+ 		InitializeCriticalSection(&sysfileSection);
+ 		stderrThreadHandle = (HANDLE) _beginthreadex(0, 0, pipeThread, 
+ 								&logtype, 0, &tid);
+ 	}
+ 
+ 	/* Fire up separate data transfer thread for csvlog*/
+ 	if (Log_destination & LOG_DESTINATION_CSVLOG)
  	{
  		unsigned int tid;
+ 		int logtype = CSV_LOGFILE;
  
! 		InitializeCriticalSection(&csvfileSection);
! 		csvThreadHandle = (HANDLE) _beginthreadex(0, 0, pipeThread, 
! 								&logtype, 0, &tid);
  	}
  #endif   /* WIN32 */
  
***************
*** 276,281 ****
--- 321,327 ----
  		int			rc;
  		fd_set		rfds;
  		struct timeval timeout;
+ 
  #endif
  
  		if (got_SIGHUP)
***************
*** 337,347 ****
  		 * Wait for some data, timing out after 1 second
  		 */
  		FD_ZERO(&rfds);
! 		FD_SET(syslogPipe[0], &rfds);
  		timeout.tv_sec = 1;
  		timeout.tv_usec = 0;
  
! 		rc = select(syslogPipe[0] + 1, &rfds, NULL, NULL, &timeout);
  
  		if (rc < 0)
  		{
--- 383,396 ----
  		 * Wait for some data, timing out after 1 second
  		 */
  		FD_ZERO(&rfds);
! 		if (Redirect_stderr)
! 			FD_SET(syslogPipe[0], &rfds);
! 		if (Log_destination & LOG_DESTINATION_CSVLOG)
! 			FD_SET(csvlogPipe[0], &rfds);
  		timeout.tv_sec = 1;
  		timeout.tv_usec = 0;
  
! 		rc = select(maxfd + 1, &rfds, NULL, NULL, &timeout);
  
  		if (rc < 0)
  		{
***************
*** 350,385 ****
  						(errcode_for_socket_access(),
  						 errmsg("select() failed in logger process: %m")));
  		}
! 		else if (rc > 0 && FD_ISSET(syslogPipe[0], &rfds))
  		{
! 			bytesRead = piperead(syslogPipe[0],
! 								 logbuffer + bytes_in_logbuffer,
! 								 sizeof(logbuffer) - bytes_in_logbuffer);
! 			if (bytesRead < 0)
  			{
! 				if (errno != EINTR)
! 					ereport(LOG,
! 							(errcode_for_socket_access(),
! 							 errmsg("could not read from logger pipe: %m")));
! 			}
! 			else if (bytesRead > 0)
! 			{
! 				bytes_in_logbuffer += bytesRead;
! 				process_pipe_input(logbuffer, &bytes_in_logbuffer);
! 				continue;
  			}
! 			else
  			{
! 				/*
! 				 * Zero bytes read when select() is saying read-ready means
! 				 * EOF on the pipe: that is, there are no longer any processes
! 				 * with the pipe write end open.  Therefore, the postmaster
! 				 * and all backends are shut down, and we are done.
! 				 */
! 				pipe_eof_seen = true;
  
! 				/* if there's any data left then force it out now */
! 				flush_pipe_input(logbuffer, &bytes_in_logbuffer);
  			}
  		}
  #else							/* WIN32 */
--- 399,465 ----
  						(errcode_for_socket_access(),
  						 errmsg("select() failed in logger process: %m")));
  		}
! 		else if (rc > 0)
  		{
! 			if (Redirect_stderr && FD_ISSET(syslogPipe[0], &rfds))
  			{
! 				bytesRead = piperead(syslogPipe[0],
! 							 stderr_logbuffer + bytes_in_stderr_logbuffer,
! 							 sizeof(stderr_logbuffer) - bytes_in_stderr_logbuffer);
! 				if (bytesRead < 0)
! 				{
!                 if (errno != EINTR)
!                     ereport(LOG,
!                             (errcode_for_socket_access(),
!                              errmsg("could not read from stderr logger pipe: %m")));
! 				}
! 				else if (bytesRead > 0)
! 				{
! 					bytes_in_stderr_logbuffer += bytesRead;
! 					process_pipe_input(stderr_logbuffer, 
! 									   &bytes_in_stderr_logbuffer, 
! 									   STDERR_LOGFILE);
! 					continue;
! 				}
! 				else
! 				{
! 					/*
! 					 * Zero bytes read when select() is saying read-ready means
! 					 * EOF on the pipe: that is, there are no longer any processes
! 					 * with the pipe write end open.  Therefore, the postmaster
! 					 * and all backends are shut down, and we are done.
! 					 */
! 					pipe_eof_seen = true;
! 
! 				}
  			}
! 
! 			if ((Log_destination & LOG_DESTINATION_CSVLOG) && 
! 				FD_ISSET(csvlogPipe[0], &rfds))
  			{
! 				bytesRead = piperead(csvlogPipe[0],
! 							 csv_logbuffer + bytes_in_csv_logbuffer,
! 							 sizeof(csv_logbuffer) - bytes_in_csv_logbuffer);
! 				if (bytesRead < 0)
! 				{
!                 if (errno != EINTR)
!                     ereport(LOG,
!                             (errcode_for_socket_access(),
!                              errmsg("could not read from CSV logger pipe: %m")));
! 				}
! 				else if (bytesRead > 0)
! 				{
! 					bytes_in_csv_logbuffer += bytesRead;
! 					process_pipe_input(csv_logbuffer, 
! 									   &bytes_in_csv_logbuffer, 
! 									   CSV_LOGFILE);
! 					continue;
! 				}
! 				else
! 				{
! 					pipe_eof_seen = true;
  
! 				}
  			}
  		}
  #else							/* WIN32 */
***************
*** 394,399 ****
--- 474,489 ----
  
  		if (pipe_eof_seen)
  		{
+ 
+ #ifndef WIN32
+ 			/* if there's any data left then force it out now */
+ 			if (Redirect_stderr)
+ 				flush_pipe_input(stderr_logbuffer, &bytes_in_stderr_logbuffer, STDERR_LOGFILE);
+ 
+ 			if (Log_destination & LOG_DESTINATION_CSVLOG)
+ 				flush_pipe_input(csv_logbuffer, &bytes_in_csv_logbuffer, CSV_LOGFILE);
+ 
+ #endif
  			ereport(LOG,
  					(errmsg("logger shutting down")));
  
***************
*** 415,424 ****
  int
  SysLogger_Start(void)
  {
  	pid_t		sysloggerPid;
- 	char	   *filename;
  
! 	if (!Redirect_stderr)
  		return 0;
  
  	/*
--- 505,516 ----
  int
  SysLogger_Start(void)
  {
+ 	char	*csv_filename;
+ 	char	*stderr_filename;
+ 
  	pid_t		sysloggerPid;
  
! 	if ( (!Redirect_stderr) && (!(Log_destination & LOG_DESTINATION_CSVLOG)) )
  		return 0;
  
  	/*
***************
*** 433,461 ****
  	 * pipe open, so we can pass it down to the reincarnated syslogger. This
  	 * is a bit klugy but we have little choice.
  	 */
! #ifndef WIN32
! 	if (syslogPipe[0] < 0)
  	{
! 		if (pgpipe(syslogPipe) < 0)
! 			ereport(FATAL,
! 					(errcode_for_socket_access(),
! 					 (errmsg("could not create pipe for syslog: %m"))));
! 	}
  #else
! 	if (!syslogPipe[0])
! 	{
! 		SECURITY_ATTRIBUTES sa;
! 
! 		memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES));
! 		sa.nLength = sizeof(SECURITY_ATTRIBUTES);
! 		sa.bInheritHandle = TRUE;
  
! 		if (!CreatePipe(&syslogPipe[0], &syslogPipe[1], &sa, 32768))
! 			ereport(FATAL,
! 					(errcode_for_file_access(),
! 					 (errmsg("could not create pipe for syslog: %m"))));
  	}
  #endif
  
  	/*
  	 * Create log directory if not present; ignore errors
--- 525,586 ----
  	 * pipe open, so we can pass it down to the reincarnated syslogger. This
  	 * is a bit klugy but we have little choice.
  	 */
! 
! 	/* Create the syslog pipe only if we need to redirect stderr */
! 	if (Redirect_stderr)
  	{
! #ifndef WIN32
! 		if (syslogPipe[0] < 0)
! 		{
! 			if (pgpipe(syslogPipe) < 0)
! 				ereport(FATAL,
! 						(errcode_for_socket_access(),
! 						 (errmsg("could not create pipe for syslog: %m"))));
! 		}
  #else
! 		if (!syslogPipe[0])
! 		{
! 			SECURITY_ATTRIBUTES sa;
  
! 			memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES));
! 			sa.nLength = sizeof(SECURITY_ATTRIBUTES);
! 			sa.bInheritHandle = TRUE;
! 
! 			if (!CreatePipe(&syslogPipe[0], &syslogPipe[1], &sa, LOG_BUFFER_SIZE))
! 				ereport(FATAL,
! 						(errcode_for_file_access(),
! 						 (errmsg("could not create pipe for syslog: %m"))));
! 		}
! #endif
  	}
+ 
+ 	/* Create the csv log pipe if we need csv type log output */
+ 	if (Log_destination & LOG_DESTINATION_CSVLOG)
+ 	{
+ #ifndef WIN32
+ 		if (csvlogPipe[0] < 0)
+ 		{
+ 			if (pgpipe(csvlogPipe) < 0)
+ 				ereport(FATAL,
+ 						(errcode_for_socket_access(),
+ 						(errmsg("could not create pipe for csvlog: %m"))));
+ 		}
+ #else
+ 		if (!csvlogPipe[0])
+ 		{
+ 			SECURITY_ATTRIBUTES sa;
+ 	
+ 			memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES));
+ 			sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ 			sa.bInheritHandle = TRUE;
+ 	
+ 			if (!CreatePipe(&csvlogPipe[0], &csvlogPipe[1], &sa, LOG_BUFFER_SIZE))
+ 				ereport(FATAL,
+ 						(errcode_for_file_access(),
+ 						(errmsg("could not create pipe for csvlog: %m"))));
+ 		}
  #endif
+ 	}
  
  	/*
  	 * Create log directory if not present; ignore errors
***************
*** 466,484 ****
  	 * The initial logfile is created right in the postmaster, to verify that
  	 * the Log_directory is writable.
  	 */
! 	filename = logfile_getname(time(NULL));
  
! 	syslogFile = fopen(filename, "a");
  
! 	if (!syslogFile)
! 		ereport(FATAL,
  				(errcode_for_file_access(),
  				 (errmsg("could not create log file \"%s\": %m",
! 						 filename))));
  
- 	setvbuf(syslogFile, NULL, LBF_MODE, 0);
  
- 	pfree(filename);
  
  #ifdef EXEC_BACKEND
  	switch ((sysloggerPid = syslogger_forkexec()))
--- 591,627 ----
  	 * The initial logfile is created right in the postmaster, to verify that
  	 * the Log_directory is writable.
  	 */
! 	if (Redirect_stderr)
! 	{
! 		stderr_filename = logfile_getname(time(NULL), STDERR_LOGFILE);
! 
! 		syslogFile = fopen(stderr_filename, "a");
! 		if (!syslogFile)
! 			ereport(FATAL,
! 				(errcode_for_file_access(),
! 				 (errmsg("could not create log file \"%s\": %m",
! 							 stderr_filename))));
  
! 		setvbuf(syslogFile, NULL, LBF_MODE, 0);
! 		pfree(stderr_filename);
! 	}
! 
! 	if (Log_destination & LOG_DESTINATION_CSVLOG)
! 	{
! 		csv_filename = logfile_getname(time(NULL), CSV_LOGFILE);
  
! 		csvlogFile = fopen(csv_filename, "a");
! 		if (!csvlogFile)
! 			ereport(FATAL,
  				(errcode_for_file_access(),
  				 (errmsg("could not create log file \"%s\": %m",
! 							csv_filename))));
! 
! 		setvbuf(csvlogFile, NULL, LBF_MODE, 0);
! 		pfree(csv_filename);
! 	}
  
  
  
  #ifdef EXEC_BACKEND
  	switch ((sysloggerPid = syslogger_forkexec()))
***************
*** 515,554 ****
  			if (!redirection_done)
  			{
  #ifndef WIN32
! 				fflush(stdout);
! 				if (dup2(syslogPipe[1], fileno(stdout)) < 0)
! 					ereport(FATAL,
! 							(errcode_for_file_access(),
! 							 errmsg("could not redirect stdout: %m")));
! 				fflush(stderr);
! 				if (dup2(syslogPipe[1], fileno(stderr)) < 0)
! 					ereport(FATAL,
! 							(errcode_for_file_access(),
! 							 errmsg("could not redirect stderr: %m")));
! 				/* Now we are done with the write end of the pipe. */
! 				close(syslogPipe[1]);
! 				syslogPipe[1] = -1;
  #else
  				int			fd;
  
! 				fflush(stderr);
! 				fd = _open_osfhandle((long) syslogPipe[1],
! 									 _O_APPEND | _O_TEXT);
! 				if (dup2(fd, _fileno(stderr)) < 0)
! 					ereport(FATAL,
! 							(errcode_for_file_access(),
! 							 errmsg("could not redirect stderr: %m")));
! 				close(fd);
! 				/* Now we are done with the write end of the pipe. */
! 				CloseHandle(syslogPipe[1]);
! 				syslogPipe[1] = 0;
  #endif
  				redirection_done = true;
  			}
  
! 			/* postmaster will never write the file; close it */
! 			fclose(syslogFile);
  			syslogFile = NULL;
  			return (int) sysloggerPid;
  	}
  
--- 658,722 ----
  			if (!redirection_done)
  			{
  #ifndef WIN32
! 				if (Log_destination & LOG_DESTINATION_CSVLOG)
! 				{
! 					csvlog_collector = csvlogPipe[1];
! 				}
! 
! 				if (Redirect_stderr)
! 				{
! 					fflush(stdout);
! 					if (dup2(syslogPipe[1], fileno(stdout)) < 0)
! 						ereport(FATAL,
! 								(errcode_for_file_access(),
! 								 errmsg("could not redirect stdout: %m")));
! 					fflush(stderr);
! 					if (dup2(syslogPipe[1], fileno(stderr)) < 0)
! 						ereport(FATAL,
! 								(errcode_for_file_access(),
! 								 errmsg("could not redirect stderr: %m")));
! 					/* Now we are done with the write end of the pipe. */
! 					close(syslogPipe[1]);
! 					syslogPipe[1] = -1;
! 				}
! 
  #else
  				int			fd;
  
! 				if (Log_destination & LOG_DESTINATION_CSVLOG)
! 				{
! 					csvlog_collector = _open_osfhandle((long) csvlogPipe[1],
! 										 _O_APPEND | _O_TEXT);
! 				}
! 
! 				if (Redirect_stderr)
! 				{
! 					fflush(stderr);
! 					fd = _open_osfhandle((long) syslogPipe[1],
! 										 _O_APPEND | _O_TEXT);
! 					if (dup2(fd, _fileno(stderr)) < 0)
! 						ereport(FATAL,
! 								(errcode_for_file_access(),
! 								 errmsg("could not redirect stderr: %m")));
! 					close(fd);
! 					/* Now we are done with the write end of the pipe. */
! 					CloseHandle(syslogPipe[1]);
! 					syslogPipe[1] = 0;
! 				}
! 
  #endif
  				redirection_done = true;
  			}
  
! 			/* postmaster will never write the files; close it */
! 			if (syslogFile)
! 				fclose(syslogFile);
  			syslogFile = NULL;
+ 
+ 			if (csvlogFile)
+ 				fclose(csvlogFile);
+ 			csvlogFile = NULL;
+ 
  			return (int) sysloggerPid;
  	}
  
***************
*** 567,577 ****
  static pid_t
  syslogger_forkexec(void)
  {
! 	char	   *av[10];
! 	int			ac = 0,
! 				bufc = 0,
! 				i;
! 	char		numbuf[2][32];
  
  	av[ac++] = "postgres";
  	av[ac++] = "--forklog";
--- 735,745 ----
  static pid_t
  syslogger_forkexec(void)
  {
! 	char	*av[11];
! 	int	ac = 0,
! 		bufc = 0,
! 		i;
! 	char	numbuf[3][32];
  
  	av[ac++] = "postgres";
  	av[ac++] = "--forklog";
***************
*** 583,594 ****
--- 751,773 ----
  		snprintf(numbuf[bufc++], 32, "%d", fileno(syslogFile));
  	else
  		strcpy(numbuf[bufc++], "-1");
+ 
+ 	if (csvlogFile != NULL)
+ 		snprintf(numbuf[bufc++], 32, "%d", fileno(csvlogFile));
+ 	else
+ 		strcpy(numbuf[bufc++], "-1");
  #else							/* WIN32 */
  	if (syslogFile != NULL)
  		snprintf(numbuf[bufc++], 32, "%ld",
  				 _get_osfhandle(_fileno(syslogFile)));
  	else
  		strcpy(numbuf[bufc++], "0");
+ 
+ 	if (csvlogFile != NULL)
+ 		snprintf(numbuf[bufc++], 32, "%ld",
+ 				 _get_osfhandle(_fileno(csvlogFile)));
+ 	else
+ 		strcpy(numbuf[bufc++], "0");
  #endif   /* WIN32 */
  
  	/* Add to the arg list */
***************
*** 612,618 ****
  {
  	int			fd;
  
! 	Assert(argc == 5);
  	argv += 3;
  
  #ifndef WIN32
--- 791,797 ----
  {
  	int			fd;
  
! 	Assert(argc == 6);
  	argv += 3;
  
  #ifndef WIN32
***************
*** 622,627 ****
--- 801,812 ----
  		syslogFile = fdopen(fd, "a");
  		setvbuf(syslogFile, NULL, LBF_MODE, 0);
  	}
+ 	fd = atoi(*argv++);
+ 	if (fd != -1)
+ 	{
+ 		csvlogFile = fdopen(fd, "a");
+ 		setvbuf(csvlogFile, NULL, LBF_MODE, 0);
+ 	}
  #else							/* WIN32 */
  	fd = atoi(*argv++);
  	if (fd != 0)
***************
*** 633,638 ****
--- 818,833 ----
  			setvbuf(syslogFile, NULL, LBF_MODE, 0);
  		}
  	}
+ 	fd = atoi(*argv++);
+ 	if (fd != 0)
+ 	{
+ 		fd = _open_osfhandle(fd, _O_APPEND);
+ 		if (fd > 0)
+ 		{
+ 			csvlogFile = fdopen(fd, "a");
+ 			setvbuf(csvlogFile, NULL, LBF_MODE, 0);
+ 		}
+ 	}
  #endif   /* WIN32 */
  }
  #endif   /* EXEC_BACKEND */
***************
*** 667,676 ****
   * logbuffer, and *bytes_in_logbuffer is updated.
   */
  static void
! process_pipe_input(char *logbuffer, int *bytes_in_logbuffer)
  {
  	char   *cursor = logbuffer;
  	int		count = *bytes_in_logbuffer;
  
  	/* While we have enough for a header, process data... */
  	while (count >= (int) sizeof(PipeProtoHeader))
--- 862,877 ----
   * logbuffer, and *bytes_in_logbuffer is updated.
   */
  static void
! process_pipe_input(char *logbuffer, int *bytes_in_logbuffer, int log_type)
  {
  	char   *cursor = logbuffer;
  	int		count = *bytes_in_logbuffer;
+ 	save_buffer *saved_chunks;
+ 
+ 	saved_chunks = log_type == CSV_LOGFILE ? 
+ 		csvlog_saved_chunks :
+ 		stderr_saved_chunks;
+ 		
  
  	/* While we have enough for a header, process data... */
  	while (count >= (int) sizeof(PipeProtoHeader))
***************
*** 734,740 ****
  					 * 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);
  				}
  			}
  			else
--- 935,941 ----
  					 * 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, log_type);
  				}
  			}
  			else
***************
*** 761,774 ****
  					appendBinaryStringInfo(str,
  										   cursor + PIPE_HEADER_SIZE,
  										   p.len);
! 					write_syslogger_file(str->data, str->len);
  					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);
  				}
  			}
  
--- 962,975 ----
  					appendBinaryStringInfo(str,
  										   cursor + PIPE_HEADER_SIZE,
  										   p.len);
! 					write_syslogger_file(str->data, str->len, log_type);
  					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, log_type);
  				}
  			}
  
***************
*** 794,800 ****
  				if (cursor[chunklen] == '\0')
  					break;
  			}
! 			write_syslogger_file(cursor, chunklen);
  			cursor += chunklen;
  			count -= chunklen;
  		}
--- 995,1001 ----
  				if (cursor[chunklen] == '\0')
  					break;
  			}
! 			write_syslogger_file(cursor, chunklen, log_type);
  			cursor += chunklen;
  			count -= chunklen;
  		}
***************
*** 813,822 ****
   * useful at other times, so it is careful to leave things in a clean state.
   */
  static void
! flush_pipe_input(char *logbuffer, int *bytes_in_logbuffer)
  {
  	int i;
  	StringInfo str;
  
  	/* Dump any incomplete protocol messages */
  	for (i = 0; i < CHUNK_SLOTS; i++)
--- 1014,1029 ----
   * useful at other times, so it is careful to leave things in a clean state.
   */
  static void
! flush_pipe_input(char *logbuffer, int *bytes_in_logbuffer, int log_type)
  {
  	int i;
  	StringInfo str;
+ 	save_buffer *saved_chunks;
+ 
+ 	saved_chunks = log_type == CSV_LOGFILE ? 
+ 		csvlog_saved_chunks :
+ 		stderr_saved_chunks;
+ 		
  
  	/* Dump any incomplete protocol messages */
  	for (i = 0; i < CHUNK_SLOTS; i++)
***************
*** 824,830 ****
  		if (saved_chunks[i].pid != 0)
  		{
  			str = &(saved_chunks[i].data);
! 			write_syslogger_file(str->data, str->len);
  			saved_chunks[i].pid = 0;
  			pfree(str->data);
  		}
--- 1031,1037 ----
  		if (saved_chunks[i].pid != 0)
  		{
  			str = &(saved_chunks[i].data);
! 			write_syslogger_file(str->data, str->len, log_type);
  			saved_chunks[i].pid = 0;
  			pfree(str->data);
  		}
***************
*** 834,849 ****
  	 * remove any protocol headers that may exist in it.
  	 */
  	if (*bytes_in_logbuffer > 0)
! 		write_syslogger_file(logbuffer, *bytes_in_logbuffer);
  	*bytes_in_logbuffer = 0;
  }
  
  
- /* --------------------------------
-  *		logfile routines
-  * --------------------------------
-  */
- 
  /*
   * Write text to the currently open logfile
   *
--- 1041,1051 ----
  	 * remove any protocol headers that may exist in it.
  	 */
  	if (*bytes_in_logbuffer > 0)
! 		write_syslogger_file(logbuffer, *bytes_in_logbuffer, log_type);
  	*bytes_in_logbuffer = 0;
  }
  
  
  /*
   * Write text to the currently open logfile
   *
***************
*** 852,867 ****
   * even though its stderr does not point at the syslog pipe.
   */
  void
! write_syslogger_file(const char *buffer, int count)
  {
! 	int			rc;
  
  #ifndef WIN32
! 	rc = fwrite(buffer, 1, count, syslogFile);
  #else
! 	EnterCriticalSection(&sysfileSection);
! 	rc = fwrite(buffer, 1, count, syslogFile);
! 	LeaveCriticalSection(&sysfileSection);
  #endif
  
  	/* can't use ereport here because of possible recursion */
--- 1054,1092 ----
   * even though its stderr does not point at the syslog pipe.
   */
  void
! write_syslogger_file(const char *buffer, int count, int log_type)
  {
! 	int	rc;
! 	FILE	**fh = NULL;
! 
! 	/* Select the file to write to based on the log_type. */
! 	switch (log_type)
! 	{
! 		case STDERR_LOGFILE:
! 			fh = (FILE **) &syslogFile;
! 			break;
! 		case CSV_LOGFILE:
! 			fh = (FILE **) &csvlogFile;
! 			break;
! 		default:
! 			return;
! 	}
  
  #ifndef WIN32
! 	rc = fwrite(buffer, 1, count, *fh);
  #else
! 	if (log_type == STDERR_LOGFILE)
! 	{
! 		EnterCriticalSection(&sysfileSection);
! 		rc = fwrite(buffer, 1, count, *fh);
! 		LeaveCriticalSection(&sysfileSection);
! 	}
! 	else if (log_type == CSV_LOGFILE)
! 	{
! 		EnterCriticalSection(&csvfileSection);
! 		rc = fwrite(buffer, 1, count, *fh);
! 		LeaveCriticalSection(&csvfileSection);
! 	}
  #endif
  
  	/* can't use ereport here because of possible recursion */
***************
*** 881,894 ****
  static unsigned int __stdcall
  pipeThread(void *arg)
  {
  	char		logbuffer[READ_BUF_SIZE];
  	int			bytes_in_logbuffer = 0;
  
  	for (;;)
  	{
  		DWORD		bytesRead;
  
! 		if (!ReadFile(syslogPipe[0],
  					  logbuffer + bytes_in_logbuffer,
  					  sizeof(logbuffer) - bytes_in_logbuffer,
  					  &bytesRead, 0))
--- 1106,1127 ----
  static unsigned int __stdcall
  pipeThread(void *arg)
  {
+ 
+ 	int         log_type = *(int *)arg;
  	char		logbuffer[READ_BUF_SIZE];
  	int			bytes_in_logbuffer = 0;
+ 	HANDLE      logpipe;
+ 
+ 	if (log_type == CSV_LOGFILE)
+ 		logpipe = csvlogPipe[0];
+ 	else
+ 		logpipe = syslogPipe[0];
  
  	for (;;)
  	{
  		DWORD		bytesRead;
  
! 		if (!ReadFile(logpipe,
  					  logbuffer + bytes_in_logbuffer,
  					  sizeof(logbuffer) - bytes_in_logbuffer,
  					  &bytesRead, 0))
***************
*** 906,912 ****
  		else if (bytesRead > 0)
  		{
  			bytes_in_logbuffer += bytesRead;
! 			process_pipe_input(logbuffer, &bytes_in_logbuffer);
  		}
  	}
  
--- 1139,1145 ----
  		else if (bytesRead > 0)
  		{
  			bytes_in_logbuffer += bytesRead;
! 			process_pipe_input(logbuffer, &bytes_in_logbuffer, log_type);
  		}
  	}
  
***************
*** 914,946 ****
  	pipe_eof_seen = true;
  
  	/* if there's any data left then force it out now */
! 	flush_pipe_input(logbuffer, &bytes_in_logbuffer);
  
  	_endthread();
  	return 0;
  }
  #endif   /* WIN32 */
  
  /*
!  * perform logfile rotation
   */
  static void
  logfile_rotate(bool time_based_rotation)
  {
! 	char	   *filename;
! 	FILE	   *fh;
  
  	rotation_requested = false;
  
  	/*
! 	 * When doing a time-based rotation, invent the new logfile name based on
! 	 * the planned rotation time, not current time, to avoid "slippage" in the
! 	 * file name when we don't do the rotation immediately.
  	 */
  	if (time_based_rotation)
! 		filename = logfile_getname(next_rotation_time);
! 	else
! 		filename = logfile_getname(time(NULL));
  
  	/*
  	 * Decide whether to overwrite or append.  We can overwrite if (a)
--- 1147,1187 ----
  	pipe_eof_seen = true;
  
  	/* if there's any data left then force it out now */
! 	flush_pipe_input(logbuffer, &bytes_in_logbuffer, log_type);
  
  	_endthread();
  	return 0;
  }
  #endif   /* WIN32 */
  
+ 
  /*
!  * Log file rotation controller. Decides the filename and which file needs
!  * to be rotated. The worker method below this does the actual rotation.
   */
  static void
  logfile_rotate(bool time_based_rotation)
  {
! 	char	*filename;
! 	char	*csv_filename;
! 
! 	pg_time_t timestamp;
! 	bool overwrite_logfile;
  
  	rotation_requested = false;
  
+ 	timestamp = time(NULL);
+ 	overwrite_logfile = false;
+ 
  	/*
! 	 * When doing a time-based rotation, invent the new logfile name based
! 	 * on the planned rotation time, not current time, to avoid "slippage" 
! 	 * in the file name when we don't do the rotation immediately.
  	 */
  	if (time_based_rotation)
! 		timestamp = next_rotation_time;
! 
! 	filename = logfile_getname(timestamp, STDERR_LOGFILE);
  
  	/*
  	 * Decide whether to overwrite or append.  We can overwrite if (a)
***************
*** 956,961 ****
--- 1197,1232 ----
  	 */
  	if (Log_truncate_on_rotation && time_based_rotation &&
  		last_file_name != NULL && strcmp(filename, last_file_name) != 0)
+ 		overwrite_logfile = true;
+ 
+ 	if (Redirect_stderr)
+ 		logfile_rotate_worker(overwrite_logfile, STDERR_LOGFILE, filename, 
+ 						(FILE **) &syslogFile);
+ 
+ 	if (Log_destination & LOG_DESTINATION_CSVLOG)
+ 	{
+ 		csv_filename = logfile_getname(timestamp, CSV_LOGFILE);
+ 		logfile_rotate_worker(overwrite_logfile, CSV_LOGFILE, csv_filename,
+ 						(FILE **) &csvlogFile);
+ 	}
+ 
+ 	set_next_rotation_time();
+ 
+ 	/* instead of pfree'ing filename, remember it for next time */
+ 	if (last_file_name != NULL)
+ 		pfree(last_file_name);
+ 	last_file_name = filename;
+ }
+ 
+ /*
+  * logfile rotation worker - Does the actual file rotation 
+  */
+ static void
+ logfile_rotate_worker(bool overwrite_logfile, int log_type, char* filename, FILE **dest_file)
+ {
+ 	FILE	   *fh;
+ 
+ 	if (overwrite_logfile)
  		fh = fopen(filename, "w");
  	else
  		fh = fopen(filename, "a");
***************
*** 990,1019 ****
  
  	/* On Windows, need to interlock against data-transfer thread */
  #ifdef WIN32
! 	EnterCriticalSection(&sysfileSection);
  #endif
! 	fclose(syslogFile);
! 	syslogFile = fh;
  #ifdef WIN32
! 	LeaveCriticalSection(&sysfileSection);
  #endif
- 
- 	set_next_rotation_time();
- 
- 	/* instead of pfree'ing filename, remember it for next time */
- 	if (last_file_name != NULL)
- 		pfree(last_file_name);
- 	last_file_name = filename;
  }
  
  
  /*
!  * construct logfile name using timestamp information
   *
   * Result is palloc'd.
   */
  static char *
! logfile_getname(pg_time_t timestamp)
  {
  	char	   *filename;
  	int			len;
--- 1261,1290 ----
  
  	/* On Windows, need to interlock against data-transfer thread */
  #ifdef WIN32
! 	if (log_type == STDERR_LOGFILE)
! 		EnterCriticalSection(&sysfileSection);
! 	else
! 		EnterCriticalSection(&csvfileSection);
  #endif
! 	fclose(*dest_file);
! 	*dest_file = fh;
  #ifdef WIN32
! 	if (log_type == STDERR_LOGFILE)
! 		LeaveCriticalSection(&sysfileSection);
! 	else
! 		LeaveCriticalSection(&csvfileSection);
  #endif
  }
  
  
  /*
!  * construct logfile name using timestamp information. Adds a '.csv' as
!  * extension to csvlog files if enabled.
   *
   * Result is palloc'd.
   */
  static char *
! logfile_getname(pg_time_t timestamp, int log_type)
  {
  	char	   *filename;
  	int			len;
***************
*** 1038,1043 ****
--- 1309,1321 ----
  				 Log_filename, (unsigned long) timestamp);
  	}
  
+ 	if (log_type == CSV_LOGFILE)
+ 	{
+ 		len = strlen(filename);
+ 		/* Append .csv to the new filename */
+ 		snprintf(filename + len, MAXPGPATH - len, ".csv");
+ 	}
+ 
  	return filename;
  }
  
Index: src/backend/utils/error/elog.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/utils/error/elog.c,v
retrieving revision 1.190
diff -c -r1.190 elog.c
*** src/backend/utils/error/elog.c	21 Jul 2007 22:12:04 -0000	1.190
--- src/backend/utils/error/elog.c	22 Jul 2007 22:18:39 -0000
***************
*** 78,83 ****
--- 78,85 ----
  
  extern bool redirection_done;
  
+ char timestamp[128];
+ 
  /* GUC parameters */
  PGErrorVerbosity Log_error_verbosity = PGERROR_VERBOSE;
  char	   *Log_line_prefix = NULL;		/* format for extra log line info */
***************
*** 126,131 ****
--- 128,136 ----
  static bool is_log_level_output(int elevel, int log_min_level);
  static void write_pipe_chunks(int fd, char *data, int len);
  
+ static void write_csvlog(ErrorData *edata);
+ static void get_error_message(StringInfo buf, ErrorData *edata);
+ static void get_timestamp(StringInfo buf);
  
  /*
   * errstart --- begin an error-reporting cycle
***************
*** 1499,1539 ****
  				appendStringInfo(buf, "%ld", log_line_number);
  				break;
  			case 'm':
! 				{
! 					/*
! 					 * 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);
! 				}
  				break;
  			case 't':
  				{
--- 1504,1510 ----
  				appendStringInfo(buf, "%ld", log_line_number);
  				break;
  			case 'm':
! 				get_timestamp(buf);
  				break;
  			case 't':
  				{
***************
*** 1640,1645 ****
--- 1611,1617 ----
  {
  	StringInfoData buf;
  
+ 	memset(timestamp, '\0', sizeof(timestamp));
  	initStringInfo(&buf);
  
  	log_line_prefix(&buf);
***************
*** 1648,1664 ****
  	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');
  
--- 1620,1627 ----
  	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');
  
***************
*** 1773,1779 ****
  #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
--- 1736,1743 ----
  #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
***************
*** 1796,1805 ****
  		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);
  
  	pfree(buf.data);
  }
--- 1760,1773 ----
  		else
  			write(fileno(stderr), buf.data, buf.len);
  	}
!  
! 	/* Output log in csv format, if enabled */
! 	if(Log_destination & LOG_DESTINATION_CSVLOG) 
! 		write_csvlog(edata);
!  
  	/* If in the syslogger process, try to write messages direct to file */
  	if (am_syslogger)
! 		write_syslogger_file(buf.data, buf.len, STDERR_LOGFILE);
  
  	pfree(buf.data);
  }
***************
*** 2211,2213 ****
--- 2179,2454 ----
  
  	return false;
  }
+ 
+ 
+ /*
+  * append a CSV'd version of a string to a StringInfo
+  * We use the PostgreSQL defaults for CSV, i.e. quote = escape = '"'
+  */
+ 
+ static inline void 
+ appendCSVLiteral(StringInfo buf, const char* data)
+ {
+ 	const char * p = data;
+ 	char c;
+ 
+ 	appendStringInfoCharMacro(buf, '"');
+ 	while ( (c = *p++) != '\0' )
+ 	{
+ 		if (c == '"')
+ 			appendStringInfoCharMacro(buf, '"');
+ 		appendStringInfoCharMacro(buf, c);
+ 	}
+ 	appendStringInfoCharMacro(buf, '"');
+ }
+ 
+ /* 
+  * Constructs the error message, depending on the Errordata it gets, 
+  * in CSV (comma seperated values) format. The COPY command 
+  * can then be used to load the messages into a table.
+  */
+ static void
+ write_csvlog(ErrorData *edata)
+ {
+ 	StringInfoData msgbuf;
+ 	StringInfoData buf;
+ 
+     /* static counter for line numbers */
+     static long log_line_number = 0;
+ 
+     /* has counter been reset in current process? */
+     static int  log_my_pid = 0;
+ 
+     /*
+      * 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++;
+ 
+ 	initStringInfo(&msgbuf);
+ 	initStringInfo(&buf);
+ 
+ 	/* 
+ 	 * The format of the log output in CSV format:
+ 	 * timestamp with milliseconds, username, databasename, session id,
+ 	 * host and port number, process id, process line number, command tag, 
+      * session start time, transaction id, error severity, sql state code, 
+      * statement or error message.
+ 	 */
+ 	
+ 	/* timestamp_with_milliseconds */
+ 	/* 
+ 	 * Check if the timestamp is already calculated for the syslog message,
+ 	 * if it is, then no need to calculate it again, will use the same,
+ 	 * else get the current timestamp. This is done to put same timestamp
+ 	 * in both syslog and csvlog messages.
+ 	 */
+ 	if (timestamp[0] == '\0')
+ 		get_timestamp(&buf);
+ 	else
+ 		appendStringInfoString(&buf, timestamp);
+ 
+ 	appendStringInfoChar(&buf, ',');
+ 
+ 	/* username */
+ 	if (MyProcPort)
+ 	{
+ 		const char *username = MyProcPort->user_name;
+ 		if (username == NULL || *username == '\0')
+ 			username = _("[unknown]");
+ 
+ 		appendCSVLiteral(&buf, username);
+ 	}
+ 	appendStringInfoChar(&buf, ',');
+ 
+ 	/* databasename */
+ 	if (MyProcPort)
+ 	{
+ 		const char *dbname = MyProcPort->database_name;
+ 
+ 		if (dbname == NULL || *dbname == '\0')
+ 			dbname = _("[unknown]");
+ 
+ 		appendCSVLiteral(&buf, dbname);
+ 	}
+ 
+ 	appendStringInfoChar(&buf, ',');
+ 
+ 	/* session id */
+ 	if (MyProcPort)
+ 	{
+ 		appendStringInfo(&buf, "%lx.%x",
+ 				 (long) (MyProcPort->session_start), MyProcPid);
+ 	}
+ 	else
+ 	{
+ 		appendStringInfo(&buf, "%lx.%x", (long) MyStartTime, MyProcPid);
+ 	}
+ 
+ 	appendStringInfoChar(&buf, ',');
+ 
+ 	/* Remote host and port */
+ 	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);
+ 	}
+ 
+ 	appendStringInfoChar(&buf, ',');
+ 
+ 	/* Process id  */
+ 	if (MyProcPid != 0)
+ 		appendStringInfo(&buf, "%d", MyProcPid);
+ 
+ 	appendStringInfoChar(&buf, ',');
+ 
+ 	/* Line number */
+ 	appendStringInfo(&buf, "%ld", log_line_number);
+ 
+ 	appendStringInfoChar(&buf, ',');
+ 
+ 	/* Command tag */
+ 	if (MyProcPort)
+ 	{
+ 		const char *psdisp;
+ 		int			displen;
+ 
+ 		psdisp = get_ps_display(&displen);
+ 		appendStringInfo(&buf, "%.*s", displen, psdisp);
+ 	}
+ 
+ 	appendStringInfoChar(&buf, ',');
+ 
+ 	/* session start timestamp */
+ 	if (MyProcPort)
+ 	{
+ 		char		strfbuf[128];
+ 
+ 		strftime(strfbuf, sizeof(strfbuf),
+ 		/* 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(&MyProcPort->session_start));
+ 		appendStringInfoString(&buf, strfbuf);
+ 	}
+ 
+ 
+ 	appendStringInfoChar(&buf, ',');
+ 
+ 
+ 	/* Transaction id */
+ 	if (MyProcPort)
+ 	{
+ 		if (IsTransactionState())
+ 			appendStringInfo(&buf, "%u", GetTopTransactionId());
+ 		else
+ 			appendStringInfo(&buf, "%u", InvalidTransactionId);
+ 	}
+ 
+ 	appendStringInfoChar(&buf, ',');
+ 
+ 	/* Error severity */
+ 	if (error_severity(edata->elevel) != NULL)
+ 		appendStringInfo(&buf, "%s,", error_severity(edata->elevel));
+ 	else
+ 		appendStringInfoString(&buf, ",");
+ 	
+ 	/* SQL state code */
+ 	if (Log_error_verbosity >= PGERROR_VERBOSE)
+ 		appendStringInfo(&buf, "%s", 
+ 				 unpack_sql_state(edata->sqlerrcode));
+ 
+ 	appendStringInfoChar(&buf, ',');
+  
+ 	/* Error message and cursor position if any */
+ 	get_error_message(&msgbuf, edata);
+ 
+ 	appendCSVLiteral(&buf, 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, CSV_LOGFILE);
+ 	else
+ 		write_pipe_chunks(csvlog_collector, buf.data, buf.len);	
+ 
+ 	pfree(msgbuf.data);
+ 	pfree(buf.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 the current timestamp. Appends the calculated timestamp
+  * to the buffer passed in.
+  */
+ 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		msbuf[8];
+ 	struct timeval tv;
+ 
+ 	gettimeofday(&tv, NULL);
+ 	stamp_time = tv.tv_sec;
+ 
+ 	strftime(timestamp, sizeof(timestamp),
+ 	/* 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(timestamp + 19, msbuf, 4);
+ 
+ 	appendStringInfoString(buf, timestamp);
+ }
+ 
Index: src/backend/utils/init/globals.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/utils/init/globals.c,v
retrieving revision 1.101
diff -c -r1.101 globals.c
*** src/backend/utils/init/globals.c	16 Apr 2007 18:29:54 -0000	1.101
--- src/backend/utils/init/globals.c	22 Jul 2007 22:18:39 -0000
***************
*** 33,38 ****
--- 33,39 ----
  volatile uint32 CritSectionCount = 0;
  
  int			MyProcPid;
+ time_t			MyStartTime;
  struct Port *MyProcPort;
  long		MyCancelKey;
  
Index: src/backend/utils/misc/guc.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/utils/misc/guc.c,v
retrieving revision 1.405
diff -c -r1.405 guc.c
*** src/backend/utils/misc/guc.c	10 Jul 2007 13:14:21 -0000	1.405
--- src/backend/utils/misc/guc.c	22 Jul 2007 22:18:43 -0000
***************
*** 2211,2217 ****
  	{
  		{"log_destination", PGC_SIGHUP, LOGGING_WHERE,
  			gettext_noop("Sets the destination for server log output."),
! 			gettext_noop("Valid values are combinations of \"stderr\", \"syslog\", "
  						 "and \"eventlog\", depending on the platform."),
  			GUC_LIST_INPUT
  		},
--- 2211,2217 ----
  	{
  		{"log_destination", PGC_SIGHUP, LOGGING_WHERE,
  			gettext_noop("Sets the destination for server log output."),
! 			gettext_noop("Valid values are combinations of \"stderr\", \"syslog\", \"csvlog\","
  						 "and \"eventlog\", depending on the platform."),
  			GUC_LIST_INPUT
  		},
***************
*** 6278,6283 ****
--- 6278,6285 ----
  
  		if (pg_strcasecmp(tok, "stderr") == 0)
  			newlogdest |= LOG_DESTINATION_STDERR;
+ 		else if (pg_strcasecmp(tok, "csvlog") == 0)
+ 			newlogdest |= LOG_DESTINATION_CSVLOG;
  #ifdef HAVE_SYSLOG
  		else if (pg_strcasecmp(tok, "syslog") == 0)
  			newlogdest |= LOG_DESTINATION_SYSLOG;
Index: src/backend/utils/misc/postgresql.conf.sample
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/utils/misc/postgresql.conf.sample,v
retrieving revision 1.219
diff -c -r1.219 postgresql.conf.sample
*** src/backend/utils/misc/postgresql.conf.sample	10 Jul 2007 13:14:21 -0000	1.219
--- src/backend/utils/misc/postgresql.conf.sample	22 Jul 2007 22:18:44 -0000
***************
*** 226,232 ****
  # - Where to Log -
  
  #log_destination = 'stderr'		# Valid values are combinations of 
! 					# stderr, syslog and eventlog, 
  					# depending on platform.
  
  # This is used when logging to stderr:
--- 226,232 ----
  # - Where to Log -
  
  #log_destination = 'stderr'		# Valid values are combinations of 
! 					# stderr, syslog, csvlog and eventlog, 
  					# depending on platform.
  
  # This is used when logging to stderr:
***************
*** 234,240 ****
  					# files
  					# (change requires restart)
  
! # These are only used if redirect_stderr 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,240 ----
  					# files
  					# (change requires restart)
  
! # These are only used if redirect_stderr is on, or if log_destination is csvlog:
  #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.
Index: src/include/miscadmin.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/miscadmin.h,v
retrieving revision 1.194
diff -c -r1.194 miscadmin.h
*** src/include/miscadmin.h	16 Apr 2007 18:29:56 -0000	1.194
--- src/include/miscadmin.h	22 Jul 2007 22:18:45 -0000
***************
*** 132,137 ****
--- 132,138 ----
  extern int	MaxConnections;
  
  extern DLLIMPORT int MyProcPid;
+ extern DLLIMPORT time_t MyStartTime;
  extern DLLIMPORT struct Port *MyProcPort;
  extern long MyCancelKey;
  
Index: src/include/postmaster/syslogger.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/postmaster/syslogger.h,v
retrieving revision 1.9
diff -c -r1.9 syslogger.h
*** src/include/postmaster/syslogger.h	14 Jun 2007 01:48:51 -0000	1.9
--- src/include/postmaster/syslogger.h	22 Jul 2007 22:18:45 -0000
***************
*** 60,65 ****
--- 60,71 ----
  #define PIPE_MAX_PAYLOAD  ((int) (PIPE_CHUNK_SIZE - PIPE_HEADER_SIZE))
  
  
+ #define LOG_BUFFER_SIZE	32768
+ 
+ #define STDERR_LOGFILE	1
+ #define CSV_LOGFILE	2
+ 
+ 
  /* GUC options */
  extern bool Redirect_stderr;
  extern int	Log_RotationAge;
***************
*** 72,85 ****
  
  #ifndef WIN32
  extern int	syslogPipe[2];
  #else
  extern HANDLE syslogPipe[2];
  #endif
! 
  
  extern int	SysLogger_Start(void);
  
! extern void write_syslogger_file(const char *buffer, int count);
  
  #ifdef EXEC_BACKEND
  extern void SysLoggerMain(int argc, char *argv[]);
--- 78,93 ----
  
  #ifndef WIN32
  extern int	syslogPipe[2];
+ extern int	csvlogPipe[2];
  #else
  extern HANDLE syslogPipe[2];
+ extern HANDLE csvlogPipe[2];
  #endif
! extern int csvlog_collector;
  
  extern int	SysLogger_Start(void);
  
! extern void write_syslogger_file(const char *buffer, int count, int log_type);
  
  #ifdef EXEC_BACKEND
  extern void SysLoggerMain(int argc, char *argv[]);
Index: src/include/utils/elog.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/utils/elog.h,v
retrieving revision 1.86
diff -c -r1.86 elog.h
*** src/include/utils/elog.h	4 May 2007 02:01:02 -0000	1.86
--- src/include/utils/elog.h	22 Jul 2007 22:18:45 -0000
***************
*** 291,296 ****
--- 291,297 ----
  #define LOG_DESTINATION_STDERR	 1
  #define LOG_DESTINATION_SYSLOG	 2
  #define LOG_DESTINATION_EVENTLOG 4
+ #define LOG_DESTINATION_CSVLOG	8
  
  /* Other exported functions */
  extern void DebugFileOpen(void);
---------------------------(end of broadcast)---------------------------
TIP 6: explain analyze is your friend

Reply via email to