4343#include "storage/ipc.h"
4444#include "storage/latch.h"
4545#include "storage/pg_shmem.h"
46+ #include "tcop/tcopprot.h"
4647#include "utils/guc.h"
4748#include "utils/ps_status.h"
4849#include "utils/timestamp.h"
@@ -133,7 +134,6 @@ static void syslogger_parseArgs(int argc, char *argv[]);
133134NON_EXEC_STATIC void SysLoggerMain (int argc ,char * argv [])pg_attribute_noreturn ();
134135static void process_pipe_input (char * logbuffer ,int * bytes_in_logbuffer );
135136static void flush_pipe_input (char * logbuffer ,int * bytes_in_logbuffer );
136- static void open_csvlogfile (void );
137137static FILE * logfile_open (const char * filename ,const char * mode ,
138138bool allow_errors );
139139
@@ -269,11 +269,13 @@ SysLoggerMain(int argc, char *argv[])
269269#endif /* WIN32 */
270270
271271/*
272- * Remember activelogfile's name. We recomputethis from the reference
272+ * Remember activelogfiles' name(s) . We recompute'em from the reference
273273 * time because passing down just the pg_time_t is a lot cheaper than
274274 * passing a whole file path in the EXEC_BACKEND case.
275275 */
276276last_file_name = logfile_getname (first_syslogger_file_time ,NULL );
277+ if (csvlogFile != NULL )
278+ last_csv_file_name = logfile_getname (first_syslogger_file_time ,".csv" );
277279
278280/* remember active logfile parameters */
279281currentLogDir = pstrdup (Log_directory );
@@ -282,6 +284,13 @@ SysLoggerMain(int argc, char *argv[])
282284/* set next planned rotation time */
283285set_next_rotation_time ();
284286
287+ /*
288+ * Reset whereToSendOutput, as the postmaster will do (but hasn't yet, at
289+ * the point where we forked). This prevents duplicate output of messages
290+ * from syslogger itself.
291+ */
292+ whereToSendOutput = DestNone ;
293+
285294/* main worker loop */
286295for (;;)
287296{
@@ -328,6 +337,14 @@ SysLoggerMain(int argc, char *argv[])
328337rotation_requested = true;
329338}
330339
340+ /*
341+ * Force a rotation if CSVLOG output was just turned on or off and
342+ * we need to open or close csvlogFile accordingly.
343+ */
344+ if (((Log_destination & LOG_DESTINATION_CSVLOG )!= 0 )!=
345+ (csvlogFile != NULL ))
346+ rotation_requested = true;
347+
331348/*
332349 * If rotation time parameter changed, reset next rotation time,
333350 * but don't immediately force a rotation.
@@ -567,12 +584,27 @@ SysLogger_Start(void)
567584 * a time-based rotation.
568585 */
569586first_syslogger_file_time = time (NULL );
587+
570588filename = logfile_getname (first_syslogger_file_time ,NULL );
571589
572590syslogFile = logfile_open (filename ,"a" , false);
573591
574592pfree (filename );
575593
594+ /*
595+ * Likewise for the initial CSV log file, if that's enabled. (Note that
596+ * we open syslogFile even when only CSV output is nominally enabled,
597+ * since some code paths will write to syslogFile anyway.)
598+ */
599+ if (Log_destination & LOG_DESTINATION_CSVLOG )
600+ {
601+ filename = logfile_getname (first_syslogger_file_time ,".csv" );
602+
603+ csvlogFile = logfile_open (filename ,"a" , false);
604+
605+ pfree (filename );
606+ }
607+
576608#ifdef EXEC_BACKEND
577609switch ((sysloggerPid = syslogger_forkexec ()))
578610#else
@@ -662,9 +694,14 @@ SysLogger_Start(void)
662694redirection_done = true;
663695}
664696
665- /* postmaster will never write the file; closeit */
697+ /* postmaster will never write the file(s) ; close'em */
666698fclose (syslogFile );
667699syslogFile = NULL ;
700+ if (csvlogFile != NULL )
701+ {
702+ fclose (csvlogFile );
703+ csvlogFile = NULL ;
704+ }
668705return (int )sysloggerPid ;
669706}
670707
@@ -686,6 +723,7 @@ syslogger_forkexec(void)
686723char * av [10 ];
687724int ac = 0 ;
688725char filenobuf [32 ];
726+ char csvfilenobuf [32 ];
689727
690728av [ac ++ ]= "postgres" ;
691729av [ac ++ ]= "--forklog" ;
@@ -707,6 +745,21 @@ syslogger_forkexec(void)
707745#endif /* WIN32 */
708746av [ac ++ ]= filenobuf ;
709747
748+ #ifndef WIN32
749+ if (csvlogFile != NULL )
750+ snprintf (csvfilenobuf ,sizeof (csvfilenobuf ),"%d" ,
751+ fileno (csvlogFile ));
752+ else
753+ strcpy (csvfilenobuf ,"-1" );
754+ #else /* WIN32 */
755+ if (csvlogFile != NULL )
756+ snprintf (csvfilenobuf ,sizeof (csvfilenobuf ),"%ld" ,
757+ (long )_get_osfhandle (_fileno (csvlogFile )));
758+ else
759+ strcpy (csvfilenobuf ,"0" );
760+ #endif /* WIN32 */
761+ av [ac ++ ]= csvfilenobuf ;
762+
710763av [ac ]= NULL ;
711764Assert (ac < lengthof (av ));
712765
@@ -723,16 +776,29 @@ syslogger_parseArgs(int argc, char *argv[])
723776{
724777int fd ;
725778
726- Assert (argc == 4 );
779+ Assert (argc == 5 );
727780argv += 3 ;
728781
782+ /*
783+ * Re-open the error output files that were opened by SysLogger_Start().
784+ *
785+ * We expect this will always succeed, which is too optimistic, but if it
786+ * fails there's not a lot we can do to report the problem anyway. As
787+ * coded, we'll just crash on a null pointer dereference after failure...
788+ */
729789#ifndef WIN32
730790fd = atoi (* argv ++ );
731791if (fd != -1 )
732792{
733793syslogFile = fdopen (fd ,"a" );
734794setvbuf (syslogFile ,NULL ,PG_IOLBF ,0 );
735795}
796+ fd = atoi (* argv ++ );
797+ if (fd != -1 )
798+ {
799+ csvlogFile = fdopen (fd ,"a" );
800+ setvbuf (csvlogFile ,NULL ,PG_IOLBF ,0 );
801+ }
736802#else /* WIN32 */
737803fd = atoi (* argv ++ );
738804if (fd != 0 )
@@ -744,6 +810,16 @@ syslogger_parseArgs(int argc, char *argv[])
744810setvbuf (syslogFile ,NULL ,PG_IOLBF ,0 );
745811}
746812}
813+ fd = atoi (* argv ++ );
814+ if (fd != 0 )
815+ {
816+ fd = _open_osfhandle (fd ,_O_APPEND |_O_TEXT );
817+ if (fd > 0 )
818+ {
819+ csvlogFile = fdopen (fd ,"a" );
820+ setvbuf (csvlogFile ,NULL ,PG_IOLBF ,0 );
821+ }
822+ }
747823#endif /* WIN32 */
748824}
749825#endif /* EXEC_BACKEND */
@@ -985,13 +1061,29 @@ write_syslogger_file(const char *buffer, int count, int destination)
9851061int rc ;
9861062FILE * logfile ;
9871063
988- if (destination == LOG_DESTINATION_CSVLOG && csvlogFile == NULL )
989- open_csvlogfile ();
1064+ /*
1065+ * If we're told to write to csvlogFile, but it's not open, dump the data
1066+ * to syslogFile (which is always open) instead. This can happen if CSV
1067+ * output is enabled after postmaster start and we've been unable to open
1068+ * csvlogFile. There are also race conditions during a parameter change
1069+ * whereby backends might send us CSV output before we open csvlogFile or
1070+ * after we close it. Writing CSV-formatted output to the regular log
1071+ * file isn't great, but it beats dropping log output on the floor.
1072+ *
1073+ * Think not to improve this by trying to open csvlogFile on-the-fly. Any
1074+ * failure in that would lead to recursion.
1075+ */
1076+ logfile = (destination == LOG_DESTINATION_CSVLOG &&
1077+ csvlogFile != NULL ) ?csvlogFile :syslogFile ;
9901078
991- logfile = destination == LOG_DESTINATION_CSVLOG ?csvlogFile :syslogFile ;
9921079rc = fwrite (buffer ,1 ,count ,logfile );
9931080
994- /* can't use ereport here because of possible recursion */
1081+ /*
1082+ * Try to report any failure. We mustn't use ereport because it would
1083+ * just recurse right back here, but write_stderr is OK: it will write
1084+ * either to the postmaster's original stderr, or to /dev/null, but never
1085+ * to our input pipe which would result in a different sort of looping.
1086+ */
9951087if (rc != count )
9961088write_stderr ("could not write to log file: %s\n" ,strerror (errno ));
9971089}
@@ -1074,29 +1166,6 @@ pipeThread(void *arg)
10741166}
10751167#endif /* WIN32 */
10761168
1077- /*
1078- * Open the csv log file - we do this opportunistically, because
1079- * we don't know if CSV logging will be wanted.
1080- *
1081- * This is only used the first time we open the csv log in a given syslogger
1082- * process, not during rotations. As with opening the main log file, we
1083- * always append in this situation.
1084- */
1085- static void
1086- open_csvlogfile (void )
1087- {
1088- char * filename ;
1089-
1090- filename = logfile_getname (time (NULL ),".csv" );
1091-
1092- csvlogFile = logfile_open (filename ,"a" , false);
1093-
1094- if (last_csv_file_name != NULL )/* probably shouldn't happen */
1095- pfree (last_csv_file_name );
1096-
1097- last_csv_file_name = filename ;
1098- }
1099-
11001169/*
11011170 * Open a new logfile with proper permissions and buffering options.
11021171 *
@@ -1164,7 +1233,7 @@ logfile_rotate(bool time_based_rotation, int size_rotation_for)
11641233else
11651234fntime = time (NULL );
11661235filename = logfile_getname (fntime ,NULL );
1167- if (csvlogFile != NULL )
1236+ if (Log_destination & LOG_DESTINATION_CSVLOG )
11681237csvfilename = logfile_getname (fntime ,".csv" );
11691238
11701239/*
@@ -1216,10 +1285,16 @@ logfile_rotate(bool time_based_rotation, int size_rotation_for)
12161285filename = NULL ;
12171286}
12181287
1219- /* Same as above, but for csv file. */
1220-
1221- if (csvlogFile != NULL &&
1222- (time_based_rotation || (size_rotation_for & LOG_DESTINATION_CSVLOG )))
1288+ /*
1289+ * Same as above, but for csv file. Note that if LOG_DESTINATION_CSVLOG
1290+ * was just turned on, we might have to open csvlogFile here though it was
1291+ * not open before. In such a case we'll append not overwrite (since
1292+ * last_csv_file_name will be NULL); that is consistent with the normal
1293+ * rules since it's not a time-based rotation.
1294+ */
1295+ if ((Log_destination & LOG_DESTINATION_CSVLOG )&&
1296+ (csvlogFile == NULL ||
1297+ time_based_rotation || (size_rotation_for & LOG_DESTINATION_CSVLOG )))
12231298{
12241299if (Log_truncate_on_rotation && time_based_rotation &&
12251300last_csv_file_name != NULL &&
@@ -1250,7 +1325,8 @@ logfile_rotate(bool time_based_rotation, int size_rotation_for)
12501325return ;
12511326}
12521327
1253- fclose (csvlogFile );
1328+ if (csvlogFile != NULL )
1329+ fclose (csvlogFile );
12541330csvlogFile = fh ;
12551331
12561332/* instead of pfree'ing filename, remember it for next time */
@@ -1259,6 +1335,16 @@ logfile_rotate(bool time_based_rotation, int size_rotation_for)
12591335last_csv_file_name = csvfilename ;
12601336csvfilename = NULL ;
12611337}
1338+ else if (!(Log_destination & LOG_DESTINATION_CSVLOG )&&
1339+ csvlogFile != NULL )
1340+ {
1341+ /* CSVLOG was just turned off, so close the old file */
1342+ fclose (csvlogFile );
1343+ csvlogFile = NULL ;
1344+ if (last_csv_file_name != NULL )
1345+ pfree (last_csv_file_name );
1346+ last_csv_file_name = NULL ;
1347+ }
12621348
12631349if (filename )
12641350pfree (filename );