Thread: logger subprocess

logger subprocess

From
Andreas Pflug
Date:
This is the known patch, with following changes:

- realStdErr handed over for EXEC_BACKEND, but still not tested
- Sometimes EMFILE is received in the logger's process queue, when a
backend ended after a SSL connection was interrupted. This is ignored
now (previously it forced an exit(1) and restart of the subprocess)
- log_destination needs to be PGC_POSTMASTER, because the logger process
creation depends on that.
- no functions included

Regards,
Andreas
Index: src/backend/postmaster/Makefile
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/postmaster/Makefile,v
retrieving revision 1.18
diff -u -r1.18 Makefile
--- src/backend/postmaster/Makefile    21 Jul 2004 20:34:46 -0000    1.18
+++ src/backend/postmaster/Makefile    27 Jul 2004 10:33:30 -0000
@@ -12,7 +12,7 @@
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global

-OBJS = postmaster.o bgwriter.o pgstat.o pgarch.o
+OBJS = postmaster.o bgwriter.o pgstat.o pgarch.o syslogger.o

 all: SUBSYS.o

Index: src/backend/postmaster/postmaster.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/postmaster/postmaster.c,v
retrieving revision 1.416
diff -u -r1.416 postmaster.c
--- src/backend/postmaster/postmaster.c    27 Jul 2004 01:46:03 -0000    1.416
+++ src/backend/postmaster/postmaster.c    27 Jul 2004 10:33:36 -0000
@@ -118,7 +118,7 @@
 #include "utils/ps_status.h"
 #include "bootstrap/bootstrap.h"
 #include "pgstat.h"
-
+#include "postmaster/syslogger.h"

 /*
  * List of active backends (or child processes anyway; we don't actually
@@ -201,6 +201,7 @@
             BgWriterPID = 0,
             PgArchPID = 0,
             PgStatPID = 0;
+pid_t       SysLoggerPID = 0;

 /* Startup/shutdown state */
 #define            NoShutdown        0
@@ -852,6 +853,12 @@
 #endif

     /*
+     * start logging to file
+     */
+
+    SysLoggerPID = SysLogger_Start();
+
+    /*
      * Reset whereToSendOutput from Debug (its starting state) to None.
      * This stops ereport from sending log messages to stderr unless
      * Log_destination permits.  We don't do this until the postmaster
@@ -1231,6 +1238,11 @@
             StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
             PgStatPID = pgstat_start();

+        /* If we have lost the system logger, try to start a new one */
+        if (SysLoggerPID == 0 &&
+            StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
+            SysLoggerPID = SysLogger_Start();
+
         /*
          * Touch the socket and lock file at least every ten minutes, to ensure
          * that they are not removed by overzealous /tmp-cleaning tasks.
@@ -1771,6 +1783,9 @@
             kill(BgWriterPID, SIGHUP);
         if (PgArchPID != 0)
             kill(PgArchPID, SIGHUP);
+        if (SysLoggerPID != 0)
+            kill(SysLoggerPID, SIGHUP);
+
         /* PgStatPID does not currently need SIGHUP */
         load_hba();
         load_ident();
@@ -1836,7 +1851,6 @@
             if (PgStatPID != 0)
                 kill(PgStatPID, SIGQUIT);
             break;
-
         case SIGINT:
             /*
              * Fast Shutdown:
@@ -1903,6 +1917,7 @@
                 kill(PgStatPID, SIGQUIT);
             if (DLGetHead(BackendList))
                 SignalChildren(SIGQUIT);
+
             ExitPostmaster(0);
             break;
     }
@@ -2065,6 +2080,15 @@
             continue;
         }

+        /* was it the system logger, try to start a new one */
+        if (SysLoggerPID != 0 && pid == SysLoggerPID)
+        {
+            if (exitstatus != 0)
+                LogChildExit(LOG, gettext("system logger process"),
+                             pid, exitstatus);
+            SysLoggerPID = SysLogger_Start();
+            continue;
+        }
         /*
          * Else do standard backend child cleanup.
          */
@@ -2968,6 +2992,16 @@
         PgstatCollectorMain(argc, argv);
         proc_exit(0);
     }
+    if (strcmp(argv[1], "-forklog") == 0)
+    {
+        /* Close the postmaster's sockets */
+        ClosePostmasterPorts();
+
+        /* Do not want to attach to shared memory */
+
+        SysLoggerMain(argc, argv);
+        proc_exit(0);
+    }

     return 1;                    /* shouldn't get here */
 }
@@ -3024,7 +3058,6 @@
         if (Shutdown <= SmartShutdown)
             SignalChildren(SIGUSR1);
     }
-
     if (PgArchPID != 0 && Shutdown == NoShutdown)
     {
         if (CheckPostmasterSignal(PMSIGNAL_WAKEN_ARCHIVER))
@@ -3036,6 +3069,10 @@
             kill(PgArchPID, SIGUSR1);
         }
     }
+    if (CheckPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE) && SysLoggerPID != 0)
+    {
+        kill(SysLoggerPID, SIGUSR1);
+    }

     PG_SETMASK(&UnBlockSig);

Index: src/backend/utils/error/elog.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/error/elog.c,v
retrieving revision 1.142
diff -u -r1.142 elog.c
--- src/backend/utils/error/elog.c    24 Jun 2004 21:03:13 -0000    1.142
+++ src/backend/utils/error/elog.c    27 Jul 2004 10:33:39 -0000
@@ -84,6 +84,10 @@
 static void write_eventlog(int level, const char *line);
 #endif

+/* in syslogger.c */
+extern FILE *syslogFile;
+extern FILE *realStdErr;
+extern pid_t SysLoggerPID;
 /*
  * ErrorData holds the data accumulated during any one ereport() cycle.
  * Any non-NULL pointers must point to palloc'd data in ErrorContext.
@@ -1451,10 +1455,31 @@
         write_eventlog(eventlog_level, buf.data);
     }
 #endif   /* WIN32 */
-    /* Write to stderr, if enabled */
-    if ((Log_destination & LOG_DESTINATION_STDERR) || whereToSendOutput == Debug)
+
+    /*
+     * Write to stderr. If Log_destination is file or stderr
+     * if file is target, the logger process will handle this
+     */
+    if ((Log_destination & (LOG_DESTINATION_STDERR | LOG_DESTINATION_FILE))
+        || whereToSendOutput == Debug)
     {
-        fprintf(stderr, "%s", buf.data);
+        if (SysLoggerPID == MyProcPid && realStdErr != 0)
+        {
+            /*
+             * If realStdErr is not null in the SysLogger process,
+             * there's something really wrong because stderr is probably
+             * redirected to the pipe. To avoid circular writes, we
+             * write to realStdErr which is hopefully the stderr the postmaster
+             * was started with.
+             */
+            fprintf(realStdErr, "%s", buf.data);
+        }
+        else
+            fprintf(stderr, "%s", buf.data) ;
+
+        /* syslogFile is open in SysLogger only */
+        if (syslogFile != 0)
+            fprintf(syslogFile, "%s", buf.data) ;
     }

     pfree(buf.data);
Index: src/backend/utils/misc/guc.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/misc/guc.c,v
retrieving revision 1.224
diff -u -r1.224 guc.c
--- src/backend/utils/misc/guc.c    24 Jul 2004 19:51:23 -0000    1.224
+++ src/backend/utils/misc/guc.c    27 Jul 2004 10:33:49 -0000
@@ -44,6 +44,7 @@
 #include "parser/parse_expr.h"
 #include "parser/parse_relation.h"
 #include "postmaster/bgwriter.h"
+#include "postmaster/syslogger.h"
 #include "postmaster/postmaster.h"
 #include "storage/bufmgr.h"
 #include "storage/fd.h"
@@ -1285,6 +1286,23 @@
         BLCKSZ, BLCKSZ, BLCKSZ, NULL, NULL
     },

+    {
+      {"log_rotation_age", PGC_SIGHUP, LOGGING_WHERE,
+       gettext_noop("Automatic logfile rotation will occur after n minutes"),
+       NULL
+      },
+      &Log_RotationAge,
+      24*60, 0, INT_MAX, NULL, NULL
+    },
+    {
+      {"log_rotation_size", PGC_SIGHUP, LOGGING_WHERE,
+       gettext_noop("Automatic logfile rotation will occur if this size is reached (in kb)"),
+       NULL
+      },
+      &Log_RotationSize,
+      10*1024, 0, INT_MAX, NULL, NULL
+    },
+
     /* End-of-list marker */
     {
         {NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL
@@ -1625,15 +1643,34 @@
     },

     {
-        {"log_destination", PGC_SIGHUP, LOGGING_WHERE,
-         gettext_noop("Sets the destination for server log output."),
-         gettext_noop("Valid values are combinations of stderr, syslog "
+        {"log_destination", PGC_POSTMASTER, LOGGING_WHERE,
+         gettext_noop("Sets the target for log output."),
+         gettext_noop("Valid values are combinations of stderr, file, syslog "
                       "and eventlog, depending on platform."),
          GUC_LIST_INPUT
         },
         &log_destination_string,
         "stderr", assign_log_destination, NULL
     },
+    {
+        {"log_directory", PGC_SIGHUP, LOGGING_WHERE,
+         gettext_noop("Sets the target directory for log output."),
+         gettext_noop("May be specified as relative to the cluster directory "
+                      "or as absolute path."),
+         GUC_LIST_INPUT | GUC_REPORT
+        },
+        &Log_directory,
+        "pg_log", NULL, NULL
+    },
+    {
+        {"log_filename_prefix", PGC_SIGHUP, LOGGING_WHERE,
+         gettext_noop("prefix for logfile names created in the log_directory."),
+         NULL,
+         GUC_LIST_INPUT | GUC_REPORT
+        },
+        &Log_filename_prefix,
+        "postgresql-", NULL, NULL
+    },

 #ifdef HAVE_SYSLOG
     {
@@ -5079,6 +5116,8 @@

         if (pg_strcasecmp(tok,"stderr") == 0)
             newlogdest |= LOG_DESTINATION_STDERR;
+        else if (pg_strcasecmp(tok,"file") == 0)
+            newlogdest |= LOG_DESTINATION_FILE;
 #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-server/src/backend/utils/misc/postgresql.conf.sample,v
retrieving revision 1.118
diff -u -r1.118 postgresql.conf.sample
--- src/backend/utils/misc/postgresql.conf.sample    21 Jul 2004 20:34:46 -0000    1.118
+++ src/backend/utils/misc/postgresql.conf.sample    27 Jul 2004 10:33:49 -0000
@@ -167,9 +167,17 @@

 # - Where to Log -

-#log_destination = 'stderr'    # Valid values are combinations of stderr,
-                                # syslog and eventlog, depending on
-                                # platform.
+#log_destination = 'stderr' # Valid values are combinations of stderr, file,
+                            # syslog and eventlog, depending on platform.
+#log_directory = 'pg_log'   # subdirectory where logfiles are written
+                            # if 'file' log_destination is used.
+                            # May be specified absolute or relative to PGDATA
+#log_filename_prefix = 'postgresql_' # prefix for logfile names
+#log_rotation_age = 1440    # Automatic rotation of logfiles will happen if
+                            # specified age in minutes is reached. 0 to disable.
+#log_rotation_size = 10240  # Automatic rotation of logfiles will happen if
+                            # specified size in kb is reached. 0 to disable.
+
 #syslog_facility = 'LOCAL0'
 #syslog_ident = 'postgres'

Index: src/include/storage/pmsignal.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/storage/pmsignal.h,v
retrieving revision 1.9
diff -u -r1.9 pmsignal.h
--- src/include/storage/pmsignal.h    19 Jul 2004 02:47:15 -0000    1.9
+++ src/include/storage/pmsignal.h    27 Jul 2004 10:33:51 -0000
@@ -25,7 +25,7 @@
     PMSIGNAL_PASSWORD_CHANGE,    /* pg_pwd file has changed */
     PMSIGNAL_WAKEN_CHILDREN,    /* send a SIGUSR1 signal to all backends */
     PMSIGNAL_WAKEN_ARCHIVER,    /* send a NOTIFY signal to xlog archiver */
-
+    PMSIGNAL_ROTATE_LOGFILE,    /* send SIGUSR1 to syslogger to rotate logfile */
     NUM_PMSIGNALS                /* Must be last value of enum! */
 } PMSignalReason;

Index: src/include/utils/elog.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/utils/elog.h,v
retrieving revision 1.70
diff -u -r1.70 elog.h
--- src/include/utils/elog.h    6 Jul 2004 19:51:59 -0000    1.70
+++ src/include/utils/elog.h    27 Jul 2004 10:33:51 -0000
@@ -185,10 +185,10 @@
 #define LOG_DESTINATION_STDERR   1
 #define LOG_DESTINATION_SYSLOG   2
 #define LOG_DESTINATION_EVENTLOG 4
+#define LOG_DESTINATION_FILE     8

 /* Other exported functions */
 extern void DebugFileOpen(void);
-
 /*
  * Write errors to stderr (or by equal means when stderr is
  * not available). Used before ereport/elog can be used

Re: logger subprocess

From
Andreas Pflug
Date:
Bruce Momjian wrote:
> Your patch has been added to the PostgreSQL unapplied patches list at:
>
>     http://momjian.postgresql.org/cgi-bin/pgpatches
>
> It will be applied as soon as one of the PostgreSQL committers reviews
> and approves it.

Do not apply.
I'm investigating issues under win32.
Main issue:
pgpipe doesn't create a pipe, but sockets. win32 doesn't like to
redirect stderr into sockets...

There's also an issue about file handles not being inherited. The file
handles are there with _spawnl, but not CreateProcess (despite
bInheritHandles=true). Still hunting.


Regards,
Andreas

Re: logger subprocess

From
Bruce Momjian
Date:
Retracted by author.

---------------------------------------------------------------------------

Andreas Pflug wrote:
> This is the known patch, with following changes:
>
> - realStdErr handed over for EXEC_BACKEND, but still not tested
> - Sometimes EMFILE is received in the logger's process queue, when a
> backend ended after a SSL connection was interrupted. This is ignored
> now (previously it forced an exit(1) and restart of the subprocess)
> - log_destination needs to be PGC_POSTMASTER, because the logger process
> creation depends on that.
> - no functions included
>
> Regards,
> Andreas


>
> ---------------------------(end of broadcast)---------------------------
> TIP 4: Don't 'kill -9' the postmaster

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073

Re: logger subprocess

From
Andreas Pflug
Date:
This patch is updated to compile under WIN32.

It does *not* implement the syslogger subprocess facility. This will
have to be implemented in completely win32 specific way; see discussion
on win32-hackers.

Regards,
Andreas
Index: src/backend/postmaster/Makefile
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/postmaster/Makefile,v
retrieving revision 1.18
diff -u -r1.18 Makefile
--- src/backend/postmaster/Makefile    21 Jul 2004 20:34:46 -0000    1.18
+++ src/backend/postmaster/Makefile    29 Jul 2004 08:41:52 -0000
@@ -12,7 +12,7 @@
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global

-OBJS = postmaster.o bgwriter.o pgstat.o pgarch.o
+OBJS = postmaster.o bgwriter.o pgstat.o pgarch.o syslogger.o

 all: SUBSYS.o

Index: src/backend/postmaster/postmaster.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/postmaster/postmaster.c,v
retrieving revision 1.416
diff -u -r1.416 postmaster.c
--- src/backend/postmaster/postmaster.c    27 Jul 2004 01:46:03 -0000    1.416
+++ src/backend/postmaster/postmaster.c    29 Jul 2004 08:41:57 -0000
@@ -118,7 +118,7 @@
 #include "utils/ps_status.h"
 #include "bootstrap/bootstrap.h"
 #include "pgstat.h"
-
+#include "postmaster/syslogger.h"

 /*
  * List of active backends (or child processes anyway; we don't actually
@@ -201,6 +201,7 @@
             BgWriterPID = 0,
             PgArchPID = 0,
             PgStatPID = 0;
+pid_t       SysLoggerPID = 0;

 /* Startup/shutdown state */
 #define            NoShutdown        0
@@ -852,6 +853,12 @@
 #endif

     /*
+     * start logging to file
+     */
+
+    SysLoggerPID = SysLogger_Start();
+
+    /*
      * Reset whereToSendOutput from Debug (its starting state) to None.
      * This stops ereport from sending log messages to stderr unless
      * Log_destination permits.  We don't do this until the postmaster
@@ -1231,6 +1238,11 @@
             StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
             PgStatPID = pgstat_start();

+        /* If we have lost the system logger, try to start a new one */
+        if (SysLoggerPID == 0 &&
+            StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
+            SysLoggerPID = SysLogger_Start();
+
         /*
          * Touch the socket and lock file at least every ten minutes, to ensure
          * that they are not removed by overzealous /tmp-cleaning tasks.
@@ -1771,6 +1783,9 @@
             kill(BgWriterPID, SIGHUP);
         if (PgArchPID != 0)
             kill(PgArchPID, SIGHUP);
+        if (SysLoggerPID != 0)
+            SysLogger_SIGHUP();
+
         /* PgStatPID does not currently need SIGHUP */
         load_hba();
         load_ident();
@@ -1836,7 +1851,6 @@
             if (PgStatPID != 0)
                 kill(PgStatPID, SIGQUIT);
             break;
-
         case SIGINT:
             /*
              * Fast Shutdown:
@@ -2065,6 +2079,15 @@
             continue;
         }

+        /* was it the system logger, try to start a new one */
+        if (SysLoggerPID != 0 && pid == SysLoggerPID)
+        {
+            if (exitstatus != 0)
+                LogChildExit(LOG, gettext("system logger process"),
+                             pid, exitstatus);
+            SysLoggerPID = SysLogger_Start();
+            continue;
+        }
         /*
          * Else do standard backend child cleanup.
          */
@@ -2968,7 +2991,18 @@
         PgstatCollectorMain(argc, argv);
         proc_exit(0);
     }
+#ifndef WIN32
+    if (strcmp(argv[1], "-forklog") == 0)
+    {
+        /* Close the postmaster's sockets */
+        ClosePostmasterPorts();
+
+        /* Do not want to attach to shared memory */

+        SysLoggerMain(argc, argv);
+        proc_exit(0);
+    }
+#endif
     return 1;                    /* shouldn't get here */
 }

@@ -3024,7 +3058,6 @@
         if (Shutdown <= SmartShutdown)
             SignalChildren(SIGUSR1);
     }
-
     if (PgArchPID != 0 && Shutdown == NoShutdown)
     {
         if (CheckPostmasterSignal(PMSIGNAL_WAKEN_ARCHIVER))
@@ -3036,6 +3069,10 @@
             kill(PgArchPID, SIGUSR1);
         }
     }
+    if (CheckPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE) && SysLoggerPID != 0)
+    {
+        SysLogger_SIGUSR1();
+    }

     PG_SETMASK(&UnBlockSig);

Index: src/backend/utils/error/elog.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/error/elog.c,v
retrieving revision 1.143
diff -u -r1.143 elog.c
--- src/backend/utils/error/elog.c    28 Jul 2004 22:05:46 -0000    1.143
+++ src/backend/utils/error/elog.c    29 Jul 2004 08:42:01 -0000
@@ -82,6 +82,10 @@
 static void write_eventlog(int level, const char *line);
 #endif

+/* in syslogger.c */
+extern FILE *syslogFile;
+extern FILE *realStdErr;
+extern pid_t SysLoggerPID;
 /*
  * ErrorData holds the data accumulated during any one ereport() cycle.
  * Any non-NULL pointers must point to palloc'd data in ErrorContext.
@@ -1440,10 +1444,31 @@
         write_eventlog(eventlog_level, buf.data);
     }
 #endif   /* WIN32 */
-    /* Write to stderr, if enabled */
-    if ((Log_destination & LOG_DESTINATION_STDERR) || whereToSendOutput == Debug)
+
+    /*
+     * Write to stderr. If Log_destination is file or stderr
+     * if file is target, the logger process will handle this
+     */
+    if ((Log_destination & (LOG_DESTINATION_STDERR | LOG_DESTINATION_FILE))
+        || whereToSendOutput == Debug)
     {
-        fprintf(stderr, "%s", buf.data);
+        if (SysLoggerPID == MyProcPid && realStdErr != 0)
+        {
+            /*
+             * If realStdErr is not null in the SysLogger process,
+             * there's something really wrong because stderr is probably
+             * redirected to the pipe. To avoid circular writes, we
+             * write to realStdErr which is hopefully the stderr the postmaster
+             * was started with.
+             */
+            fprintf(realStdErr, "%s", buf.data);
+        }
+        else
+            fprintf(stderr, "%s", buf.data) ;
+
+        /* syslogFile is open in SysLogger only */
+        if (syslogFile != 0)
+            fprintf(syslogFile, "%s", buf.data) ;
     }

     pfree(buf.data);
Index: src/backend/utils/misc/guc.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/misc/guc.c,v
retrieving revision 1.225
diff -u -r1.225 guc.c
--- src/backend/utils/misc/guc.c    28 Jul 2004 14:23:29 -0000    1.225
+++ src/backend/utils/misc/guc.c    29 Jul 2004 08:42:10 -0000
@@ -44,6 +44,7 @@
 #include "parser/parse_expr.h"
 #include "parser/parse_relation.h"
 #include "postmaster/bgwriter.h"
+#include "postmaster/syslogger.h"
 #include "postmaster/postmaster.h"
 #include "storage/bufmgr.h"
 #include "storage/fd.h"
@@ -1285,6 +1286,23 @@
         BLCKSZ, BLCKSZ, BLCKSZ, NULL, NULL
     },

+    {
+      {"log_rotation_age", PGC_SIGHUP, LOGGING_WHERE,
+       gettext_noop("Automatic logfile rotation will occur after n minutes"),
+       NULL
+      },
+      &Log_RotationAge,
+      24*60, 0, INT_MAX, NULL, NULL
+    },
+    {
+      {"log_rotation_size", PGC_SIGHUP, LOGGING_WHERE,
+       gettext_noop("Automatic logfile rotation will occur if this size is reached (in kb)"),
+       NULL
+      },
+      &Log_RotationSize,
+      10*1024, 0, INT_MAX, NULL, NULL
+    },
+
     /* End-of-list marker */
     {
         {NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL
@@ -1623,9 +1641,9 @@
         &session_authorization_string,
         NULL, assign_session_authorization, show_session_authorization
     },
-
+#ifdef WIN32
     {
-        {"log_destination", PGC_SIGHUP, LOGGING_WHERE,
+        {"log_destination", PGC_POSTMASTER, LOGGING_WHERE,
          gettext_noop("Sets the destination for server log output."),
          gettext_noop("Valid values are combinations of stderr, syslog "
                       "and eventlog, depending on platform."),
@@ -1634,6 +1652,37 @@
         &log_destination_string,
         "stderr", assign_log_destination, NULL
     },
+#else
+    {
+        {"log_destination", PGC_POSTMASTER, LOGGING_WHERE,
+         gettext_noop("Sets the destination for server log output."),
+         gettext_noop("Valid values are combinations of stderr, file, syslog "
+                      "and eventlog, depending on platform."),
+         GUC_LIST_INPUT
+        },
+        &log_destination_string,
+        "stderr", assign_log_destination, NULL
+    },
+#endif
+    {
+        {"log_directory", PGC_SIGHUP, LOGGING_WHERE,
+         gettext_noop("Sets the target directory for log output."),
+         gettext_noop("May be specified as relative to the cluster directory "
+                      "or as absolute path."),
+         GUC_LIST_INPUT | GUC_REPORT
+        },
+        &Log_directory,
+        "pg_log", NULL, NULL
+    },
+    {
+        {"log_filename_prefix", PGC_SIGHUP, LOGGING_WHERE,
+         gettext_noop("prefix for logfile names created in the log_directory."),
+         NULL,
+         GUC_LIST_INPUT | GUC_REPORT
+        },
+        &Log_filename_prefix,
+        "postgresql-", NULL, NULL
+    },

 #ifdef HAVE_SYSLOG
     {
@@ -5079,6 +5128,10 @@

         if (pg_strcasecmp(tok,"stderr") == 0)
             newlogdest |= LOG_DESTINATION_STDERR;
+#ifndef WIN32
+        else if (pg_strcasecmp(tok,"file") == 0)
+            newlogdest |= LOG_DESTINATION_FILE;
+#endif
 #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-server/src/backend/utils/misc/postgresql.conf.sample,v
retrieving revision 1.118
diff -u -r1.118 postgresql.conf.sample
--- src/backend/utils/misc/postgresql.conf.sample    21 Jul 2004 20:34:46 -0000    1.118
+++ src/backend/utils/misc/postgresql.conf.sample    29 Jul 2004 08:42:11 -0000
@@ -167,9 +167,17 @@

 # - Where to Log -

-#log_destination = 'stderr'    # Valid values are combinations of stderr,
-                                # syslog and eventlog, depending on
-                                # platform.
+#log_destination = 'stderr' # Valid values are combinations of stderr, file,
+                            # syslog and eventlog, depending on platform.
+#log_directory = 'pg_log'   # subdirectory where logfiles are written
+                            # if 'file' log_destination is used.
+                            # May be specified absolute or relative to PGDATA
+#log_filename_prefix = 'postgresql_' # prefix for logfile names
+#log_rotation_age = 1440    # Automatic rotation of logfiles will happen if
+                            # specified age in minutes is reached. 0 to disable.
+#log_rotation_size = 10240  # Automatic rotation of logfiles will happen if
+                            # specified size in kb is reached. 0 to disable.
+
 #syslog_facility = 'LOCAL0'
 #syslog_ident = 'postgres'

Index: src/include/storage/pmsignal.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/storage/pmsignal.h,v
retrieving revision 1.9
diff -u -r1.9 pmsignal.h
--- src/include/storage/pmsignal.h    19 Jul 2004 02:47:15 -0000    1.9
+++ src/include/storage/pmsignal.h    29 Jul 2004 08:42:12 -0000
@@ -25,7 +25,7 @@
     PMSIGNAL_PASSWORD_CHANGE,    /* pg_pwd file has changed */
     PMSIGNAL_WAKEN_CHILDREN,    /* send a SIGUSR1 signal to all backends */
     PMSIGNAL_WAKEN_ARCHIVER,    /* send a NOTIFY signal to xlog archiver */
-
+    PMSIGNAL_ROTATE_LOGFILE,    /* send SIGUSR1 to syslogger to rotate logfile */
     NUM_PMSIGNALS                /* Must be last value of enum! */
 } PMSignalReason;

Index: src/include/utils/elog.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/utils/elog.h,v
retrieving revision 1.70
diff -u -r1.70 elog.h
--- src/include/utils/elog.h    6 Jul 2004 19:51:59 -0000    1.70
+++ src/include/utils/elog.h    29 Jul 2004 08:42:13 -0000
@@ -185,6 +185,7 @@
 #define LOG_DESTINATION_STDERR   1
 #define LOG_DESTINATION_SYSLOG   2
 #define LOG_DESTINATION_EVENTLOG 4
+#define LOG_DESTINATION_FILE     8

 /* Other exported functions */
 extern void DebugFileOpen(void);
/*-------------------------------------------------------------------------
 *
 * syslogger.c
 *
 * The system logger (syslogger) is new in Postgres 7.5. It catches all
 * stderr output from backends, the postmaster and subprocesses by
 * redirecting to a pipe, and writes it to a logfile and stderr if
 * configured.
 * It's possible to have size and age limits for the logfile configured
 * in postgresql.conf. If these limits are reached or passed, the
 * current logfile is closed and a new one is created (rotated).
 * The logfiles are stored in a subdirectory (configurable in
 * postgresql.conf), using an internal naming scheme that mangles
 * creation time and current postmaster pid.
 *
 * Author: Andreas Pflug <pgadmin@pse-consulting.de>
 *
 * Copyright (c) 2004, PostgreSQL Global Development Group
 *
 *
 * IDENTIFICATION
 *      $PostgreSQL: $
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <signal.h>
#include <time.h>
#include <unistd.h>
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#include "postmaster/postmaster.h"
#include "storage/pmsignal.h"
#include "storage/pg_shmem.h"
#include "storage/ipc.h"
#include "postmaster/syslogger.h"
#include "utils/ps_status.h"
#include "utils/guc.h"

/*
 * GUC parameters
 */
int            Log_RotationAge = 24*60;
int            Log_RotationSize  = 10*1024;
char *      Log_directory = "pg_log";
char *      Log_filename_prefix = "postgresql-";


extern pid_t SysLoggerPID;


/*
 * Flags set by interrupt handlers for later service in the main loop.
 */
static volatile sig_atomic_t got_SIGHUP = false;
static volatile sig_atomic_t rotation_requested = false;

#define MAXRETRIES  3
static pg_time_t    last_rotation_time = 0;
static int          pipeRetry=0;
static char         currentLogDir[MAXPGPATH];


static void sigHupHandler(SIGNAL_ARGS);
static void rotationHandler(SIGNAL_ARGS);

#ifdef EXEC_BACKEND
static pid_t syslogger_forkexec(void);
#endif

static char* logfile_getname(pg_time_t timestamp);
static bool logfile_rotate(void);

FILE *realStdErr = NULL;
FILE *syslogFile = NULL;
int syslogPipe[2] = {0, 0};



/*
 * Main entry point for syslogger process
 * argc/argv parameters are valid only in EXEC_BACKEND case.
 */
void
SysLoggerMain(int argc, char *argv[])
{
#ifdef EXEC_BACKEND

    int fd;
    Assert(argc == 8);

    argv += 3;
    StrNCpy(postgres_exec_path,    *argv++, MAXPGPATH);
    syslogPipe[0] = atoi(*argv++);
    syslogPipe[1] = atoi(*argv++);
    fd = atoi(*argv++);
    if (fd != 0)
        syslogFile = fdopen(fd, "a+");
    fd = atoi(*argv++);
    if (fd != 0)
        realStdErr = fdopen(fd, "a+");

#endif

    IsUnderPostmaster = true;
    MyProcPid = getpid();
    init_ps_display("system logger process", "", "");
    set_ps_display("");

    /*
     * Properly accept or ignore signals the postmaster might send us
     *
     * Note: we ignore all termination signals, and wait for the postmaster
     * to die to catch as much pipe output as possible.
     */

    pqsignal(SIGHUP, sigHupHandler);    /* set flag to read config file */
    pqsignal(SIGINT,  SIG_IGN);
    pqsignal(SIGTERM, SIG_IGN);
    pqsignal(SIGQUIT, SIG_IGN);
    pqsignal(SIGALRM, SIG_IGN);
    pqsignal(SIGPIPE, SIG_IGN);
    pqsignal(SIGUSR1, rotationHandler);  /* request log rotation */
    pqsignal(SIGUSR2, SIG_IGN);

    /*
     * Reset some signals that are accepted by postmaster but not here
     */
    pqsignal(SIGCHLD, SIG_DFL);
    pqsignal(SIGTTIN, SIG_DFL);
    pqsignal(SIGTTOU, SIG_DFL);
    pqsignal(SIGCONT, SIG_DFL);
    pqsignal(SIGWINCH, SIG_DFL);

    PG_SETMASK(&UnBlockSig);

    /*
     * if we restarted, our stderr is redirected.
     * Direct it back to system stderr.
     */
    if (realStdErr != NULL)
    {
        if (dup2(fileno(realStdErr), fileno(stderr)) < 0)
        {
            char *errstr = strerror(errno);
            /*
             * Now we have a real problem: we can't redirect to stderr,
             * and can't ereport it correctly (it would go into our queue
             * which will never be read
             * We're writing everywhere, hoping to leave at least some
             * hint of what happened.
             */

            fprintf(realStdErr, "PANIC: Syslogger couldn't redirect its stderr to the saved stderr: %s\n", errstr);
            fprintf(stderr, "PANIC: Syslogger couldn't redirect its stderr to the saved stderr: %s\n", errstr);

            ereport(PANIC,
                    (errcode_for_file_access(),
                     (errmsg("Syslogger couldn't redirect its stderr to the saved stderr: %s", errstr))));
            exit(1);
        }

        realStdErr = NULL;
    }

    /* we'll never write that pipe */
    close(syslogPipe[1]);
    syslogPipe[1] = 0;
    /* remember age of initial logfile */
    last_rotation_time = time(NULL);

    strncpy(currentLogDir, Log_directory, MAXPGPATH);
    /* main worker loop */
    for (;;)
    {
        pg_time_t    now;
        int            elapsed_secs;
        char        logbuffer[1024];
        char        bytesRead;
        int         rc;
        fd_set        rfds;
        struct timeval timeout;

        if (got_SIGHUP)
        {
            got_SIGHUP = false;
            ProcessConfigFile(PGC_SIGHUP);

            /*
             * check if the log directory changed
             * in postgresql.conf. If so, we rotate to make sure
             * we're writing the logfiles where the backends
             * expect us to do so.
             */
            if (strncmp(Log_directory, currentLogDir, MAXPGPATH))
            {
                rotation_requested = true;
                strncpy(currentLogDir, Log_directory, MAXPGPATH);
            }
        }

        if (!rotation_requested && last_rotation_time != 0 && Log_RotationAge > 0)
        {
            /*
             * Do an unforced rotation if too much time has elapsed
             * since the last one.
             */
            now = time(NULL);
            elapsed_secs = now - last_rotation_time;
            if (elapsed_secs >= Log_RotationAge * 60)
                rotation_requested = true;
        }

        if (!rotation_requested && Log_RotationSize > 0)
        {
            /*
             * Do an unforced rotation if file is too big
             */
            if (ftell(syslogFile) >= Log_RotationSize * 1024)
                rotation_requested = true;
        }


        if (rotation_requested)
        {
            if (!logfile_rotate())
            {
                ereport(ERROR,
                        (errcode_for_file_access(),
                         (errmsg("logfile rotation failed, disabling auto rotation (SIGHUP to reenable): %m"))));

                Log_RotationAge = 0;
                Log_RotationSize = 0;
            }
            rotation_requested = false;
        }

        FD_ZERO(&rfds);
        FD_SET(syslogPipe[0], &rfds);
        timeout.tv_sec=1;
        timeout.tv_usec=0;

        /*
         * Check if data is present
         */
        rc = select(syslogPipe[0]+1, &rfds, NULL, NULL, &timeout);
        PG_SETMASK(&UnBlockSig);

        if (rc < 0 && errno != EINTR)
        {
            ereport(COMMERROR,
                    (errcode_for_socket_access(),
                     errmsg("select() failed in system logger: %m")));
            exit(1);
        }

        if (rc > 0 && FD_ISSET(syslogPipe[0], &rfds))
        {
            bytesRead = piperead(syslogPipe[0],
                                 logbuffer, sizeof(logbuffer));

            if (bytesRead < 0 && errno != EINTR)
            {
                /*
                 * EMFILE has been observed when a backend terminated
                 * after a SSL client connection broke down.
                 * There might be others we should ignore.
                 */
                if (errno == EMFILE)
                    continue;

                ereport(COMMERROR,
                        (errcode_for_socket_access(),
                         errmsg("could not read from system logger pipe: %m")));
                exit(1);
            }
            else if (bytesRead > 0)
            {
                pipeRetry = 0;

                if (fwrite(logbuffer, 1, bytesRead, syslogFile) < 1)
                {
                    ereport(COMMERROR,
                            (errcode_for_file_access(),
                             errmsg("fwrite to logfile failed in system logger: %m")));
                    exit(1);
                }
                fflush(syslogFile);

                if (Log_destination & LOG_DESTINATION_STDERR)
                {
                    if (realStdErr)
                    {
                        fwrite(logbuffer, 1, bytesRead, realStdErr);
                        fflush(realStdErr);
                    }
                    else
                    {
                        fwrite(logbuffer, 1, bytesRead, stderr);
                        fflush(stderr);
                    }
                }
                continue;
            }
        }

        /*
         * If postmaster died, there's nothing to log any more.
         * We check this only after pipe timeouts to receive as much as possible
         * from the pipe.
         */
        if (!PostmasterIsAlive(true))
        {
            if (syslogFile)
                fclose(syslogFile);
            exit(1);
        }
    }
}



void
SysLogger_SIGHUP(void)
{
#ifdef WIN32
    got_SIGHUP = true;
#else
    kill(SysLoggerPID, SIGHUP);
#endif
}


void
SysLogger_SIGUSR1(void)
{
#ifdef WIN32
    rotation_requested = true;
#else
    kill(SysLoggerPID, SIGUSR1);
#endif
}

int
SysLogger_Start(void)
{
#ifndef WIN32
    pid_t sysloggerPid;
    pg_time_t now;
    char *filename;

    if (!(Log_destination & LOG_DESTINATION_FILE))
        return 0;

    /* create the pipe which will receive stderr output */
    if (!syslogPipe[0])
    {
        if (pgpipe(syslogPipe) < 0)
            ereport(FATAL,
                    (errcode_for_file_access(),
                     (errmsg("pipe for syslogging not created: %m"))));

        if (!set_noblock(syslogPipe[1]))
        {
            ereport(FATAL,
                    (errcode_for_socket_access(),
                     errmsg("could not set syslogging pipe to nonblocking mode: %m")));
        }
    }

    now = time(NULL);

    /*
     * The initial logfile is created right in the postmaster,
     * to insure that the logger process has a writable file.
     */
    filename = logfile_getname(now);

    /*
     * The file is opened for appending, in case the syslogger
     * is restarted right after a rotation.
     */
    syslogFile = fopen(filename, "a+");

    if (!syslogFile)
    {
        /*
         * if we can't open the syslog file for the syslogger process,
         * we try to redirect stderr back to have some logging.
         */
        ereport(WARNING,
                (errcode_for_file_access(),
                 (errmsg("error opening syslog file %s: %m", filename))));

        if (realStdErr != NULL)
        {
            if (dup2(fileno(realStdErr), fileno(stderr)) < 0)
                ereport(FATAL,
                        (errcode_for_file_access(),
                         (errmsg("error redirecting stderr to default: %m"))));

            ereport(FATAL,
                    (errmsg("logfile output corrupted")));
        }

    }
    pfree(filename);

    fflush(stdout);
    fflush(stderr);

#ifdef __BEOS__
    /* Specific beos actions before backend startup */
    beos_before_backend_startup();
#endif


#ifdef EXEC_BACKEND
    switch ((sysloggerPid = syslogger_forkexec()))
#else
    switch ((sysloggerPid = fork()))
#endif
    {
        case -1:
#ifdef __BEOS__
            /* Specific beos actions */
            beos_backend_startup_failed();
#endif
            ereport(LOG,
                    (errmsg("could not fork system logger: %m")));
            return 0;

#ifndef EXEC_BACKEND
        case 0:
            /* in postmaster child ... */
#ifdef __BEOS__
            /* Specific beos actions after backend startup */
            beos_backend_startup();
#endif
            /* Close the postmaster's sockets */
            ClosePostmasterPorts();

            /* Drop our connection to postmaster's shared memory, as well */
            PGSharedMemoryDetach();

            /* do the work */
            SysLoggerMain(0, NULL);
            break;
#endif

        default:
            /* now we redirect stderr, if not done already */
            if (realStdErr == NULL)
            {
                int dh= dup(fileno(stderr));

                if (dh < 0)
                    ereport(FATAL,
                            (errcode_for_file_access(),
                             (errmsg("stderr duplication failed: %m"))));

                realStdErr = fdopen(dh, "a");
                if (realStdErr == NULL)
                    ereport(FATAL,
                            (errcode_for_file_access(),
                             (errmsg("realstderr reopen failed: %m"))));

                if (dup2(syslogPipe[1], fileno(stdout)) < 0)
                    ereport(FATAL,
                            (errcode_for_file_access(),
                          (errmsg("stdout pipe redirection failed: %m"))));

                if (dup2(syslogPipe[1], fileno(stderr)) < 0)
                    ereport(FATAL,
                            (errcode_for_file_access(),
                          (errmsg("stderr pipe redirection failed: %m"))));
            }

            /* postmaster will never write the file; close it */
            fclose(syslogFile);
            syslogFile = NULL;

            return (int) sysloggerPid;
    }
#endif
    /* we should never reach here */
    return 0;
}


#ifdef EXEC_BACKEND
static pid_t
syslogger_forkexec()
{
    char *av[10];
    int ac = 0, bufc = 0, i;
    char numbuf[2][32];

    av[ac++] = "postgres";
    av[ac++] = "-forklog";
    av[ac++] = NULL;            /* filled in by postmaster_forkexec */

    /* postgres_exec_path is not passed by write_backend_variables */
    av[ac++] = postgres_exec_path;

    /* Pipe file ids (those not passed by write_backend_variables) */
    snprintf(numbuf[bufc++],32,"%d", syslogPipe[0]);
    snprintf(numbuf[bufc++],32,"%d", syslogPipe[1]);
    if (syslogFile != 0)
        snprintf(numbuf[bufc++],32,"%d", fileno(syslogFile));
    else
        strcpy(numbuf[bufc++], "0");
    if (realStdErr != 0)
        snprintf(numbuf[bufc++],32,"%d", fileno(realStdErr));
    else
        strcpy(numbuf[bufc++], "0");

    /* Add to the arg list */
    Assert(bufc <= lengthof(pgstatBuf));
    for (i = 0; i < bufc; i++)
        av[ac++] = numbuf[i];

    av[ac] = NULL;
    Assert(ac < lengthof(av));

    return postmaster_forkexec(ac, av);
}
#endif

/* --------------------------------
 *        logfile routines
 * --------------------------------
 */


/*
 * perform rotation
 */
bool
logfile_rotate(void)
{
    char *filename;
    pg_time_t now;
    FILE *fh;

    now = time(NULL);
    filename = logfile_getname(now);

    fh = fopen(filename, "a+");
    if (!fh)
    {
        /*
         * if opening the new file fails, the caller is responsible
         * for taking consequences. */

        pfree(filename);
        return false;
    }

    fclose(syslogFile);
    syslogFile = fh;

    last_rotation_time = now;

    /* official opening of the new logfile */
    ereport(NOTICE,
            (errcode(ERRCODE_WARNING),
             errmsg("Opened new log file %s", filename)));

    pfree(filename);
    return true;
}



/*
 * creates logfile name using timestamp information
 */

#define TIMESTAMPPATTERN "%Y-%m-%d_%H%M%S"

static char*
logfile_getname(pg_time_t timestamp)
{
    char *filetemplate;
    char *filename;


    if (is_absolute_path(Log_directory))
    {
      filetemplate = palloc(strlen(Log_directory)
                            + strlen(Log_filename_prefix)
                            + sizeof(TIMESTAMPPATTERN)+10 +2);
        if (filetemplate)
            sprintf(filetemplate, "%s/%s%s_%05lu.log",
                    Log_directory, Log_filename_prefix,
                    TIMESTAMPPATTERN, (unsigned long)PostmasterPid);
    }
    else
    {
        filetemplate = palloc(strlen(DataDir) + strlen(Log_directory)
                              + strlen(Log_filename_prefix)
                              + sizeof(TIMESTAMPPATTERN) +10 +3);
        if (filetemplate)
            sprintf(filetemplate, "%s/%s/%s%s_%05lu.log",
                    DataDir, Log_directory, Log_filename_prefix,
                    TIMESTAMPPATTERN, (unsigned long)PostmasterPid);
    }
    filename = palloc(MAXPGPATH);

    if (!filename || !filetemplate)
        ereport(FATAL,
                (errcode(ERRCODE_OUT_OF_MEMORY),
                 errmsg("Out of memory")));

    pg_strftime(filename, MAXPGPATH, filetemplate, pg_localtime(×tamp));

    pfree(filetemplate);

    return filename;
}

/* --------------------------------
 *        API helper routines
 * --------------------------------
 */

/*
 * Rotate log file
 */
bool
LogFileRotate(void)
{
    if (!(Log_destination & LOG_DESTINATION_FILE))
    {
        ereport(NOTICE,
                (errcode(ERRCODE_WARNING),
                 errmsg("no logfile configured; rotation not supported")));
        return false;
    }

    SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);

    return true;
}

/* --------------------------------
 *        signal handler routines
 * --------------------------------
 */

/* SIGHUP: set flag to reload config file */
static void
sigHupHandler(SIGNAL_ARGS)
{
    got_SIGHUP = true;
}

/* SIGUSR1: set flag to rotate logfile */
static void
rotationHandler(SIGNAL_ARGS)
{
    rotation_requested = true;
}
/*-------------------------------------------------------------------------
 *
 * syslogger.h
 *      Exports from postmaster/syslogger.c.
 *
 * Portions Copyright (c) 2004, PostgreSQL Global Development Group
 *
 * $PostgreSQL: $
 *
 *-------------------------------------------------------------------------
 */
#ifndef _SYSLOGGER_H
#define _SYSLOGGER_H

#include "pgtime.h"

/* GUC options */
extern int        Log_RotationAge;
extern int        Log_RotationSize;
extern char *   Log_directory;
extern char *   Log_filename_prefix;


int SysLogger_Start(void);
void SysLoggerMain(int argc, char *argv[]);
void SysLogger_SIGHUP(void);
void SysLogger_SIGUSR1(void);

extern bool LogFileRotate(void);

#endif   /* _SYSLOGGER_H */