]> granicus.if.org Git - postgresql/commitdiff
Create a built-in log rotation program, so that we no longer have to
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 5 Aug 2004 23:32:13 +0000 (23:32 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 5 Aug 2004 23:32:13 +0000 (23:32 +0000)
recommend that people go get Apache's rotatelogs program.  Additional
benefits are that configuration is done through GUC, rather than
externally, and that the postmaster can monitor the log rotator and
restart it after failure (though we certainly hope that won't happen
often).
Andreas Pflug, some rework by Tom Lane.

13 files changed:
doc/src/sgml/maintenance.sgml
doc/src/sgml/runtime.sgml
src/backend/postmaster/Makefile
src/backend/postmaster/pgarch.c
src/backend/postmaster/pgstat.c
src/backend/postmaster/postmaster.c
src/backend/postmaster/syslogger.c [new file with mode: 0644]
src/backend/utils/error/elog.c
src/backend/utils/misc/guc.c
src/backend/utils/misc/postgresql.conf.sample
src/include/postmaster/postmaster.h
src/include/postmaster/syslogger.h [new file with mode: 0644]
src/include/utils/elog.h

index 91ead3dc1681064b49dd4a148efe8c314e74bfed..7d1dafad3182b74dc9f307746d5ead34258b050c 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/maintenance.sgml,v 1.36 2004/07/24 19:51:22 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/maintenance.sgml,v 1.37 2004/08/05 23:32:10 tgl Exp $
 -->
 
 <chapter id="maintenance">
@@ -445,22 +445,52 @@ VACUUM
   </para>
 
   <para>
-   If you simply direct the <systemitem>stderr</> of the <command>postmaster</command> into a
-   file, the only way to truncate the log file is to stop and restart
+   If you simply direct the <systemitem>stderr</> of the
+   <command>postmaster</command> into a
+   file, you will have log output, but
+   the only way to truncate the log file is to stop and restart
    the <command>postmaster</command>. This may be OK if you are using
    <productname>PostgreSQL</productname> in a development environment,
    but few production servers would find this behavior acceptable.
   </para>
 
   <para>
-   The simplest production-grade approach to managing log output is to
+   A better approach is to send the <command>postmaster</>'s
+   <systemitem>stderr</> output to some type of log rotation program.
+   There is a built-in log rotation program, which you can use by
+   setting the configuration parameter <literal>redirect_stderr</> to
+   <literal>true</> in <filename>postgresql.conf</>.  The control
+   parameters for this program are described in <xref
+   linkend="runtime-config-logging-where">.
+  </para>
+
+  <para>
+   Alternatively, you might prefer to use an external log rotation
+   program, if you have one that you are already using with other
+   server software. For example, the <application>rotatelogs</application>
+   tool included in the <productname>Apache</productname> distribution
+   can be used with <productname>PostgreSQL</productname>.  To do this,
+   just pipe the <command>postmaster</>'s
+   <systemitem>stderr</> output to the desired program.
+   If you start the server with
+   <command>pg_ctl</>, then <systemitem>stderr</>
+   is already redirected to <systemitem>stdout</>, so you just need a
+   pipe command:
+
+<programlisting>
+pg_ctl start | rotatelogs /var/log/pgsql_log 86400
+</programlisting>
+  </para>
+
+  <para>
+   Another production-grade approach to managing log output is to
    send it all to <application>syslog</> and let
    <application>syslog</> deal with file rotation. To do this, set the
-   configuration parameter <literal>log_destination</> to 'syslog' (to log to
-   <application>syslog</> only) in <filename>postgresql.conf</>. Then
-   you can send a <literal>SIGHUP</literal> signal to the
-   <application>syslog</> daemon whenever you want to force it to
-   start writing a new log file.  If you want to automate log
+   configuration parameter <literal>log_destination</> to <literal>syslog</>
+   (to log to <application>syslog</> only) in
+   <filename>postgresql.conf</>. Then you can send a <literal>SIGHUP</literal>
+   signal to the <application>syslog</> daemon whenever you want to force it
+   to start writing a new log file.  If you want to automate log
    rotation, the <application>logrotate</application> program can be
    configured to work with log files from
    <application>syslog</application>.
@@ -471,27 +501,15 @@ VACUUM
    particularly with large log messages; it may truncate or drop messages
    just when you need them the most.  Also, on <productname>linux</>,
    <application>syslog</> will sync each message to disk, yielding poor
-   performance.  Use a <literal>-</> at the start of the file name
-   in the <application>syslog</> config file to disable this behavior.
+   performance.  (You can use a <literal>-</> at the start of the file name
+   in the <application>syslog</> config file to disable this behavior.)
   </para>
 
   <para>
-   You may find it more useful to pipe the
-   <systemitem>stderr</> of the <command>postmaster</> to some type of
-   log rotation program. If you start the server with
-   <command>pg_ctl</>, then the <systemitem>stderr</> of the <command>postmaster</command>
-   is already redirected to <systemitem>stdout</>, so you just need a
-   pipe command:
-
-<programlisting>
-pg_ctl start | rotatelogs /var/log/pgsql_log 86400
-</programlisting>
-
-   The <productname>PostgreSQL</> distribution doesn't include a
-   suitable log rotation program, but there are many available on the
-   Internet. For example, the <application>rotatelogs</application>
-   tool included in the <productname>Apache</productname> distribution
-   can be used with <productname>PostgreSQL</productname>.
+   Note that all the solutions described above take care of starting new
+   log files at configurable intervals, but they do not handle deletion
+   of old, no-longer-interesting log files.  You will also want to set
+   up a batch job to periodically delete old log files.
   </para>
  </sect1>
 </chapter>
index 95a8c23c8220c57e76290862a5fa334eac092630..9f569712b08b4bef0330523c90321744dc7ef67e 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.271 2004/08/03 23:42:59 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.272 2004/08/05 23:32:10 tgl Exp $
 -->
 
 <Chapter Id="runtime">
@@ -1800,15 +1800,91 @@ SET ENABLE_SEQSCAN TO OFF;
         option to a list of desired log destinations separated by
         commas. The default is to log to <systemitem>stderr</systemitem> 
         only.
+         This option can only be set at server start or in the
+         <filename>postgresql.conf</filename> configuration file.
        </para>
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-redirect-stderr" xreflabel="redirect_stderr">
+      <term><varname>redirect_stderr</varname> (<type>boolean</type>)</term>
+       <listitem>
+        <para>
+         This option allows messages sent to <application>stderr</> to be
+         captured and redirected into log files.
+         This option, in combination with logging to <application>stderr</>,
+         is often more useful than
+         logging to <application>syslog</>, since some types of messages
+         may not appear in <application>syslog</> output (a common example
+         is dynamic-linker failure messages).
+         This option can only be set at server start.
+        </para>
+       </listitem>
+     </varlistentry>
+
+     <varlistentry id="guc-log-directory" xreflabel="log_directory">
+      <term><varname>log_directory</varname> (<type>string</type>)</term>
+       <listitem>
+        <para>
+         When <varname>redirect_stderr</> is enabled, this option
+          determines the directory in which log files will be created.
+         It may be specified as an absolute path, or relative to the
+         cluster data directory.
+         This option can only be set at server start or in the
+         <filename>postgresql.conf</filename> configuration file.
+        </para>
+       </listitem>
+     </varlistentry>
+
+     <varlistentry id="guc-log-filename-prefix" xreflabel="log_filename_prefix">
+      <term><varname>log_filename_prefix</varname> (<type>string</type>)</term>
+       <listitem>
+        <para>
+         When <varname>redirect_stderr</> is enabled, this option
+          sets the prefix of the file names of the created log files.
+         The postmaster PID and the current time are appended to this
+         prefix to form an exact log file name.
+         This option can only be set at server start or in the
+         <filename>postgresql.conf</filename> configuration file.
+        </para>
+       </listitem>
+     </varlistentry>
+
+     <varlistentry id="guc-log-rotation-age" xreflabel="log_rotation_age">
+      <term><varname>log_rotation_age</varname> (<type>integer</type>)</term>
+       <listitem>
+        <para>
+         When <varname>redirect_stderr</> is enabled, this option
+         determines the maximum lifetime of an individual log file.
+         After this many minutes have elapsed, a new log file will
+         be created.  Set to zero to disable time-based creation of
+         new log files.
+         This option can only be set at server start or in the
+         <filename>postgresql.conf</filename> configuration file.
+        </para>
+       </listitem>
+     </varlistentry>
+
+     <varlistentry id="guc-log-rotation-size" xreflabel="log_rotation_size">
+      <term><varname>log_rotation_size</varname> (<type>integer</type>)</term>
+       <listitem>
+        <para>
+         When <varname>redirect_stderr</> is enabled, this option
+         determines the maximum size of an individual log file.
+         After this many kilobytes have been emitted into a log file,
+         a new log file will be created.  Set to zero to disable size-based
+         creation of new log files.
+         This option can only be set at server start or in the
+         <filename>postgresql.conf</filename> configuration file.
+        </para>
+       </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-syslog-facility" xreflabel="syslog_facility">
       <term><varname>syslog_facility</varname> (<type>string</type>)</term>
        <listitem>
         <para>
-          If logging to <application>syslog</> is enabled, this option
+          When logging to <application>syslog</> is enabled, this option
           determines the <application>syslog</application>
           <quote>facility</quote> to be used.  You may choose
           from <literal>LOCAL0</>, <literal>LOCAL1</>,
@@ -1826,7 +1902,7 @@ SET ENABLE_SEQSCAN TO OFF;
       <term><varname>syslog_ident</varname> (<type>string</type>)</term>
        <listitem>
         <para>
-         If logging to <application>syslog</> is enabled, this option
+         When logging to <application>syslog</> is enabled, this option
          determines the program name used to identify
          <productname>PostgreSQL</productname> messages in
          <application>syslog</application> logs. The default is
index abbac1674f1ec8a6a1f1200d1faf3637b182c319..fdb12febb4a4579a14246674361fcacfc467e569 100644 (file)
@@ -4,7 +4,7 @@
 #    Makefile for src/backend/postmaster
 #
 # IDENTIFICATION
-#    $PostgreSQL: pgsql/src/backend/postmaster/Makefile,v 1.18 2004/07/21 20:34:46 momjian Exp $
+#    $PostgreSQL: pgsql/src/backend/postmaster/Makefile,v 1.19 2004/08/05 23:32:10 tgl Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -12,7 +12,7 @@ subdir = src/backend/postmaster
 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 960ece7583199f56e915a4f065d67f6f303cf2a0..1d398ba596212f688deb621de0a0f314f1b65924 100644 (file)
@@ -19,7 +19,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/postmaster/pgarch.c,v 1.4 2004/08/03 20:32:33 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/postmaster/pgarch.c,v 1.5 2004/08/05 23:32:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -172,7 +172,7 @@ pgarch_start(void)
                        beos_backend_startup();
 #endif
                        /* Close the postmaster's sockets */
-                       ClosePostmasterPorts();
+                       ClosePostmasterPorts(false);
 
                        /* Drop our connection to postmaster's shared memory, as well */
                        PGSharedMemoryDetach();
index dbd4f15cefdfd5cbcfeef6b1691a11b39475e393..7638dd28a1130e6516bc6a4870d7fef27cd2d088 100644 (file)
@@ -13,7 +13,7 @@
  *
  *     Copyright (c) 2001-2003, PostgreSQL Global Development Group
  *
- *     $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.77 2004/07/01 00:50:36 tgl Exp $
+ *     $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.78 2004/08/05 23:32:10 tgl Exp $
  * ----------
  */
 #include "postgres.h"
@@ -611,7 +611,7 @@ pgstat_start(void)
                        beos_backend_startup();
 #endif
                        /* Close the postmaster's sockets */
-                       ClosePostmasterPorts();
+                       ClosePostmasterPorts(false);
 
                        /* Drop our connection to postmaster's shared memory, as well */
                        PGSharedMemoryDetach();
index 6c9e87f5f3a56baedb7c63ffea3f3b6355581f78..870ad318a830bb26349f6c7cb9bff8369d0cc87f 100644 (file)
@@ -37,7 +37,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.419 2004/08/04 20:09:47 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.420 2004/08/05 23:32:10 tgl Exp $
  *
  * NOTES
  *
 #include "nodes/nodes.h"
 #include "postmaster/postmaster.h"
 #include "postmaster/pgarch.h"
+#include "postmaster/syslogger.h"
 #include "storage/fd.h"
 #include "storage/ipc.h"
 #include "storage/pg_shmem.h"
@@ -199,7 +200,8 @@ char           *preload_libraries_string = NULL;
 static pid_t StartupPID = 0,
                        BgWriterPID = 0,
                        PgArchPID = 0,
-                       PgStatPID = 0;
+                       PgStatPID = 0,
+                       SysLoggerPID = 0;
 
 /* Startup/shutdown state */
 #define                        NoShutdown              0
@@ -828,7 +830,7 @@ PostmasterMain(int argc, char *argv[])
         * CAUTION: when changing this list, check for side-effects on the signal
         * handling setup of child processes.  See tcop/postgres.c,
         * bootstrap/bootstrap.c, postmaster/bgwriter.c, postmaster/pgarch.c,
-        * and postmaster/pgstat.c.
+        * postmaster/pgstat.c, and postmaster/syslogger.c.
         */
        pqinitmask();
        PG_SETMASK(&BlockSig);
@@ -850,6 +852,11 @@ PostmasterMain(int argc, char *argv[])
        pqsignal(SIGXFSZ, SIG_IGN); /* ignored */
 #endif
 
+       /*
+        * If enabled, start up syslogger collection subprocess
+        */
+       SysLoggerPID = SysLogger_Start();
+
        /*
         * Reset whereToSendOutput from Debug (its starting state) to None.
         * This stops ereport from sending log messages to stderr unless
@@ -933,8 +940,8 @@ checkDataDir(const char *checkdir)
        {
                write_stderr("%s does not know where to find the database system data.\n"
                                         "You must specify the directory that contains the database system\n"
-                                        "or configuration files by either specifying the -D invocation option\n"
-                                        "or by setting the PGDATA environment variable.\n",
+                                        "either by specifying the -D invocation option or by setting the\n"
+                                        "PGDATA environment variable.\n",
                                         progname);
                ExitPostmaster(2);
        }
@@ -944,12 +951,12 @@ checkDataDir(const char *checkdir)
                if (errno == ENOENT)
                        ereport(FATAL,
                                        (errcode_for_file_access(),
-                                        errmsg("data or configuration location \"%s\" does not exist",
+                                        errmsg("data directory \"%s\" does not exist",
                                                        checkdir)));
                else
                        ereport(FATAL,
                                        (errcode_for_file_access(),
-                        errmsg("could not read permissions of \"%s\": %m",
+                        errmsg("could not read permissions of directory \"%s\": %m",
                                        checkdir)));
        }
 
@@ -1050,7 +1057,7 @@ pmdaemonize(void)
                ExitPostmaster(1);
        }
 #endif
-       i = open(NULL_DEV, O_RDWR | PG_BINARY);
+       i = open(NULL_DEV, O_RDWR);
        dup2(i, 0);
        dup2(i, 1);
        dup2(i, 2);
@@ -1207,6 +1214,10 @@ ServerLoop(void)
                        }
                }
 
+               /* If we have lost the system logger, try to start a new one */
+               if (SysLoggerPID == 0 && Redirect_stderr)
+                       SysLoggerPID = SysLogger_Start();
+
                /*
                 * If no background writer process is running, and we are not in
                 * a state that prevents it, start one.  It doesn't matter if this
@@ -1714,9 +1725,12 @@ ConnFree(Port *conn)
  * This is called during child process startup to release file descriptors
  * that are not needed by that child process.  The postmaster still has
  * them open, of course.
+ *
+ * Note: we pass am_syslogger as a boolean because we don't want to set
+ * the global variable yet when this is called.
  */
 void
-ClosePostmasterPorts(void)
+ClosePostmasterPorts(bool am_syslogger)
 {
        int                     i;
 
@@ -1729,6 +1743,20 @@ ClosePostmasterPorts(void)
                        ListenSocket[i] = -1;
                }
        }
+
+       /* If using syslogger, close the read side of the pipe */
+       if (!am_syslogger)
+       {
+#ifndef WIN32
+               if (syslogPipe[0] >= 0)
+                       close(syslogPipe[0]);
+               syslogPipe[0] = -1;
+#else
+               if (syslogPipe[0])
+                       CloseHandle(syslogPipe[0]);
+               syslogPipe[0] = 0;
+#endif
+       }
 }
 
 
@@ -1770,6 +1798,8 @@ SIGHUP_handler(SIGNAL_ARGS)
                        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();
@@ -2063,6 +2093,18 @@ reaper(SIGNAL_ARGS)
                        continue;
                }
 
+               /* Was it the system logger? try to start a new one */
+               if (SysLoggerPID != 0 && pid == SysLoggerPID)
+               {
+                       SysLoggerPID = 0;
+                       /* for safety's sake, launch new logger *first* */
+                       SysLoggerPID = SysLogger_Start();
+                       if (exitstatus != 0)
+                               LogChildExit(LOG, gettext("system logger process"),
+                                                        pid, exitstatus);
+                       continue;
+               }
+
                /*
                 * Else do standard backend child cleanup.
                 */
@@ -2258,6 +2300,8 @@ HandleChildCrash(int pid, int exitstatus, const char *procname)
                kill(PgStatPID, SIGQUIT);
        }
 
+       /* We do NOT restart the syslogger */
+
        FatalError = true;
 }
 
@@ -2528,7 +2572,7 @@ BackendRun(Port *port)
         * Let's clean up ourselves as the postmaster child, and close the
         * postmaster's listen sockets
         */
-       ClosePostmasterPorts();
+       ClosePostmasterPorts(false);
 
        /* We don't want the postmaster's proc_exit() handlers */
        on_exit_reset();
@@ -2921,7 +2965,7 @@ SubPostmasterMain(int argc, char *argv[])
        if (strcmp(argv[1], "-forkboot") == 0)
        {
                /* Close the postmaster's sockets */
-               ClosePostmasterPorts();
+               ClosePostmasterPorts(false);
 
                /* Attach process to shared segments */
                CreateSharedMemoryAndSemaphores(false, MaxBackends, 0);
@@ -2932,7 +2976,7 @@ SubPostmasterMain(int argc, char *argv[])
        if (strcmp(argv[1], "-forkarch") == 0)
        {
                /* Close the postmaster's sockets */
-               ClosePostmasterPorts();
+               ClosePostmasterPorts(false);
 
                /* Do not want to attach to shared memory */
 
@@ -2942,7 +2986,7 @@ SubPostmasterMain(int argc, char *argv[])
        if (strcmp(argv[1], "-forkbuf") == 0)
        {
                /* Close the postmaster's sockets */
-               ClosePostmasterPorts();
+               ClosePostmasterPorts(false);
 
                /* Do not want to attach to shared memory */
 
@@ -2961,6 +3005,16 @@ SubPostmasterMain(int argc, char *argv[])
                PgstatCollectorMain(argc, argv);
                proc_exit(0);
        }
+       if (strcmp(argv[1], "-forklog") == 0)
+       {
+               /* Close the postmaster's sockets */
+               ClosePostmasterPorts(true);
+
+               /* Do not want to attach to shared memory */
+
+               SysLoggerMain(argc, argv);
+               proc_exit(0);
+       }
 
        return 1;                                       /* shouldn't get here */
 }
@@ -3017,7 +3071,7 @@ sigusr1_handler(SIGNAL_ARGS)
                if (Shutdown <= SmartShutdown)
                        SignalChildren(SIGUSR1);
        }
+
        if (PgArchPID != 0 && Shutdown == NoShutdown)
        {
                if (CheckPostmasterSignal(PMSIGNAL_WAKEN_ARCHIVER))
@@ -3214,7 +3268,7 @@ StartChildProcess(int xlop)
                IsUnderPostmaster = true;       /* we are a postmaster subprocess now */
 
                /* Close the postmaster's sockets */
-               ClosePostmasterPorts();
+               ClosePostmasterPorts(false);
 
                /* Lose the postmaster's on-exit routines and port connections */
                on_exit_reset();
@@ -3400,6 +3454,9 @@ write_backend_variables(char *filename, Port *port)
        write_var(PostmasterHandle, fp);
 #endif
 
+       write_var(syslogPipe[0], fp);
+       write_var(syslogPipe[1], fp);
+
        StrNCpy(str_buf, my_exec_path, MAXPGPATH);
        write_array_var(str_buf, fp);
 
@@ -3471,6 +3528,9 @@ read_backend_variables(char *filename, Port *port)
        read_var(PostmasterHandle, fp);
 #endif
 
+       read_var(syslogPipe[0], fp);
+       read_var(syslogPipe[1], fp);
+
        read_array_var(str_buf, fp);
        StrNCpy(my_exec_path, str_buf, MAXPGPATH);
 
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
new file mode 100644 (file)
index 0000000..f5bffdb
--- /dev/null
@@ -0,0 +1,748 @@
+/*-------------------------------------------------------------------------
+ *
+ * syslogger.c
+ *
+ * The system logger (syslogger) is new in Postgres 8.0. It catches all
+ * stderr output from the postmaster, backends, and other subprocesses
+ * by redirecting to a pipe, and writes it to a set of logfiles.
+ * 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: pgsql/src/backend/postmaster/syslogger.c,v 1.1 2004/08/05 23:32:10 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <fcntl.h>
+#include <signal.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "libpq/pqsignal.h"
+#include "miscadmin.h"
+#include "postmaster/postmaster.h"
+#include "postmaster/syslogger.h"
+#include "pgtime.h"
+#include "storage/ipc.h"
+#include "storage/pg_shmem.h"
+#include "utils/guc.h"
+#include "utils/ps_status.h"
+
+
+/*
+ * GUC parameters.  Redirect_stderr cannot be changed after postmaster
+ * start, but the rest can change at SIGHUP.
+ */
+bool           Redirect_stderr = false;
+int                    Log_RotationAge = 24*60;
+int                    Log_RotationSize  = 10*1024;
+char *      Log_directory = "pg_log";
+char *      Log_filename_prefix = "postgresql-";
+
+/*
+ * Globally visible state (used by elog.c)
+ */
+bool am_syslogger = false;
+
+/*
+ * Private state
+ */
+static pg_time_t       last_rotation_time = 0;
+
+static bool    redirection_done = false;
+
+static bool    pipe_eof_seen = false;
+
+static FILE *syslogFile = NULL;
+
+/* 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
+
+/*
+ * Flags set by interrupt handlers for later service in the main loop.
+ */
+static volatile sig_atomic_t got_SIGHUP = false;
+
+
+/* Local subroutines */
+#ifdef EXEC_BACKEND
+static pid_t syslogger_forkexec(void);
+static void syslogger_parseArgs(int argc, char *argv[]);
+#endif
+#ifdef WIN32
+static unsigned int __stdcall pipeThread(void *arg);
+#endif
+static void logfile_rotate(void);
+static char* logfile_getname(pg_time_t timestamp);
+static void sigHupHandler(SIGNAL_ARGS);
+
+
+/*
+ * Main entry point for syslogger process
+ * argc/argv parameters are valid only in EXEC_BACKEND case.
+ */
+NON_EXEC_STATIC void
+SysLoggerMain(int argc, char *argv[])
+{
+       char         currentLogDir[MAXPGPATH];
+
+       IsUnderPostmaster = true;       /* we are a postmaster subprocess now */
+
+       MyProcPid = getpid();           /* reset MyProcPid */
+
+       /* Lose the postmaster's on-exit routines */
+       on_exit_reset();
+
+#ifdef EXEC_BACKEND
+       syslogger_parseArgs(argc, argv);
+#endif /* EXEC_BACKEND */
+
+       am_syslogger = true;
+
+       init_ps_display("logger process", "", "");
+       set_ps_display("");
+
+       /*
+        * If we restarted, our stderr is already redirected into our own
+        * input pipe.  This is of course pretty useless, not to mention that
+        * it interferes with detecting pipe EOF.  Point stderr to /dev/null.
+        * This 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     i = open(NULL_DEV, O_WRONLY);
+
+               dup2(i, fileno(stdout));
+               dup2(i, fileno(stderr));
+               close(i);
+       }
+
+       /*
+        * Also close our copy of the write end of the pipe.  This is needed
+        * to ensure we can detect pipe EOF correctly.  (But note that in the
+        * restart case, the postmaster already did this.)
+        */
+#ifndef WIN32
+       if (syslogPipe[1] >= 0)
+               close(syslogPipe[1]);
+       syslogPipe[1] = -1;
+#else
+       if (syslogPipe[1])
+               CloseHandle(syslogPipe[1]);
+       syslogPipe[1] = 0;
+#endif
+
+       /*
+        * Properly accept or ignore signals the postmaster might send us
+        *
+        * Note: we ignore all termination signals, and instead exit only when
+        * all upstream processes are gone, to ensure we don't miss any dying
+        * gasps of broken backends...
+        */
+
+       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, SIG_IGN);
+       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);
+
+#ifdef WIN32
+       /* Fire up separate data transfer thread */
+       InitializeCriticalSection(&sysfileSection);
+
+       {
+               unsigned int tid;
+
+               threadHandle = (HANDLE)_beginthreadex(0, 0, pipeThread, 0, 0, &tid);
+       }
+#endif  /* WIN32 */
+
+       /* remember age of initial logfile */
+       last_rotation_time = time(NULL);
+       /* remember active logfile directory */
+       strncpy(currentLogDir, Log_directory, MAXPGPATH);
+
+       /* main worker loop */
+       for (;;)
+       {
+               bool rotation_requested = false;
+#ifndef WIN32
+               char        logbuffer[1024];
+               int          bytesRead;
+               int         rc;
+               fd_set          rfds;
+               struct timeval timeout;
+#endif
+
+               if (got_SIGHUP)
+               {
+                       got_SIGHUP = false;
+                       ProcessConfigFile(PGC_SIGHUP);
+
+                       /*
+                        * Check if the log directory changed in postgresql.conf. If so,
+                        * force rotation to make sure we're writing the logfiles in the
+                        * right place.
+                        *
+                        * XXX is it worth responding similarly to a change of
+                        * Log_filename_prefix?
+                        */
+                       if (strncmp(Log_directory, currentLogDir, MAXPGPATH) != 0)
+                       {
+                               strncpy(currentLogDir, Log_directory, MAXPGPATH);
+                               rotation_requested = true;
+                       }
+               }
+
+               if (!rotation_requested &&
+                       last_rotation_time != 0 &&
+                       Log_RotationAge > 0)
+               {
+                       /*
+                        * Do a logfile rotation if too much time has elapsed
+                        * since the last one.
+                        */
+                       pg_time_t   now = time(NULL);
+                       int         elapsed_secs = now - last_rotation_time;
+
+                       if (elapsed_secs >= Log_RotationAge * 60)
+                               rotation_requested = true;
+               }
+
+               if (!rotation_requested && Log_RotationSize > 0)
+               {
+                       /*
+                        * Do a rotation if file is too big
+                        */
+                       if (ftell(syslogFile) >= Log_RotationSize * 1024L)
+                               rotation_requested = true;
+               }
+
+               if (rotation_requested)
+                       logfile_rotate();
+
+#ifndef WIN32
+               /*
+                * 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)
+               {
+                       if (errno != EINTR)
+                               ereport(LOG,
+                                               (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, sizeof(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)
+                       {
+                               write_syslogger_file(logbuffer, bytesRead);
+                               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;
+                       }
+               }
+#else /* WIN32 */
+               /*
+                * On Windows we leave it to a separate thread to transfer data and
+                * detect pipe EOF.  The main thread just wakes up once a second to
+                * check for SIGHUP and rotation conditions.
+                */
+               pgwin32_backend_usleep(1000000);
+#endif /* WIN32 */
+
+               if (pipe_eof_seen)
+               {
+                       ereport(LOG,
+                                       (errmsg("logger shutting down")));
+                       if (syslogFile)
+                               fclose(syslogFile);
+                       /* normal exit from the syslogger is here */
+                       proc_exit(0);
+               }
+       }
+}
+
+/*
+ * Postmaster subroutine to start a syslogger subprocess.
+ */
+int
+SysLogger_Start(void)
+{
+    pid_t sysloggerPid;
+       pg_time_t now;
+       char *filename;
+
+       if (!Redirect_stderr)
+           return 0;
+
+       /*
+        * If first time through, create the pipe which will receive stderr output.
+        *
+        * If the syslogger crashes and needs to be restarted, we continue to use
+        * the same pipe (indeed must do so, since extant backends will be writing
+        * into that pipe).
+        *
+        * This means the postmaster must continue to hold the read end of the
+        * 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 syslogging: %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 syslogging: %m"))));
+       }
+#endif
+
+       /*
+        * create log directory if not present; ignore errors
+        */
+       if (is_absolute_path(Log_directory))
+               mkdir(Log_directory, 0700);
+       else
+       {
+               filename = palloc(MAXPGPATH);
+               snprintf(filename, MAXPGPATH, "%s/%s", DataDir, Log_directory);
+               mkdir(filename, 0700);
+               pfree(filename);
+       }
+
+       /*
+        * The initial logfile is created right in the postmaster,
+        * to verify that the Log_directory is writable.
+        */
+       now = time(NULL);
+       filename = logfile_getname(now);
+
+       syslogFile = fopen(filename, "a");
+
+       if (!syslogFile)
+           ereport(FATAL,
+                               (errcode_for_file_access(),
+                                (errmsg("could not create logfile \"%s\": %m",
+                                                filename))));
+
+       setvbuf(syslogFile, NULL, _IOLBF, 0);
+
+       pfree(filename);
+
+       /*
+        * Now we can fork off the syslogger subprocess.
+        */
+       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(true);
+
+                       /* Drop our connection to postmaster's shared memory, as well */
+                       PGSharedMemoryDetach();
+
+                       /* do the work */
+                       SysLoggerMain(0, NULL);
+                       break;
+#endif
+
+               default:
+                       /* success, in postmaster */
+
+                       /* now we redirect stderr, if not done already */
+                       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
+                               fflush(stderr);
+                               if (dup2(_open_osfhandle((long)syslogPipe[1], _O_APPEND),
+                                                _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. */
+                               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;
+       }
+
+       /* we should never reach here */
+       return 0;
+}
+
+
+#ifdef EXEC_BACKEND
+
+/*
+ * syslogger_forkexec() -
+ *
+ * Format up the arglist for, then fork and exec, a syslogger process
+ */
+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";
+       av[ac++] = NULL;                        /* filled in by postmaster_forkexec */
+
+       /* static variables (those not passed by write_backend_variables) */
+#ifndef WIN32
+       if (syslogFile != NULL)
+           snprintf(numbuf[bufc++], 32, "%d", fileno(syslogFile));
+    else
+           strcpy(numbuf[bufc++], "-1");
+       snprintf(numbuf[bufc++], 32, "%d", (int) redirection_done);
+#else  /* WIN32 */
+       if (syslogFile != NULL)
+           snprintf(numbuf[bufc++], 32, "%ld",
+                                _get_osfhandle(_fileno(syslogFile)));
+    else
+           strcpy(numbuf[bufc++], "0");
+       snprintf(numbuf[bufc++], 32, "%d", (int) redirection_done);
+#endif /* WIN32 */
+
+       /* Add to the arg list */
+       Assert(bufc <= lengthof(numbuf));
+       for (i = 0; i < bufc; i++)
+               av[ac++] = numbuf[i];
+
+       av[ac] = NULL;
+       Assert(ac < lengthof(av));
+
+       return postmaster_forkexec(ac, av);
+}
+
+/*
+ * syslogger_parseArgs() -
+ *
+ * Extract data from the arglist for exec'ed syslogger process
+ */
+static void
+syslogger_parseArgs(int argc, char *argv[])
+{
+    int fd;
+
+       Assert(argc == 5);
+       argv += 3;
+
+#ifndef WIN32
+       fd = atoi(*argv++);
+       if (fd != -1)
+       {
+           syslogFile = fdopen(fd, "a");
+               setvbuf(syslogFile, NULL, _IOLBF, 0);
+       }
+       redirection_done = (bool) atoi(*argv++);
+#else  /* WIN32 */
+       fd = atoi(*argv++);
+       if (fd != 0)
+       {
+               fd = _open_osfhandle(fd, _O_APPEND);
+               if (fd != 0)
+               {
+                       syslogFile = fdopen(fd, "a");
+                       setvbuf(syslogFile, NULL, _IOLBF, 0);
+               }
+       }
+       redirection_done = (bool) atoi(*argv++);
+#endif /* WIN32 */
+}
+
+#endif /* EXEC_BACKEND */
+
+
+/* --------------------------------
+ *             logfile routines
+ * --------------------------------
+ */
+
+/*
+ * Write to the currently open logfile
+ *
+ * This is exported so that elog.c can call it when am_syslogger is true.
+ * This allows the syslogger process to record elog messages of its own,
+ * even though its stderr does not point at the syslog pipe.
+ */
+void
+write_syslogger_file(const char *buffer, int count)
+{
+    int rc;
+
+#ifndef WIN32
+    rc = fwrite(buffer, 1, count, syslogFile);
+#else
+    EnterCriticalSection(&sysfileSection);
+    rc = fwrite(buffer, 1, count, syslogFile);
+    LeaveCriticalSection(&sysfileSection);
+#endif
+
+    if (rc != count)
+        ereport(LOG,
+                               (errcode_for_file_access(),
+                                errmsg("could not write to logfile: %m")));
+}
+
+#ifdef WIN32
+
+/*
+ * Worker thread to transfer data from the pipe to the current logfile.
+ *
+ * We need this because on Windows, WaitForSingleObject does not work on
+ * unnamed pipes: it always reports "signaled", so the blocking ReadFile won't
+ * allow for SIGHUP; and select is for sockets only.
+ */
+static unsigned int __stdcall
+pipeThread(void *arg)
+{
+    DWORD bytesRead;
+    char    logbuffer[1024];
+
+    for (;;)
+    {
+        if (!ReadFile(syslogPipe[0], logbuffer, sizeof(logbuffer),
+                                         &bytesRead, 0))
+               {
+                       DWORD error = GetLastError();
+
+                       if (error == ERROR_HANDLE_EOF)
+                               break;
+                       ereport(LOG,
+                                       (errcode_for_file_access(),
+                                        errmsg("could not read from logger pipe: %m")));
+               }
+        else if (bytesRead > 0)
+            write_syslogger_file(logbuffer, bytesRead);
+    }
+
+       /* We exit the above loop only upon detecting pipe EOF */
+       pipe_eof_seen = true;
+    _endthread();
+    return 0;
+}
+
+#endif /* WIN32 */
+
+/*
+ * perform logfile rotation
+ */
+static void
+logfile_rotate(void)
+{
+       char *filename;
+       pg_time_t now;
+       FILE *fh;
+
+       now = time(NULL);
+       filename = logfile_getname(now);
+
+       fh = fopen(filename, "a");
+       if (!fh)
+       {
+               int     saveerrno = errno;
+
+               ereport(LOG,
+                               (errcode_for_file_access(),
+                                errmsg("could not open new logfile \"%s\": %m",
+                                               filename)));
+
+               /*
+                * ENFILE/EMFILE are not too surprising on a busy system; just keep
+                * using the old file till we manage to get a new one.  Otherwise,
+                * assume something's wrong with Log_directory and stop trying to
+                * create files.
+                */
+               if (saveerrno != ENFILE && saveerrno != EMFILE)
+               {
+                       ereport(LOG,
+                               (errmsg("disabling auto rotation (use SIGHUP to reenable)")));
+                       Log_RotationAge = 0;
+                       Log_RotationSize = 0;
+               }
+               pfree(filename);
+               return;
+       }
+
+       setvbuf(fh, NULL, _IOLBF, 0);
+
+       /* On Windows, need to interlock against data-transfer thread */
+#ifdef WIN32
+       EnterCriticalSection(&sysfileSection);
+#endif
+       fclose(syslogFile);
+       syslogFile = fh;
+#ifdef WIN32
+       LeaveCriticalSection(&sysfileSection);
+#endif
+
+       last_rotation_time = now;
+
+       pfree(filename);
+}
+
+
+/*
+ * construct logfile name using timestamp information
+ *
+ * Result is palloc'd.
+ */
+static char*
+logfile_getname(pg_time_t timestamp)
+{
+       char *filename;
+       char stamptext[128];
+
+       pg_strftime(stamptext, sizeof(stamptext), "%Y-%m-%d_%H%M%S",
+                               pg_localtime(&timestamp));
+
+       filename = palloc(MAXPGPATH);
+
+       if (is_absolute_path(Log_directory))
+               snprintf(filename, MAXPGPATH, "%s/%s%05u_%s.log",
+                                Log_directory, Log_filename_prefix,
+                                (unsigned int) PostmasterPid, stamptext);
+       else
+               snprintf(filename, MAXPGPATH, "%s/%s/%s%05u_%s.log",
+                                DataDir, Log_directory, Log_filename_prefix,
+                                (unsigned int) PostmasterPid, stamptext);
+
+       return filename;
+}
+
+/* --------------------------------
+ *             signal handler routines
+ * --------------------------------
+ */
+
+/* SIGHUP: set flag to reload config file */
+static void
+sigHupHandler(SIGNAL_ARGS)
+{
+    got_SIGHUP = true;
+}
index 340cca03cac63c635984010ceb8fe7a0552040f6..9723cf76f590121995fc0af8c476783e003802b2 100644 (file)
@@ -37,7 +37,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.145 2004/08/04 20:58:46 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/error/elog.c,v 1.146 2004/08/05 23:32:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -57,6 +57,7 @@
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
 #include "postmaster/postmaster.h"
+#include "postmaster/syslogger.h"
 #include "storage/ipc.h"
 #include "tcop/tcopprot.h"
 #include "utils/memutils.h"
@@ -71,7 +72,7 @@ sigjmp_buf *PG_exception_stack = NULL;
 /* GUC parameters */
 PGErrorVerbosity Log_error_verbosity = PGERROR_VERBOSE;
 char       *Log_line_prefix = NULL; /* format for extra log line info */
-unsigned int Log_destination = LOG_DESTINATION_STDERR;
+int                    Log_destination = LOG_DESTINATION_STDERR;
 
 #ifdef HAVE_SYSLOG
 char      *Syslog_facility;    /* openlog() parameters */
@@ -1589,6 +1590,10 @@ send_message_to_server_log(ErrorData *edata)
                fprintf(stderr, "%s", buf.data);
        }
 
+       /* 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);
 }
 
index 22df3effc329e1812c7d96603d39d8c0b485e0e2..a827653bec400ab6b9cb153b158a14df2e1698de 100644 (file)
@@ -10,7 +10,7 @@
  * Written by Peter Eisentraut <peter_e@gmx.net>.
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.225 2004/07/28 14:23:29 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.226 2004/08/05 23:32:12 tgl Exp $
  *
  *--------------------------------------------------------------------
  */
@@ -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"
@@ -801,19 +802,13 @@ static struct config_bool ConfigureNamesBool[] =
                &default_with_oids,
                true, NULL, NULL
        },
-
        {
-               {"integer_datetimes", PGC_INTERNAL, COMPILE_OPTIONS,
-                       gettext_noop("Datetimes are integer based"),
-                       NULL,
-                       GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
+               {"redirect_stderr", PGC_POSTMASTER, LOGGING_WHERE,
+                gettext_noop("Start a subprocess to capture stderr output into log files"),
+                NULL
                },
-               &integer_datetimes,
-#ifdef HAVE_INT64_TIMESTAMP
-               true, NULL, NULL
-#else
+               &Redirect_stderr,
                false, NULL, NULL
-#endif
        },
 
 #ifdef WAL_DEBUG
@@ -828,6 +823,20 @@ static struct config_bool ConfigureNamesBool[] =
        },
 #endif
 
+       {
+               {"integer_datetimes", PGC_INTERNAL, COMPILE_OPTIONS,
+                       gettext_noop("Datetimes are integer based"),
+                       NULL,
+                       GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
+               },
+               &integer_datetimes,
+#ifdef HAVE_INT64_TIMESTAMP
+               true, NULL, NULL
+#else
+               false, NULL, NULL
+#endif
+       },
+
        /* End-of-list marker */
        {
                {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL
@@ -1245,6 +1254,24 @@ static struct config_int ConfigureNamesInt[] =
                100, 1, 1000, 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 after N kilobytes"),
+                NULL
+               },
+               &Log_RotationSize,
+               10*1024, 0, INT_MAX, NULL, NULL
+       },
+
        {
                {"max_function_args", PGC_INTERNAL, COMPILE_OPTIONS,
                        gettext_noop("Shows the maximum number of function arguments"),
@@ -1634,6 +1661,23 @@ static struct config_string ConfigureNamesString[] =
                &log_destination_string,
                "stderr", assign_log_destination, NULL
        },
+       {
+               {"log_directory", PGC_SIGHUP, LOGGING_WHERE,
+                gettext_noop("Sets the destination directory for logfiles."),
+                gettext_noop("May be specified as relative to the cluster directory "
+                                         "or as absolute path.")
+               },
+               &Log_directory,
+               "pg_log", NULL, NULL
+       },
+       {
+               {"log_filename_prefix", PGC_SIGHUP, LOGGING_WHERE,
+                gettext_noop("Prefix for file names created in the log_directory."),
+                NULL
+               },
+               &Log_filename_prefix,
+               "postgresql-", NULL, NULL
+       },
 
 #ifdef HAVE_SYSLOG
        {
@@ -5055,7 +5099,7 @@ assign_log_destination(const char *value, bool doit, GucSource source)
        char *rawstring;
        List *elemlist;
        ListCell *l;
-       unsigned int  newlogdest = 0;
+       int  newlogdest = 0;
  
        /* Need a modifiable copy of string */
        rawstring = pstrdup(value);
index 9dc1ec8d837323d227814062789f9b52c693634e..633bb5a32296dd9d2cd39c62100cb65f864dbd28 100644 (file)
 #log_destination = 'stderr'    # Valid values are combinations of stderr,
                                 # syslog and eventlog, depending on
                                 # platform.
+
+# This is relevant when logging to stderr:
+#redirect_stderr = false    # Enable capturing of stderr into log files.
+# These are only relevant if redirect_stderr is true:
+#log_directory = 'pg_log'   # Directory where logfiles are written.
+                            # 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 after
+                            # so many minutes.  0 to disable.
+#log_rotation_size = 10240  # Automatic rotation of logfiles will happen after
+                            # so many kilobytes of log output.  0 to disable.
+
+# These are relevant when logging to syslog:
 #syslog_facility = 'LOCAL0'
 #syslog_ident = 'postgres'
 
+
 # - When to Log -
 
 #client_min_messages = notice  # Values, in order of decreasing detail:
index 75242d156d1acc7b62eba2468a9b4148f71fc1ff..9afc228efd0e6cb7bdcc43aa96c62144fe79553d 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/postmaster/postmaster.h,v 1.4 2004/07/21 20:34:48 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/postmaster/postmaster.h,v 1.5 2004/08/05 23:32:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -36,7 +36,7 @@ extern HANDLE PostmasterHandle;
 
 
 extern int     PostmasterMain(int argc, char *argv[]);
-extern void ClosePostmasterPorts(void);
+extern void ClosePostmasterPorts(bool am_syslogger);
 #ifdef EXEC_BACKEND
 extern pid_t postmaster_forkexec(int argc, char *argv[]);
 extern int     SubPostmasterMain(int argc, char *argv[]);
diff --git a/src/include/postmaster/syslogger.h b/src/include/postmaster/syslogger.h
new file mode 100644 (file)
index 0000000..a0f775c
--- /dev/null
@@ -0,0 +1,39 @@
+/*-------------------------------------------------------------------------
+ *
+ * syslogger.h
+ *       Exports from postmaster/syslogger.c.
+ *
+ * Copyright (c) 2004, PostgreSQL Global Development Group
+ *
+ * $PostgreSQL: pgsql/src/include/postmaster/syslogger.h,v 1.1 2004/08/05 23:32:12 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef _SYSLOGGER_H
+#define _SYSLOGGER_H
+
+/* GUC options */
+extern bool Redirect_stderr;
+extern int     Log_RotationAge;
+extern int     Log_RotationSize;
+extern char *Log_directory;
+extern char *Log_filename_prefix;
+
+extern bool am_syslogger;
+
+#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[]);
+#endif
+
+#endif   /* _SYSLOGGER_H */
index 4a2d26ddb9d97a291cb33b74a563bf420b5b97cf..ac27b796249eda4abf638251904ad2fe167713bc 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/elog.h,v 1.72 2004/07/31 23:04:55 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/elog.h,v 1.73 2004/08/05 23:32:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -272,7 +272,7 @@ typedef enum
 
 extern PGErrorVerbosity Log_error_verbosity;
 extern char *Log_line_prefix;
-extern unsigned int Log_destination;
+extern int Log_destination;
 
 /* Log destination bitmap */
 #define LOG_DESTINATION_STDERR   1