<!--
-$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">
</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>.
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>
<!--
-$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">
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</>,
<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
# 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 $
#
#-------------------------------------------------------------------------
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
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
beos_backend_startup();
#endif
/* Close the postmaster's sockets */
- ClosePostmasterPorts();
+ ClosePostmasterPorts(false);
/* Drop our connection to postmaster's shared memory, as well */
PGSharedMemoryDetach();
*
* 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"
beos_backend_startup();
#endif
/* Close the postmaster's sockets */
- ClosePostmasterPorts();
+ ClosePostmasterPorts(false);
/* Drop our connection to postmaster's shared memory, as well */
PGSharedMemoryDetach();
*
*
* 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"
static pid_t StartupPID = 0,
BgWriterPID = 0,
PgArchPID = 0,
- PgStatPID = 0;
+ PgStatPID = 0,
+ SysLoggerPID = 0;
/* Startup/shutdown state */
#define NoShutdown 0
* 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);
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
{
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);
}
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)));
}
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);
}
}
+ /* 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
* 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;
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
+ }
}
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();
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.
*/
kill(PgStatPID, SIGQUIT);
}
+ /* We do NOT restart the syslogger */
+
FatalError = true;
}
* 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();
if (strcmp(argv[1], "-forkboot") == 0)
{
/* Close the postmaster's sockets */
- ClosePostmasterPorts();
+ ClosePostmasterPorts(false);
/* Attach process to shared segments */
CreateSharedMemoryAndSemaphores(false, MaxBackends, 0);
if (strcmp(argv[1], "-forkarch") == 0)
{
/* Close the postmaster's sockets */
- ClosePostmasterPorts();
+ ClosePostmasterPorts(false);
/* Do not want to attach to shared memory */
if (strcmp(argv[1], "-forkbuf") == 0)
{
/* Close the postmaster's sockets */
- ClosePostmasterPorts();
+ ClosePostmasterPorts(false);
/* Do not want to attach to shared memory */
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 */
}
if (Shutdown <= SmartShutdown)
SignalChildren(SIGUSR1);
}
-
+
if (PgArchPID != 0 && Shutdown == NoShutdown)
{
if (CheckPostmasterSignal(PMSIGNAL_WAKEN_ARCHIVER))
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();
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);
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);
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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(×tamp));
+
+ 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;
+}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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"
/* 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 */
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);
}
* 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 $
*
*--------------------------------------------------------------------
*/
#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"
&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
},
#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
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"),
&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
{
char *rawstring;
List *elemlist;
ListCell *l;
- unsigned int newlogdest = 0;
+ int newlogdest = 0;
/* Need a modifiable copy of string */
rawstring = pstrdup(value);
#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:
* 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 $
*
*-------------------------------------------------------------------------
*/
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[]);
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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 */
* 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 $
*
*-------------------------------------------------------------------------
*/
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