Attached the patch, an orgy in #ifdefs, decorated with various indents
and crlf line ends (glad we have pgindent).
Remarks:
Log_destination for win32 is set to file, because stderr is equivalent
to /dev/null for services. This doesn't reflect correctly in
postgresql.conf.sample.
Log_destination=eventlog is like logging to /var/log/messages, you'd
make the sysadmin your enemy if he finds his app log flooded with
"NOTICE: created implicit index for pk". FYI: The size of the event log
is limited (default: 512k), cyclic overwriting can be selected, but *no*
rotation. Only really important messages should go there, not standard
logging. OTOH, really important messages *must* go there, so I changed
FATAL and PANIC messages to eventlog unconditionally.
Regards,
Andreas
Index: backend/postmaster/Makefile
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/postmaster/Makefile,v
retrieving revision 1.18
diff -u -r1.18 Makefile
--- backend/postmaster/Makefile 21 Jul 2004 20:34:46 -0000 1.18
+++ backend/postmaster/Makefile 4 Aug 2004 18:40:01 -0000
@@ -12,7 +12,7 @@
top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
-OBJS = postmaster.o bgwriter.o pgstat.o pgarch.o
+OBJS = postmaster.o bgwriter.o pgstat.o pgarch.o syslogger.o
all: SUBSYS.o
Index: backend/postmaster/postmaster.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/postmaster/postmaster.c,v
retrieving revision 1.418
diff -u -r1.418 postmaster.c
--- backend/postmaster/postmaster.c 1 Aug 2004 17:45:43 -0000 1.418
+++ backend/postmaster/postmaster.c 4 Aug 2004 18:40:06 -0000
@@ -117,7 +117,7 @@
#include "utils/ps_status.h"
#include "bootstrap/bootstrap.h"
#include "pgstat.h"
-
+#include "postmaster/syslogger.h"
/*
* List of active backends (or child processes anyway; we don't actually
@@ -200,6 +200,7 @@
BgWriterPID = 0,
PgArchPID = 0,
PgStatPID = 0;
+pid_t SysLoggerPID = 0;
/* Startup/shutdown state */
#define NoShutdown 0
@@ -851,6 +852,12 @@
#endif
/*
+ * start logging to file
+ */
+
+ SysLoggerPID = SysLogger_Start();
+
+ /*
* Reset whereToSendOutput from Debug (its starting state) to None.
* This stops ereport from sending log messages to stderr unless
* Log_destination permits. We don't do this until the postmaster
@@ -1230,6 +1237,11 @@
StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
PgStatPID = pgstat_start();
+ /* If we have lost the system logger, try to start a new one */
+ if (SysLoggerPID == 0 &&
+ StartupPID == 0 && !FatalError && Shutdown == NoShutdown)
+ SysLoggerPID = SysLogger_Start();
+
/*
* Touch the socket and lock file at least every ten minutes, to ensure
* that they are not removed by overzealous /tmp-cleaning tasks.
@@ -1770,6 +1782,8 @@
kill(BgWriterPID, SIGHUP);
if (PgArchPID != 0)
kill(PgArchPID, SIGHUP);
+ if (SysLoggerPID != 0)
+ kill(SysLoggerPID, SIGHUP);
/* PgStatPID does not currently need SIGHUP */
load_hba();
load_ident();
@@ -1836,7 +1850,7 @@
kill(PgStatPID, SIGQUIT);
break;
- case SIGINT:
+ case SIGINT:
/*
* Fast Shutdown:
*
@@ -2064,6 +2078,15 @@
continue;
}
+ /* was it the system logger, try to start a new one */
+ if (SysLoggerPID != 0 && pid == SysLoggerPID)
+ {
+ if (exitstatus != 0)
+ LogChildExit(LOG, gettext("system logger process"),
+ pid, exitstatus);
+ SysLoggerPID = SysLogger_Start();
+ continue;
+ }
/*
* Else do standard backend child cleanup.
*/
@@ -2967,7 +2990,16 @@
PgstatCollectorMain(argc, argv);
proc_exit(0);
}
+ if (strcmp(argv[1], "-forklog") == 0)
+ {
+ /* Close the postmaster's sockets */
+ ClosePostmasterPorts();
+ /* Do not want to attach to shared memory */
+
+ SysLoggerMain(argc, argv);
+ proc_exit(0);
+ }
return 1; /* shouldn't get here */
}
@@ -3023,7 +3055,7 @@
if (Shutdown <= SmartShutdown)
SignalChildren(SIGUSR1);
}
-
+
if (PgArchPID != 0 && Shutdown == NoShutdown)
{
if (CheckPostmasterSignal(PMSIGNAL_WAKEN_ARCHIVER))
@@ -3036,6 +3068,11 @@
}
}
+ if (CheckPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE) && SysLoggerPID != 0)
+ {
+ kill(SysLoggerPID, SIGUSR1);
+ }
+
PG_SETMASK(&UnBlockSig);
errno = save_errno;
Index: backend/utils/error/elog.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/error/elog.c,v
retrieving revision 1.144
diff -u -r1.144 elog.c
--- backend/utils/error/elog.c 31 Jul 2004 00:45:38 -0000 1.144
+++ backend/utils/error/elog.c 4 Aug 2004 18:40:10 -0000
@@ -71,7 +71,11 @@
/* GUC parameters */
PGErrorVerbosity Log_error_verbosity = PGERROR_VERBOSE;
char *Log_line_prefix = NULL; /* format for extra log line info */
+#ifdef WIN32
+unsigned int Log_destination = LOG_DESTINATION_FILE;
+#else
unsigned int Log_destination = LOG_DESTINATION_STDERR;
+#endif
#ifdef HAVE_SYSLOG
char *Syslog_facility; /* openlog() parameters */
@@ -83,6 +87,11 @@
static void write_eventlog(int level, const char *line);
#endif
+/* in syslogger.c */
+extern FILE *syslogFile;
+extern FILE *realStdErr;
+extern pid_t SysLoggerPID;
+
/* We provide a small stack of ErrorData records for re-entrant cases */
#define ERRORDATA_STACK_SIZE 5
@@ -1554,7 +1563,8 @@
}
#endif /* HAVE_SYSLOG */
#ifdef WIN32
- if (Log_destination & LOG_DESTINATION_EVENTLOG)
+ if ((Log_destination & LOG_DESTINATION_EVENTLOG)
+ || edata->elevel >= FATAL)
{
int eventlog_level;
switch (edata->elevel)
@@ -1583,10 +1593,31 @@
write_eventlog(eventlog_level, buf.data);
}
#endif /* WIN32 */
- /* Write to stderr, if enabled */
- if ((Log_destination & LOG_DESTINATION_STDERR) || whereToSendOutput == Debug)
+ /*
+ * Write to stderr. If Log_destination is file or stderr
+ * if file is target, the logger process will handle this
+ */
+ if ((Log_destination & (LOG_DESTINATION_STDERR | LOG_DESTINATION_FILE))
+ || whereToSendOutput == Debug)
{
- fprintf(stderr, "%s", buf.data);
+ if (SysLoggerPID == MyProcPid || syslogFile != 0)
+ {
+ if ((Log_destination & LOG_DESTINATION_STDERR)
+ || whereToSendOutput == Debug)
+ {
+ if (realStdErr != 0)
+ fprintf(realStdErr, "%s", buf.data);
+ else
+ fprintf(stderr, "%s", buf.data) ;
+
+ }
+
+ if (syslogFile != 0)
+ fprintf(syslogFile, "%s", buf.data) ;
+ }
+ else
+ fprintf(stderr, "%s", buf.data) ;
+
}
pfree(buf.data);
Index: backend/utils/misc/guc.c
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/misc/guc.c,v
retrieving revision 1.225
diff -u -r1.225 guc.c
--- backend/utils/misc/guc.c 28 Jul 2004 14:23:29 -0000 1.225
+++ backend/utils/misc/guc.c 4 Aug 2004 18:40:19 -0000
@@ -44,6 +44,7 @@
#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
#include "postmaster/bgwriter.h"
+#include "postmaster/syslogger.h"
#include "postmaster/postmaster.h"
#include "storage/bufmgr.h"
#include "storage/fd.h"
@@ -1285,6 +1286,23 @@
BLCKSZ, BLCKSZ, BLCKSZ, NULL, NULL
},
+ {
+ {"log_rotation_age", PGC_SIGHUP, LOGGING_WHERE,
+ gettext_noop("Automatic logfile rotation will occur after n minutes"),
+ NULL
+ },
+ &Log_RotationAge,
+ 24*60, 0, INT_MAX, NULL, NULL
+ },
+ {
+ {"log_rotation_size", PGC_SIGHUP, LOGGING_WHERE,
+ gettext_noop("Automatic logfile rotation will occur if this size is reached (in kb)"),
+ NULL
+ },
+ &Log_RotationSize,
+ 10*1024, 0, INT_MAX, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL
@@ -1625,14 +1643,37 @@
},
{
- {"log_destination", PGC_SIGHUP, LOGGING_WHERE,
- gettext_noop("Sets the destination for server log output."),
- gettext_noop("Valid values are combinations of stderr, syslog "
+ {"log_destination", PGC_POSTMASTER, LOGGING_WHERE,
+ gettext_noop("Sets the target for log output."),
+ gettext_noop("Valid values are combinations of stderr, file, syslog "
"and eventlog, depending on platform."),
GUC_LIST_INPUT
},
&log_destination_string,
+#ifdef WIN32
+ "file", assign_log_destination, NULL
+#else
"stderr", assign_log_destination, NULL
+#endif
+ },
+ {
+ {"log_directory", PGC_SIGHUP, LOGGING_WHERE,
+ gettext_noop("Sets the target directory for log output."),
+ gettext_noop("May be specified as relative to the cluster directory "
+ "or as absolute path."),
+ GUC_LIST_INPUT | GUC_REPORT
+ },
+ &Log_directory,
+ "pg_log", NULL, NULL
+ },
+ {
+ {"log_filename_prefix", PGC_SIGHUP, LOGGING_WHERE,
+ gettext_noop("prefix for logfile names created in the log_directory."),
+ NULL,
+ GUC_LIST_INPUT | GUC_REPORT
+ },
+ &Log_filename_prefix,
+ "postgresql-", NULL, NULL
},
#ifdef HAVE_SYSLOG
@@ -5079,6 +5120,8 @@
if (pg_strcasecmp(tok,"stderr") == 0)
newlogdest |= LOG_DESTINATION_STDERR;
+ else if (pg_strcasecmp(tok,"file") == 0)
+ newlogdest |= LOG_DESTINATION_FILE;
#ifdef HAVE_SYSLOG
else if (pg_strcasecmp(tok,"syslog") == 0)
newlogdest |= LOG_DESTINATION_SYSLOG;
Index: backend/utils/misc/postgresql.conf.sample
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/misc/postgresql.conf.sample,v
retrieving revision 1.118
diff -u -r1.118 postgresql.conf.sample
--- backend/utils/misc/postgresql.conf.sample 21 Jul 2004 20:34:46 -0000 1.118
+++ backend/utils/misc/postgresql.conf.sample 4 Aug 2004 18:40:20 -0000
@@ -167,9 +167,17 @@
# - Where to Log -
-#log_destination = 'stderr' # Valid values are combinations of stderr,
- # syslog and eventlog, depending on
- # platform.
+#log_destination = 'stderr' # Valid values are combinations of stderr, file,
+ # syslog and eventlog, depending on platform.
+#log_directory = 'pg_log' # subdirectory where logfiles are written
+ # if 'file' log_destination is used.
+ # May be specified absolute or relative to PGDATA
+#log_filename_prefix = 'postgresql_' # prefix for logfile names
+#log_rotation_age = 1440 # Automatic rotation of logfiles will happen if
+ # specified age in minutes is reached. 0 to disable.
+#log_rotation_size = 10240 # Automatic rotation of logfiles will happen if
+ # specified size in kb is reached. 0 to disable.
+
#syslog_facility = 'LOCAL0'
#syslog_ident = 'postgres'
Index: include/storage/pmsignal.h
===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/storage/pmsignal.h,v
retrieving revision 1.9
diff -u -r1.9 pmsignal.h
--- include/storage/pmsignal.h 19 Jul 2004 02:47:15 -0000 1.9
+++ include/storage/pmsignal.h 4 Aug 2004 18:40:21 -0000
@@ -25,7 +25,7 @@
PMSIGNAL_PASSWORD_CHANGE, /* pg_pwd file has changed */
PMSIGNAL_WAKEN_CHILDREN, /* send a SIGUSR1 signal to all backends */
PMSIGNAL_WAKEN_ARCHIVER, /* send a NOTIFY signal to xlog archiver */
-
+ PMSIGNAL_ROTATE_LOGFILE, /* send SIGUSR1 to syslogger to rotate logfile */
NUM_PMSIGNALS /* Must be last value of enum! */
} PMSignalReason;
Index: elog.h===================================================================
RCS file: /projects/cvsroot/pgsql-server/src/include/utils/elog.h,v
retrieving revision 1.72
diff -u -r1.72 elog.h
--- elog.h 31 Jul 2004 23:04:55 -0000 1.72
+++ elog.h 4 Aug 2004 18:45:49 -0000
@@ -278,6 +278,7 @@
#define LOG_DESTINATION_STDERR 1
#define LOG_DESTINATION_SYSLOG 2
#define LOG_DESTINATION_EVENTLOG 4
+#define LOG_DESTINATION_FILE 8
/* Other exported functions */
extern void DebugFileOpen(void);
/*-------------------------------------------------------------------------
*
* syslogger.c
*
* The system logger (syslogger) is new in Postgres 7.5. It catches all
* stderr output from backends, the postmaster and subprocesses by
* redirecting to a pipe, and writes it to a logfile and stderr if
* configured.
* It's possible to have size and age limits for the logfile configured
* in postgresql.conf. If these limits are reached or passed, the
* current logfile is closed and a new one is created (rotated).
* The logfiles are stored in a subdirectory (configurable in
* postgresql.conf), using an internal naming scheme that mangles
* creation time and current postmaster pid.
*
* Author: Andreas Pflug <pgadmin@pse-consulting.de>
*
* Copyright (c) 2004, PostgreSQL Global Development Group
*
*
* IDENTIFICATION
* $PostgreSQL: $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include "libpq/pqsignal.h"
#include "miscadmin.h"
#include "postmaster/postmaster.h"
#include "storage/pmsignal.h"
#include "storage/pg_shmem.h"
#include "storage/ipc.h"
#include "postmaster/syslogger.h"
#include "utils/ps_status.h"
#include "utils/guc.h"
/*
* GUC parameters
*/
int Log_RotationAge = 24*60;
int Log_RotationSize = 10*1024;
char * Log_directory = "pg_log";
char * Log_filename_prefix = "postgresql-";
extern pid_t SysLoggerPID;
/*
* Flags set by interrupt handlers for later service in the main loop.
*/
static volatile sig_atomic_t got_SIGHUP = false;
static volatile sig_atomic_t rotation_requested = false;
#define MAXRETRIES 3
static pg_time_t last_rotation_time = 0;
static char currentLogDir[MAXPGPATH];
static void sigHupHandler(SIGNAL_ARGS);
static void rotationHandler(SIGNAL_ARGS);
void writeLogfile(char *buffer, int count);
#ifdef EXEC_BACKEND
static pid_t syslogger_forkexec(void);
#endif
static char* logfile_getname(pg_time_t timestamp);
static bool logfile_rotate(void);
FILE *realStdErr = NULL;
volatile FILE *syslogFile = NULL;
#ifdef WIN32
static unsigned int __stdcall pipeThread(void *arg);
static HANDLE readPipe=0, writePipe=0;
static HANDLE threadHandle=0;
static CRITICAL_SECTION sysfileSection;
#else
static int syslogPipe[2] = {0, 0};
#endif
void
writeLogfile(char *buffer, int count)
{
int rc;
#ifdef WIN32
EnterCriticalSection(&sysfileSection);
rc = fwrite(buffer, 1, count, (FILE*)syslogFile);
LeaveCriticalSection(&sysfileSection);
#else
rc = fwrite(buffer, 1, count, (FILE*)syslogFile);
#endif
if (rc < 1)
{
ereport(COMMERROR,
(errcode_for_file_access(),
errmsg("fwrite to logfile failed in system logger: %m")));
exit(1);
}
if (Log_destination & LOG_DESTINATION_STDERR)
{
if (realStdErr)
fwrite(buffer, 1, count, realStdErr);
else
fwrite(buffer, 1, count, stderr);
}
}
#ifdef WIN32
unsigned int __stdcall
pipeThread(void *arg)
{
DWORD bytesRead;
char logbuffer[1024];
for (;;)
{
if (!ReadFile(readPipe,
logbuffer, sizeof(logbuffer), &bytesRead, 0))
{
ereport(COMMERROR,
(errcode_for_file_access(),
errmsg("could not read from system logger pipe: %m")));
exit(1);
}
if (bytesRead > 0)
writeLogfile(logbuffer, bytesRead);
}
_endthread();
return 0;
}
#endif
/*
* Main entry point for syslogger process
* argc/argv parameters are valid only in EXEC_BACKEND case.
*/
void
SysLoggerMain(int argc, char *argv[])
{
#ifdef EXEC_BACKEND
int fd;
Assert(argc == 8);
argv += 3;
strncpy(postgres_exec_path, *argv++, MAXPGPATH);
#ifdef WIN32
readPipe = (HANDLE)atoi(*argv++);
writePipe = (HANDLE)atoi(*argv++);
fd = atoi(*argv++);
if (fd != 0)
fd = _open_osfhandle(fd, _O_APPEND);
if (fd != 0)
syslogFile = fdopen(fd, "a+");
fd = atoi(*argv++);
if (fd != 0)
fd = _open_osfhandle(fd, _O_APPEND);
if (fd != 0)
{
realStdErr = fdopen(fd, "a");
setvbuf((FILE*)realStdErr, 0, _IONBF, 0);
}
InitializeCriticalSection(&sysfileSection);
unsigned int tid;
threadHandle = (HANDLE)_beginthreadex(0, 0, pipeThread, 0, 0, &tid);
#else
syslogPipe[0] = atoi(*argv++);
syslogPipe[1] = atoi(*argv++);
fd = atoi(*argv++);
if (fd != 0)
syslogFile = fdopen(fd, "a+");
fd = atoi(*argv++);
if (fd != 0)
{
realStdErr = fdopen(fd, "a");
setvbuf((FILE*)realStdErr, 0, _IONBF, 0);
}
#endif
#endif
setvbuf((FILE*)syslogFile, 0, _IONBF, 0);
IsUnderPostmaster = true;
MyProcPid = getpid();
init_ps_display("system logger process", "", "");
set_ps_display("");
/*
* Properly accept or ignore signals the postmaster might send us
*
* Note: we ignore all termination signals, and wait for the postmaster
* to die to catch as much pipe output as possible.
*/
pqsignal(SIGHUP, sigHupHandler); /* set flag to read config file */
pqsignal(SIGINT, SIG_IGN);
pqsignal(SIGTERM, SIG_IGN);
pqsignal(SIGQUIT, SIG_IGN);
pqsignal(SIGALRM, SIG_IGN);
pqsignal(SIGPIPE, SIG_IGN);
pqsignal(SIGUSR1, rotationHandler); /* request log rotation */
pqsignal(SIGUSR2, SIG_IGN);
/*
* Reset some signals that are accepted by postmaster but not here
*/
pqsignal(SIGCHLD, SIG_DFL);
pqsignal(SIGTTIN, SIG_DFL);
pqsignal(SIGTTOU, SIG_DFL);
pqsignal(SIGCONT, SIG_DFL);
pqsignal(SIGWINCH, SIG_DFL);
PG_SETMASK(&UnBlockSig);
/*
* if we restarted, our stderr is redirected.
* Direct it back to system stderr.
*/
if (realStdErr != NULL)
{
if (dup2(fileno(realStdErr), fileno(stderr)) < 0)
{
char *errstr = strerror(errno);
/*
* Now we have a real problem: we can't redirect to stderr,
* and can't ereport it correctly (it would go into our queue
* which will never be read
* We're writing everywhere, hoping to leave at least some
* hint of what happened.
*/
fprintf(realStdErr, "PANIC: Syslogger couldn't redirect its stderr to the saved stderr: %s\n", errstr);
fprintf(stderr, "PANIC: Syslogger couldn't redirect its stderr to the saved stderr: %s\n", errstr);
ereport(PANIC,
(errcode_for_file_access(),
(errmsg("Syslogger couldn't redirect its stderr to the saved stderr: %s", errstr))));
exit(1);
}
realStdErr = NULL;
}
/* remember age of initial logfile */
last_rotation_time = time(NULL);
strncpy(currentLogDir, Log_directory, MAXPGPATH);
/* main worker loop */
for (;;)
{
pg_time_t now;
int elapsed_secs;
#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, we rotate to make sure
* we're writing the logfiles where the backends
* expect us to do so.
*/
if (strncmp(Log_directory, currentLogDir, MAXPGPATH))
{
rotation_requested = true;
strncpy(currentLogDir, Log_directory, MAXPGPATH);
}
}
if (!rotation_requested && last_rotation_time != 0 && Log_RotationAge > 0)
{
/*
* Do an unforced rotation if too much time has elapsed
* since the last one.
*/
now = time(NULL);
elapsed_secs = now - last_rotation_time;
if (elapsed_secs >= Log_RotationAge * 60)
rotation_requested = true;
}
if (!rotation_requested && Log_RotationSize > 0)
{
/*
* Do an unforced rotation if file is too big
*/
if (ftell((FILE*)syslogFile) >= Log_RotationSize * 1024)
rotation_requested = true;
}
if (rotation_requested)
{
if (!logfile_rotate())
{
ereport(ERROR,
(errcode_for_file_access(),
(errmsg("logfile rotation failed, disabling auto rotation (SIGHUP to
reenable):%m"))));
Log_RotationAge = 0;
Log_RotationSize = 0;
}
rotation_requested = false;
}
#ifdef WIN32
pgwin32_backend_usleep(1000000);
#else
FD_ZERO(&rfds);
FD_SET(syslogPipe[0], &rfds);
timeout.tv_sec=1;
timeout.tv_usec=0;
/*
* Check if data is present
*/
rc = select(syslogPipe[0]+1, &rfds, NULL, NULL, &timeout);
PG_SETMASK(&UnBlockSig);
if (rc < 0 && errno != EINTR)
{
ereport(COMMERROR,
(errcode_for_socket_access(),
errmsg("select() failed in system logger: %m")));
exit(1);
}
if (rc > 0 && FD_ISSET(syslogPipe[0], &rfds))
{
bytesRead = piperead(syslogPipe[0],
logbuffer, sizeof(logbuffer));
if (bytesRead < 0 && errno != EINTR)
{
/*
* EMFILE has been observed when a backend terminated
* after a SSL client connection broke down.
* There might be others we should ignore.
*/
if (errno == EMFILE)
continue;
ereport(COMMERROR,
(errcode_for_socket_access(),
errmsg("could not read from system logger pipe: %m")));
exit(1);
}
else if (bytesRead > 0)
{
writeLogfile(logbuffer, bytesRead);
continue;
}
}
#endif
/*
* If postmaster died, there's nothing to log any more.
* We check this only after pipe timeouts to receive as much as possible
* from the pipe.
*/
if (!PostmasterIsAlive(true))
{
#ifdef WIN32
/* make sure the pipe is empty */
Sleep(100);
SuspendThread(threadHandle);
CloseHandle(threadHandle);
#endif
if (syslogFile)
fclose((FILE*)syslogFile);
exit(0);
}
}
}
int
SysLogger_Start(void)
{
pid_t sysloggerPid;
pg_time_t now;
char *filename;
if (!(Log_destination & LOG_DESTINATION_FILE))
return 0;
/* create the pipe which will receive stderr output */
#ifdef WIN32
if (!readPipe)
{
SECURITY_ATTRIBUTES sa;
memset(&sa, 0, sizeof(SECURITY_ATTRIBUTES));
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
if (!CreatePipe(&readPipe, &writePipe, &sa, 32768))
ereport(FATAL,
(errcode_for_file_access(),
(errmsg("pipe for syslogging not created: %m"))));
#else
if (!syslogPipe[0])
{
if (pgpipe(syslogPipe) < 0)
ereport(FATAL,
(errcode_for_socket_access(),
(errmsg("pipe for syslogging not created: %m"))));
if (!set_noblock(syslogPipe[1]))
{
ereport(FATAL,
(errcode_for_socket_access(),
errmsg("could not set syslogging pipe to nonblocking mode: %m")));
}
#endif
}
now = time(NULL);
/*
* create log directory, ignore error (will be caught on first logfile open)
*/
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 insure that the logger process has a writable file.
*/
filename = logfile_getname(now);
/*
* The file is opened for appending, in case the syslogger
* is restarted right after a rotation.
*/
syslogFile = fopen(filename, "a+");
if (!syslogFile)
{
/*
* if we can't open the syslog file for the syslogger process,
* we try to redirect stderr back to have some logging.
*/
ereport(WARNING,
(errcode_for_file_access(),
(errmsg("error opening syslog file %s: %m", filename))));
if (realStdErr != NULL)
{
if (dup2(fileno(realStdErr), fileno(stderr)) < 0)
ereport(FATAL,
(errcode_for_file_access(),
(errmsg("error redirecting stderr to default: %m"))));
ereport(FATAL,
(errmsg("logfile output corrupted")));
}
}
pfree(filename);
fflush(stdout);
fflush(stderr);
#ifdef __BEOS__
/* Specific beos actions before backend startup */
beos_before_backend_startup();
#endif
#ifdef EXEC_BACKEND
switch ((sysloggerPid = syslogger_forkexec()))
#else
switch ((sysloggerPid = fork()))
#endif
{
case -1:
#ifdef __BEOS__
/* Specific beos actions */
beos_backend_startup_failed();
#endif
ereport(LOG,
(errmsg("could not fork system logger: %m")));
return 0;
#ifndef EXEC_BACKEND
case 0:
/* in postmaster child ... */
#ifdef __BEOS__
/* Specific beos actions after backend startup */
beos_backend_startup();
#endif
/* Close the postmaster's sockets */
ClosePostmasterPorts();
/* Drop our connection to postmaster's shared memory, as well */
PGSharedMemoryDetach();
/* do the work */
SysLoggerMain(0, NULL);
break;
#endif
default:
/* now we redirect stderr, if not done already */
if (realStdErr == NULL)
{
int dh= dup(fileno(stderr));
if (dh < 0)
ereport(FATAL,
(errcode_for_file_access(),
(errmsg("stderr duplication failed: %m"))));
realStdErr = fdopen(dh, "a");
if (realStdErr == NULL)
ereport(FATAL,
(errcode_for_file_access(),
(errmsg("realstderr reopen failed: %m"))));
#ifdef WIN32
if (dup2(_open_osfhandle((long)writePipe, _O_APPEND), _fileno(stderr)) < 0)
ereport(FATAL,
(errcode_for_file_access(),
(errmsg("stderr pipe redirection failed: %m"))));
#else
if (dup2(syslogPipe[1], fileno(stdout)) < 0)
ereport(FATAL,
(errcode_for_file_access(),
(errmsg("stdout pipe redirection failed: %m"))));
if (dup2(syslogPipe[1], fileno(stderr)) < 0)
ereport(FATAL,
(errcode_for_file_access(),
(errmsg("stderr pipe redirection failed: %m"))));
#endif
}
/* postmaster will never write the file; close it */
fclose((FILE*)syslogFile);
syslogFile = NULL;
return (int) sysloggerPid;
}
/* we should never reach here */
return 0;
}
#ifdef EXEC_BACKEND
static pid_t
syslogger_forkexec()
{
char *av[10];
int ac = 0, bufc = 0, i;
char numbuf[4][32];
av[ac++] = "postgres";
av[ac++] = "-forklog";
av[ac++] = NULL; /* filled in by postmaster_forkexec */
/* postgres_exec_path is not passed by write_backend_variables */
av[ac++] = postgres_exec_path;
/* Pipe file ids (those not passed by write_backend_variables) */
#ifdef WIN32
snprintf(numbuf[bufc++], 32, "%d", (int)readPipe);
snprintf(numbuf[bufc++], 32, "%d", (int)writePipe);
if (syslogFile != 0)
snprintf(numbuf[bufc++], 32, "%ld", _get_osfhandle(_fileno((FILE*)syslogFile)));
else
strcpy(numbuf[bufc++], "0");
if (realStdErr != 0)
snprintf(numbuf[bufc++],32, "%ld", _get_osfhandle(_fileno(realStdErr)));
else
strcpy(numbuf[bufc++], "0");
#else
snprintf(numbuf[bufc++],32, "%d", syslogPipe[0]);
snprintf(numbuf[bufc++],32, "%d", syslogPipe[1]);
if (syslogFile != 0)
snprintf(numbuf[bufc++], 32, "%d", fileno(syslogFile)));
else
strcpy(numbuf[bufc++], "0");
if (realStdErr != 0)
snprintf(numbuf[bufc++], 32, "%d", fileno(realStdErr)));
else
strcpy(numbuf[bufc++], "0");
#endif
/* Add to the arg list */
Assert(bufc <= lengthof(pgstatBuf));
for (i = 0; i < bufc; i++)
av[ac++] = numbuf[i];
av[ac] = NULL;
Assert(ac < lengthof(av));
return postmaster_forkexec(ac, av);
}
#endif
/* --------------------------------
* logfile routines
* --------------------------------
*/
/*
* perform rotation
*/
bool
logfile_rotate(void)
{
char *filename;
pg_time_t now;
FILE *fh;
now = time(NULL);
filename = logfile_getname(now);
fh = fopen(filename, "a+");
if (!fh)
{
/*
* if opening the new file fails, the caller is responsible
* for taking consequences. */
pfree(filename);
return false;
}
setvbuf(fh, 0, _IONBF, 0);
#ifdef WIN32
EnterCriticalSection(&sysfileSection);
fclose((FILE*)syslogFile);
syslogFile = fh;
LeaveCriticalSection(&sysfileSection);
#else
fclose(syslogFile);
syslogFile = fh;
#endif
last_rotation_time = now;
/* official opening of the new logfile */
ereport(NOTICE,
(errcode(ERRCODE_WARNING),
errmsg("Opened new log file %s", filename)));
pfree(filename);
return true;
}
/*
* creates logfile name using timestamp information
*/
#define TIMESTAMPPATTERN "%Y-%m-%d_%H%M%S"
static char*
logfile_getname(pg_time_t timestamp)
{
char *filetemplate;
char *filename;
if (is_absolute_path(Log_directory))
{
filetemplate = palloc(strlen(Log_directory)
+ strlen(Log_filename_prefix)
+ sizeof(TIMESTAMPPATTERN)+10 +2);
if (filetemplate)
sprintf(filetemplate, "%s/%s%s_%05lu.log",
Log_directory, Log_filename_prefix,
TIMESTAMPPATTERN, (unsigned long)PostmasterPid);
}
else
{
filetemplate = palloc(strlen(DataDir) + strlen(Log_directory)
+ strlen(Log_filename_prefix)
+ sizeof(TIMESTAMPPATTERN) +10 +3);
if (filetemplate)
sprintf(filetemplate, "%s/%s/%s%s_%05lu.log",
DataDir, Log_directory, Log_filename_prefix,
TIMESTAMPPATTERN, (unsigned long)PostmasterPid);
}
filename = palloc(MAXPGPATH);
if (!filename || !filetemplate)
ereport(FATAL,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("Out of memory")));
pg_strftime(filename, MAXPGPATH, filetemplate, pg_localtime(×tamp));
pfree(filetemplate);
return filename;
}
/* --------------------------------
* API helper routines
* --------------------------------
*/
/*
* Rotate log file
*/
bool
LogFileRotate(void)
{
if (!(Log_destination & LOG_DESTINATION_FILE))
{
ereport(NOTICE,
(errcode(ERRCODE_WARNING),
errmsg("no logfile configured; rotation not supported")));
return false;
}
SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE);
return true;
}
/* --------------------------------
* signal handler routines
* --------------------------------
*/
/* SIGHUP: set flag to reload config file */
static void
sigHupHandler(SIGNAL_ARGS)
{
got_SIGHUP = true;
}
/* SIGUSR1: set flag to rotate logfile */
static void
rotationHandler(SIGNAL_ARGS)
{
rotation_requested = true;
}
/*-------------------------------------------------------------------------
*
* syslogger.h
* Exports from postmaster/syslogger.c.
*
* Copyright (c) 2004, PostgreSQL Global Development Group
*
* $PostgreSQL: $
*
*-------------------------------------------------------------------------
*/
#ifndef _SYSLOGGER_H
#define _SYSLOGGER_H
#include "pgtime.h"
/* GUC options */
extern int Log_RotationAge;
extern int Log_RotationSize;
extern char * Log_directory;
extern char * Log_filename_prefix;
extern int SysLogger_Start(void);
extern void SysLoggerMain(int argc, char *argv[]);
extern bool LogFileRotate(void);
#endif /* _SYSLOGGER_H */