logger subprocess including win32 - Mailing list pgsql-patches
From | Andreas Pflug |
---|---|
Subject | logger subprocess including win32 |
Date | |
Msg-id | 41114EEB.2070400@pse-consulting.de Whole thread Raw |
Responses |
Re: logger subprocess including win32
|
List | pgsql-patches |
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 */
pgsql-patches by date: