Bruce Momjian wrote:
For logs I think pgsql_ is best because that filename is already
going to be long, and I don't usually like dashes in file names. They look too much like arguments, but tarballs use them and it looks
OK there, I guess.
I wasn't talking about what looks best, I was talking about current practice for log files. From that you might be able to extrapolate what other people have previously found to look best.
In any case, we're not using DOS and 12 inch monitors any more. File names can be as long as we want.
Before the thread concentrates too much on a decent default value, I'm posting a fresh version of the patch, for some more discussion. Current default for pg_logfile_prefix is 'postgresql-', may the committers decide which name is The Perfect One.
All previous suggestions have been included, (nb: abstimein is not usable, because it ereports(ERROR) on failure; we want to skip wrong files gracefully, so I'm using ParseDateTime and DecodeDateTime instead).
I'd still need feedback on pg_dir_ls: should it merely return a setof text, or should I enrich it to a record returning all stat data? After spending another thought on it, I believe the more sql-like approach is to deliver a full-featured record which is selected for the desired data, not adding columns with functions.
Regards, Andreas
/*------------------------------------------------------------------------- * * 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 <[EMAIL PROTECTED]> * * 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_"; /* * 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; static pg_time_t last_rotation_time = 0; static void sigHupHandler(SIGNAL_ARGS); static void rotationHandler(SIGNAL_ARGS); #ifdef EXEC_BACKEND static pid_t syslogger_forkexec(); #endif static char* logfile_getname(pg_time_t timestamp); static bool logfile_rotate(void); FILE *realStdErr = NULL; FILE *syslogFile = NULL; int syslogPipe[2] = {0, 0}; /* * Main entry point for syslogger process * argc/argv parameters are valid only in EXEC_BACKEND case. */ void SysLoggerMain(int argc, char *argv[]) { IsUnderPostmaster = true; MyProcPid = getpid(); init_ps_display("system logger process", "", ""); set_ps_display(""); #ifdef EXEC_BACKEND Assert(argc == 6); argv += 3; StrNCpy(postgres_exec_path, argv++, MAXPGPATH); syslogPipe[0] = atoi(argv++); syslogPipe[1] = atoi(argv); #endif /* * Properly accept or ignore signals the postmaster might send us * * Note: we ignore all termination signals, and wait for the postmaster * to die to catch as much pipe output as possible. */ pqsignal(SIGHUP, sigHupHandler); /* set flag to read config file */ pqsignal(SIGINT, SIG_IGN); pqsignal(SIGTERM, SIG_IGN); pqsignal(SIGQUIT, SIG_IGN); pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); pqsignal(SIGUSR1, rotationHandler); /* request log rotation */ pqsignal(SIGUSR2, SIG_IGN); /* * Reset some signals that are accepted by postmaster but not here */ pqsignal(SIGCHLD, SIG_DFL); pqsignal(SIGTTIN, SIG_DFL); pqsignal(SIGTTOU, SIG_DFL); pqsignal(SIGCONT, SIG_DFL); pqsignal(SIGWINCH, SIG_DFL); PG_SETMASK(&UnBlockSig); /* * if we restarted, our stderr is redirected. * Direct it back to system stderr. */ if (realStdErr != NULL) { if (dup2(fileno(realStdErr), fileno(stderr)) < 0) { char *errstr = strerror(errno); /* * Now we have a real problem: we can't redirect to stderr, * and can't ereport it correctly (it would go into our queue * which will never be read * We're writing everywhere, hoping to leave at least some * hint of what happened. */ fprintf(realStdErr, "PANIC: Syslogger couldn't redirect its stderr to the saved stderr: %s\n", errstr); fprintf(stderr, "PANIC: Syslogger couldn't redirect its stderr to the saved stderr: %s\n", errstr); ereport(PANIC, (errcode_for_file_access(), (errmsg("Syslogger couldn't redirect its stderr to the saved stderr: %s", errstr)))); exit(1); } realStdErr = NULL; } /* we'll never write that pipe */ close(syslogPipe[1]); syslogPipe[1] = 0; /* remember age of initial logfile */ last_rotation_time = time(NULL); /* main worker loop */ for (;;) { pg_time_t now; int elapsed_secs; char logbuffer[1024]; char bytesRead; fd_set rfds; struct timeval timeout; int rc; if (got_SIGHUP) { char *olddir=pstrdup(Log_directory); 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 (strcmp(Log_directory, olddir)) rotation_requested = true; pfree(olddir); } if (!rotation_requested && last_rotation_time != 0 && Log_RotationAge > 0) { /* * Do an unforced rotation if too much time has elapsed * since the last one. */ now = time(NULL); elapsed_secs = now - last_rotation_time; if (elapsed_secs >= Log_RotationAge * 60) rotation_requested = true; } if (!rotation_requested && Log_RotationSize > 0) { /* * Do an unforced rotation if file is too big */ if (ftell(syslogFile) >= Log_RotationSize * 1024) rotation_requested = true; } if (rotation_requested) { if (!logfile_rotate()) { ereport(ERROR, (errcode_for_file_access(), (errmsg("logfile rotation failed, disabling auto rotation (SIGHUP to reenable): %m")))); Log_RotationAge = 0; Log_RotationSize = 0; } rotation_requested = false; } FD_ZERO(&rfds); FD_SET(syslogPipe[0], &rfds); timeout.tv_sec=1; timeout.tv_usec=0; /* * Check if data is present */ rc = select(syslogPipe[0]+1, &rfds, NULL, NULL, &timeout); PG_SETMASK(&UnBlockSig); if (rc > 0 && FD_ISSET(syslogPipe[0], &rfds)) { bytesRead = piperead(syslogPipe[0], logbuffer, sizeof(logbuffer)); if (bytesRead > 0) { if (fwrite(logbuffer, 1, bytesRead, syslogFile) < 1) { ereport(COMMERROR, (errcode_for_file_access(), errmsg("fwrite to logfile failed in system logger: %m"))); exit(1); } fflush(syslogFile); if (Log_destination & LOG_DESTINATION_STDERR) { fwrite(logbuffer, 1, bytesRead, stderr); fflush(stderr); } continue; } else if (bytesRead < 0 && errno != EINTR) { ereport(COMMERROR, (errcode_for_socket_access(), errmsg("could not read from system logger pipe: %m"))); exit(1); } } if (rc < 0 && errno != EINTR) { ereport(COMMERROR, (errcode_for_socket_access(), errmsg("select() failed in system logger: %m"))); exit(1); } /* * If postmaster died, there's nothing to log any more. * We check this only after pipe timeouts to receive as much as possible * from the pipe. */ if (!PostmasterIsAlive(true)) { if (syslogFile) fclose(syslogFile); exit(1); } } } 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 */ if (!syslogPipe[0]) { if (pgpipe(syslogPipe) < 0) ereport(FATAL, (errcode_for_file_access(), (errmsg("pipe for syslogging not created: %m")))); if (!set_noblock(syslogPipe[1])) { ereport(FATAL, (errcode_for_socket_access(), errmsg("could not set syslogging pipe to nonblocking mode: %m"))); } } now = time(NULL); /* * The initial logfile is created right in the postmaster, * to insure that the logger process has a writable file. */ filename = logfile_getname(now); /* * The file is opened for appending, in case the syslogger * is restarted right after a rotation. */ syslogFile = fopen(filename, "a+"); if (!syslogFile) { /* * if we can't open the syslog file for the syslogger process, * we try to redirect stderr back to have some logging. */ ereport(WARNING, (errcode_for_file_access(), (errmsg("error opening syslog file %s: %m", filename)))); if (realStdErr != NULL) { if (dup2(fileno(realStdErr), fileno(stderr)) < 0) ereport(FATAL, (errcode_for_file_access(), (errmsg("error redirecting stderr to default: %m")))); ereport(FATAL, (errmsg("logfile output corrupted"))); } } pfree(filename); fflush(stdout); fflush(stderr); #ifdef __BEOS__ /* Specific beos actions before backend startup */ beos_before_backend_startup(); #endif #ifdef EXEC_BACKEND switch ((sysloggerPid = syslogger_forkexec())) #else switch ((sysloggerPid = fork())) #endif { case -1: #ifdef __BEOS__ /* Specific beos actions */ beos_backend_startup_failed(); #endif ereport(LOG, (errmsg("could not fork system logger: %m"))); return 0; #ifndef EXEC_BACKEND case 0: /* in postmaster child ... */ #ifdef __BEOS__ /* Specific beos actions after backend startup */ beos_backend_startup(); #endif /* Close the postmaster's sockets */ ClosePostmasterPorts(); /* Drop our connection to postmaster's shared memory, as well */ PGSharedMemoryDetach(); /* do the work */ SysLoggerMain(0, NULL); break; #endif default: /* now we redirect stderr, if not done already */ if (realStdErr == NULL) { int dh= dup(fileno(stderr)); if (dh < 0) ereport(FATAL, (errcode_for_file_access(), (errmsg("stderr duplication failed: %m")))); realStdErr = fdopen(dh, "a"); if (realStdErr == NULL) ereport(FATAL, (errcode_for_file_access(), (errmsg("realstderr reopen failed: %m")))); if (dup2(syslogPipe[1], fileno(stdout)) < 0) ereport(FATAL, (errcode_for_file_access(), (errmsg("stdout pipe redirection failed: %m")))); if (dup2(syslogPipe[1], fileno(stderr)) < 0) ereport(FATAL, (errcode_for_file_access(), (errmsg("stderr pipe redirection failed: %m")))); } /* postmaster will never write the file; close it */ fclose(syslogFile); syslogFile = NULL; return (int) sysloggerPid; } /* we should never reach here */ return 0; } #ifdef EXEC_BACKEND static pid_t syslogger_forkexec() { char *av[10]; int ac = 0, bufc = 0, i; char numbuf[2][32]; av[ac++] = "postgres"; av[ac++] = "-forklog"; av[ac++] = NULL; /* filled in by postmaster_forkexec */ /* postgres_exec_path is not passed by write_backend_variables */ av[ac++] = postgres_exec_path; /* Pipe file ids (those not passed by write_backend_variables) */ snprintf(numbuf[bufc++],32,"%d",syslogPipe[0]); snprintf(numbuf[bufc++],32,"%d",syslogPipe[1]); /* Add to the arg list */ Assert(bufc <= lengthof(pgstatBuf)); for (i = 0; i < bufc; i++) av[ac++] = numbuf[i]; av[ac] = NULL; Assert(ac < lengthof(av)); return postmaster_forkexec(ac, av); } #endif /* -------------------------------- * logfile routines * -------------------------------- */ /* * perform rotation */ bool logfile_rotate(void) { char *filename; pg_time_t now; FILE *fh; now = time(NULL); filename = logfile_getname(now); fh = fopen(filename, "a+"); if (!fh) { /* * if opening the new file fails, the caller is responsible * for taking consequences. */ pfree(filename); return false; } fclose(syslogFile); syslogFile = fh; last_rotation_time = now; /* official opening of the new logfile */ ereport(NOTICE, (errcode(ERRCODE_WARNING), errmsg("Opened new log file %s", filename))); pfree(filename); return true; } /* * creates logfile name using timestamp information */ #define TIMESTAMPPATTERN "%Y-%m-%d_%H%M%S" static char* logfile_getname(pg_time_t timestamp) { char *filetemplate; char *filename; if (is_absolute_path(Log_directory)) { filetemplate = palloc(strlen(Log_directory) + strlen(Log_filename_prefix) + sizeof(TIMESTAMPPATTERN)+10 +2); if (filetemplate) sprintf(filetemplate, "%s/%s%s_%05d.log", Log_directory, Log_filename_prefix, TIMESTAMPPATTERN, 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_%05d.log", DataDir, Log_directory, Log_filename_prefix, TIMESTAMPPATTERN, PostmasterPid); } filename = palloc(MAXPGPATH); if (!filename || !filetemplate) ereport(FATAL, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Out of memory"))); pg_strftime(filename, MAXPGPATH, filetemplate, pg_localtime(×tamp)); pfree(filetemplate); return filename; } /* -------------------------------- * API helper routines * -------------------------------- */ /* * Rotate log file */ bool LogFileRotate(void) { if (!(Log_destination & LOG_DESTINATION_FILE)) { ereport(NOTICE, (errcode(ERRCODE_WARNING), errmsg("no logfile configured; rotation not supported"))); return false; } SendPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE); return true; } /* -------------------------------- * signal handler routines * -------------------------------- */ /* SIGHUP: set flag to reload config file */ static void sigHupHandler(SIGNAL_ARGS) { got_SIGHUP = true; } /* SIGUSR1: set flag to rotate logfile */ static void rotationHandler(SIGNAL_ARGS) { rotation_requested = true; }
/*------------------------------------------------------------------------- * * syslogger.h * Exports from postmaster/syslogger.c. * * Portions Copyright (c) 2004, PostgreSQL Global Development Group * * $PostgreSQL: $ * *------------------------------------------------------------------------- */ #ifndef _SYSLOGGER_H #define _SYSLOGGER_H #include "pgtime.h" /* GUC options */ extern int Log_RotationAge; extern int Log_RotationSize; extern char * Log_directory; extern char * Log_filename_prefix; int SysLogger_Start(void); void SysLoggerMain(int argc, char *argv[]); extern bool LogFileRotate(void); #endif /* _SYSLOGGER_H */
Index: src/include/catalog/pg_proc.h =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/include/catalog/pg_proc.h,v retrieving revision 1.342 diff -u -r1.342 pg_proc.h --- src/include/catalog/pg_proc.h 12 Jul 2004 20:23:53 -0000 1.342 +++ src/include/catalog/pg_proc.h 20 Jul 2004 21:31:56 -0000 @@ -2819,6 +2819,8 @@ DESCR("Terminate a backend process"); DATA(insert OID = 2172 ( pg_cancel_backend PGNSP PGUID 12 f f t f s 1 23 "23" _null_ pg_cancel_backend - _null_ )); DESCR("Cancel running query on a backend process"); +DATA(insert OID = 2173 ( pg_reload_conf PGNSP PGUID 12 f f t f s 1 23 "" _null_ pg_reload_conf - _null_ )); +DESCR("Reload postgresql.conf"); DATA(insert OID = 1946 ( encode PGNSP PGUID 12 f f t f i 2 25 "17 25" _null_ binary_encode - _null_ )); DESCR("Convert bytea value into some ascii-only text string"); @@ -3607,6 +3609,25 @@ DATA(insert OID = 2556 ( pg_tablespace_databases PGNSP PGUID 12 f f t t s 1 26 "26" _null_ pg_tablespace_databases - _null_)); DESCR("returns database oids in a tablespace"); +DATA(insert OID = 2557( pg_file_length PGNSP PGUID 12 f f t f v 1 20 "25" _null_ pg_file_length - _null_ )); +DESCR("length of generic file"); +DATA(insert OID = 2558( pg_file_read PGNSP PGUID 12 f f t f v 3 25 "25 20 20" _null_ pg_file_read - _null_ )); +DESCR("read contents of generic file"); +DATA(insert OID = 2559( pg_file_write PGNSP PGUID 12 f f t f v 3 20 "25 25 16" _null_ pg_file_write - _null_ )); +DESCR("write generic file"); +DATA(insert OID = 2560( pg_file_rename PGNSP PGUID 12 f f t f v 2 16 "25 25" _null_ pg_file_rename - _null_ )); +DESCR("rename generic file"); +DATA(insert OID = 2561( pg_file_rename PGNSP PGUID 12 f f t f v 33 16 "25 25 25" _null_ pg_file_rename - _null_ )); +DESCR("rename generic file"); +DATA(insert OID = 2562( pg_file_unlink PGNSP PGUID 12 f f t f v 1 16 "25" _null_ pg_file_unlink - _null_ )); +DESCR("remove generic file"); +DATA(insert OID = 2563( pg_dir_ls PGNSP PGUID 12 f f t t v 2 25 "25 16" _null_ pg_dir_ls - _null_ )); +DESCR("list generic directory"); + +DATA(insert OID = 2564( pg_logfile_rotate PGNSP PGUID 12 f f t f v 0 16 "" _null_ pg_logfile_rotate - _null_ )); +DESCR("rotate log file"); +DATA(insert OID = 2565( pg_logdir_ls PGNSP PGUID 12 f f t t v 0 2249 "" _null_ pg_logdir_ls - _null_ )); +DESCR("list all available log files"); /* * Symbolic values for provolatile column: these indicate whether the result Index: src/include/storage/pmsignal.h =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/include/storage/pmsignal.h,v retrieving revision 1.9 diff -u -r1.9 pmsignal.h --- src/include/storage/pmsignal.h 19 Jul 2004 02:47:15 -0000 1.9 +++ src/include/storage/pmsignal.h 20 Jul 2004 21:31:57 -0000 @@ -25,7 +25,7 @@ PMSIGNAL_PASSWORD_CHANGE, /* pg_pwd file has changed */ PMSIGNAL_WAKEN_CHILDREN, /* send a SIGUSR1 signal to all backends */ PMSIGNAL_WAKEN_ARCHIVER, /* send a NOTIFY signal to xlog archiver */ - + PMSIGNAL_ROTATE_LOGFILE, /* send SIGUSR1 to syslogger to rotate logfile */ NUM_PMSIGNALS /* Must be last value of enum! */ } PMSignalReason; Index: src/include/utils/builtins.h =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/include/utils/builtins.h,v retrieving revision 1.246 diff -u -r1.246 builtins.h --- src/include/utils/builtins.h 12 Jul 2004 20:23:59 -0000 1.246 +++ src/include/utils/builtins.h 20 Jul 2004 21:31:59 -0000 @@ -362,8 +362,20 @@ extern Datum current_database(PG_FUNCTION_ARGS); extern Datum pg_terminate_backend(PG_FUNCTION_ARGS); extern Datum pg_cancel_backend(PG_FUNCTION_ARGS); +extern Datum pg_reload_conf(PG_FUNCTION_ARGS); extern Datum pg_tablespace_databases(PG_FUNCTION_ARGS); +extern Datum pg_logfile_rotate(PG_FUNCTION_ARGS); +extern Datum pg_logdir_ls(PG_FUNCTION_ARGS); + +extern Datum pg_file_length(PG_FUNCTION_ARGS); +extern Datum pg_file_read(PG_FUNCTION_ARGS); +extern Datum pg_file_write(PG_FUNCTION_ARGS); +extern Datum pg_file_rename(PG_FUNCTION_ARGS); +extern Datum pg_file_unlink(PG_FUNCTION_ARGS); + +extern Datum pg_dir_ls(PG_FUNCTION_ARGS); + /* not_in.c */ extern Datum int4notin(PG_FUNCTION_ARGS); extern Datum oidnotin(PG_FUNCTION_ARGS); Index: src/include/utils/elog.h =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/include/utils/elog.h,v retrieving revision 1.70 diff -u -r1.70 elog.h --- src/include/utils/elog.h 6 Jul 2004 19:51:59 -0000 1.70 +++ src/include/utils/elog.h 20 Jul 2004 21:31:59 -0000 @@ -185,10 +185,10 @@ #define LOG_DESTINATION_STDERR 1 #define LOG_DESTINATION_SYSLOG 2 #define LOG_DESTINATION_EVENTLOG 4 +#define LOG_DESTINATION_FILE 8 /* Other exported functions */ extern void DebugFileOpen(void); - /* * Write errors to stderr (or by equal means when stderr is * not available). Used before ereport/elog can be used Index: src/backend/catalog/system_views.sql =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/catalog/system_views.sql,v retrieving revision 1.6 diff -u -r1.6 system_views.sql --- src/backend/catalog/system_views.sql 26 Apr 2004 15:24:41 -0000 1.6 +++ src/backend/catalog/system_views.sql 20 Jul 2004 21:32:00 -0000 @@ -273,3 +273,8 @@ DO INSTEAD NOTHING; GRANT SELECT, UPDATE ON pg_settings TO PUBLIC; + +CREATE VIEW pg_logdir_ls AS + SELECT * + FROM pg_logdir_ls() AS A + (filetime timestamp, pid int4, filename text); Index: src/backend/postmaster/Makefile =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/postmaster/Makefile,v retrieving revision 1.16 diff -u -r1.16 Makefile --- src/backend/postmaster/Makefile 19 Jul 2004 02:47:08 -0000 1.16 +++ src/backend/postmaster/Makefile 20 Jul 2004 21:32: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: src/backend/postmaster/postmaster.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/postmaster/postmaster.c,v retrieving revision 1.412 diff -u -r1.412 postmaster.c --- src/backend/postmaster/postmaster.c 19 Jul 2004 02:47:08 -0000 1.412 +++ src/backend/postmaster/postmaster.c 20 Jul 2004 21:32:07 -0000 @@ -118,7 +118,7 @@ #include "utils/ps_status.h" #include "bootstrap/bootstrap.h" #include "pgstat.h" - +#include "postmaster/syslogger.h" /* * List of active backends (or child processes anyway; we don't actually @@ -201,6 +201,7 @@ BgWriterPID = 0, PgArchPID = 0, PgStatPID = 0; +pid_t SysLoggerPID = 0; /* Startup/shutdown state */ #define NoShutdown 0 @@ -852,6 +853,12 @@ #endif /* + * start logging to file + */ + + SysLoggerPID = SysLogger_Start(); + + /* * Reset whereToSendOutput from Debug (its starting state) to None. * This prevents ereport from sending log messages to stderr unless * the syslog/stderr switch permits. We don't do this until the @@ -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,9 @@ kill(BgWriterPID, SIGHUP); if (PgArchPID != 0) kill(PgArchPID, SIGHUP); + if (SysLoggerPID != 0) + kill(SysLoggerPID, SIGHUP); + /* PgStatPID does not currently need SIGHUP */ load_hba(); load_ident(); @@ -1835,7 +1850,6 @@ if (PgStatPID != 0) kill(PgStatPID, SIGQUIT); break; - case SIGINT: /* * Fast Shutdown: @@ -1902,6 +1916,7 @@ kill(PgStatPID, SIGQUIT); if (DLGetHead(BackendList)) SignalChildren(SIGQUIT); + ExitPostmaster(0); break; } @@ -2059,6 +2074,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. */ @@ -2956,6 +2980,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 */ } @@ -3012,7 +3046,6 @@ if (Shutdown <= SmartShutdown) SignalChildren(SIGUSR1); } - if (PgArchPID != 0 && Shutdown == NoShutdown) { if (CheckPostmasterSignal(PMSIGNAL_WAKEN_ARCHIVER)) @@ -3024,6 +3057,10 @@ kill(PgArchPID, SIGUSR1); } } + if (CheckPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE) && SysLoggerPID != 0) + { + kill(SysLoggerPID, SIGUSR1); + } PG_SETMASK(&UnBlockSig); Index: src/backend/utils/adt/misc.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/adt/misc.c,v retrieving revision 1.35 diff -u -r1.35 misc.c --- src/backend/utils/adt/misc.c 2 Jul 2004 18:59:22 -0000 1.35 +++ src/backend/utils/adt/misc.c 20 Jul 2004 21:32:08 -0000 @@ -15,6 +15,7 @@ #include "postgres.h" #include <sys/file.h> +#include <unistd.h> #include <signal.h> #include <dirent.h> @@ -26,6 +27,49 @@ #include "funcapi.h" #include "catalog/pg_type.h" #include "catalog/pg_tablespace.h" +#include "postmaster/syslogger.h" + +/*----------------------- + * some helper functions + */ + +/* + * Return an absolute path. Argument may be absolute or + * relative to the DataDir. + */ +static char *absClusterPath(text *arg) +{ + char *filename; + int len=VARSIZE(arg) - VARHDRSZ; + + filename = palloc(len+1); + memcpy(filename, VARDATA(arg), len); + filename[len] = 0; + + if (is_absolute_path(filename)) + return filename; + else + { + char *absname = palloc(strlen(DataDir)+len+2); + sprintf(absname, "%s/%s", DataDir, filename); + pfree(filename); + return absname; + } +} + + +/* + * check for superuser, bark if not. + */ +static void +requireSuperuser(void) +{ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("only superuser may access generic file functions")))); +} + /* @@ -109,18 +153,37 @@ PG_RETURN_INT32(pg_signal_backend(PG_GETARG_INT32(0),SIGINT)); } +Datum +pg_reload_conf(PG_FUNCTION_ARGS) +{ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("only superuser can signal the postmaster")))); + + if (kill(PostmasterPid, SIGHUP)) + { + ereport(WARNING, + (errmsg("failed to send signal to postmaster: %m"))); + + PG_RETURN_INT32(0); + } + + PG_RETURN_INT32(1); +} + typedef struct { char *location; DIR *dirdesc; -} ts_db_fctx; +} directory_fctx; Datum pg_tablespace_databases(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; struct dirent *de; - ts_db_fctx *fctx; + directory_fctx *fctx; if (SRF_IS_FIRSTCALL()) { @@ -130,7 +193,7 @@ funcctx=SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); - fctx = palloc(sizeof(ts_db_fctx)); + fctx = palloc(sizeof(directory_fctx)); /* * size = path length + tablespace dirname length @@ -164,7 +227,7 @@ } funcctx=SRF_PERCALL_SETUP(); - fctx = (ts_db_fctx*) funcctx->user_fctx; + fctx = (directory_fctx*) funcctx->user_fctx; if (!fctx->dirdesc) /* not a tablespace */ SRF_RETURN_DONE(funcctx); @@ -202,3 +265,427 @@ FreeDir(fctx->dirdesc); SRF_RETURN_DONE(funcctx); } + + + +/* ------------------------------------ + * generic file handling functions + */ + +Datum pg_file_length(PG_FUNCTION_ARGS) +{ + struct stat fst; + char *filename; + fst.st_size=0; + + requireSuperuser(); + + filename = absClusterPath(PG_GETARG_TEXT_P(0)); + + if (stat(filename, &fst) < 0) + { + ereport(WARNING, + (errcode_for_file_access(), + errmsg("could not stat file %s: %m", filename))); + + PG_RETURN_INT64(-1); + } + + PG_RETURN_INT64(fst.st_size); +} + + +Datum pg_file_read(PG_FUNCTION_ARGS) +{ + size_t size; + char *buf=0; + size_t nbytes; + int8 pos; + FILE *f; + char *filename; + + requireSuperuser(); + + filename = absClusterPath(PG_GETARG_TEXT_P(0)); + pos = PG_GETARG_INT64(1); + size = PG_GETARG_INT64(2); + + f = fopen(filename, "r"); + if (!f) + { + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not open file %s for reading: %m", filename))); + PG_RETURN_NULL(); + } + + if (pos >= 0) + fseek(f, pos, SEEK_SET); + else + fseek(f, pos, SEEK_END); + + + buf = palloc(size + VARHDRSZ); + + nbytes = fread(VARDATA(buf), 1, size, f); + if (nbytes < 0) + { + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not read file %s: %m", filename))); + PG_RETURN_NULL(); + } + VARATT_SIZEP(buf) = nbytes + VARHDRSZ; + fclose(f); + + PG_RETURN_TEXT_P(buf); +} + + +Datum pg_file_write(PG_FUNCTION_ARGS) +{ + FILE *f; + char *filename; + text *data; + int8 count = 0; + + requireSuperuser(); + + filename = absClusterPath(PG_GETARG_TEXT_P(0)); + data = PG_GETARG_TEXT_P(1); + + if (PG_ARGISNULL(2) || !PG_GETARG_BOOL(2)) + { + struct stat fst; + if (stat(filename, &fst) >= 0) + ereport(ERROR, + (ERRCODE_DUPLICATE_FILE, + errmsg("file %s exists", filename))); + + f = fopen(filename, "w"); + } + else + f = fopen(filename, "a"); + + if (!f) + { + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could open file %s for writing: %m", filename))); + } + + if (VARSIZE(data) != 0) + { + count = fwrite(VARDATA(data), 1, VARSIZE(data) - VARHDRSZ, f); + + if (count != VARSIZE(data)) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("error writing file %s: %m", filename))); + } + fclose(f); + + PG_RETURN_INT64(count); +} + + +Datum pg_file_rename(PG_FUNCTION_ARGS) +{ + char *fn1, *fn2, *fn3; + int rc; + + requireSuperuser(); + + fn1=absClusterPath(PG_GETARG_TEXT_P(0)); + fn2=absClusterPath(PG_GETARG_TEXT_P(1)); + if (PG_ARGISNULL(2)) + fn3=0; + else + fn3=absClusterPath(PG_GETARG_TEXT_P(2)); + + struct stat fst; + if (stat(fn1, &fst) < 0) + { + ereport(WARNING, + (errcode_for_file_access(), + errmsg("could not stat file %s: %m", fn1))); + + PG_RETURN_BOOL(false); + } + + if (fn3 && stat(fn2, &fst) < 0) + { + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not stat file %s: %m", fn2))); + + PG_RETURN_BOOL(false); + } + + + rc = stat(fn3 ? fn3 : fn2, &fst); + if (rc >= 0 || errno != ENOENT) + { + ereport(ERROR, + (ERRCODE_DUPLICATE_FILE, + errmsg("cannot rename: target file %s exists", fn3 ? fn3 : fn2))); + } + + if (fn3) + { + if (rename(fn2, fn3) != 0) + { + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not rename %s to %s: %m", fn2, fn3))); + } + if (rename(fn1, fn2) != 0) + { + ereport(WARNING, + (errcode_for_file_access(), + errmsg("could not rename %s to %s: %m", fn1, fn2))); + + if (rename(fn3, fn2) != 0) + { + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not rename %s back to %s: %m", fn3, fn2))); + } + else + { + ereport(ERROR, + (ERRCODE_UNDEFINED_FILE, + errmsg("renaming %s to %s was reverted", fn2, fn3))); + + } + } + } + if (rename(fn1, fn2) != 0) + { + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not rename %s to %s: %m", fn1, fn2))); + } + + PG_RETURN_BOOL(true); +} + + +Datum pg_file_unlink(PG_FUNCTION_ARGS) +{ + char *filename; + + requireSuperuser(); + + filename = absClusterPath(PG_GETARG_TEXT_P(0)); + + if (unlink(filename) < 0) + { + ereport(WARNING, + (errcode_for_file_access(), + errmsg("could not unlink file %s", filename))); + + PG_RETURN_BOOL(false); + } + PG_RETURN_BOOL(true); +} + + +Datum pg_dir_ls(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + struct dirent *de; + directory_fctx *fctx; + + requireSuperuser(); + + if (SRF_IS_FIRSTCALL()) + { + MemoryContext oldcontext; + + funcctx=SRF_FIRSTCALL_INIT(); + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + fctx = palloc(sizeof(directory_fctx)); + fctx->location = absClusterPath(PG_GETARG_TEXT_P(0)); + + fctx->dirdesc = AllocateDir(fctx->location); + + if (!fctx->dirdesc) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("%s is not browsable: %m", fctx->location))); + + if (PG_ARGISNULL(1) || !PG_GETARG_BOOL(1)) + { + pfree(fctx->location); + fctx->location = 0; + } + funcctx->user_fctx = fctx; + MemoryContextSwitchTo(oldcontext); + } + + funcctx=SRF_PERCALL_SETUP(); + fctx = (directory_fctx*) funcctx->user_fctx; + + if (!fctx->dirdesc) /* not a readable directory */ + SRF_RETURN_DONE(funcctx); + + while ((de = readdir(fctx->dirdesc)) != NULL) + { + char *name; + text *result; + int len; + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + if (fctx->location) + { + char *path=palloc(strlen(fctx->location) + strlen(de->d_name) +2); + sprintf(path, "%s/%s", fctx->location, de->d_name); + + name = path; + } + else + name = de->d_name; + + + len = strlen(name); + result = palloc(len + VARHDRSZ); + VARATT_SIZEP(result) = len + VARHDRSZ; + memcpy(VARDATA(result), name, len); + + SRF_RETURN_NEXT(funcctx, PointerGetDatum(result)); + } + + FreeDir(fctx->dirdesc); + SRF_RETURN_DONE(funcctx); +} + + +/* + * logfile handling functions + */ + + +Datum pg_logfile_rotate(PG_FUNCTION_ARGS) +{ + requireSuperuser(); + + PG_RETURN_BOOL(LogFileRotate()); +} + + +Datum pg_logdir_ls(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + struct dirent *de; + directory_fctx *fctx; + + requireSuperuser(); + + if (SRF_IS_FIRSTCALL()) + { + MemoryContext oldcontext; + TupleDesc tupdesc; + + funcctx=SRF_FIRSTCALL_INIT(); + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + fctx = palloc(sizeof(directory_fctx)); + + if (is_absolute_path(Log_directory)) + fctx->location = Log_directory; + else + { + fctx->location = palloc(strlen(DataDir) + strlen(Log_directory) +2); + sprintf(fctx->location, "%s/%s", DataDir, Log_directory); + } + + tupdesc = CreateTemplateTupleDesc(3, false); + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "starttime", + TIMESTAMPOID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "pid", + INT4OID, -1, 0); + TupleDescInitEntry(tupdesc, (AttrNumber) 3, "filename", + TEXTOID, -1, 0); + + funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc); + + fctx->dirdesc = AllocateDir(fctx->location); + + if (!fctx->dirdesc) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("%s is not browsable: %m", fctx->location))); + + funcctx->user_fctx = fctx; + MemoryContextSwitchTo(oldcontext); + } + + funcctx=SRF_PERCALL_SETUP(); + fctx = (directory_fctx*) funcctx->user_fctx; + + if (!fctx->dirdesc) /* not a readable directory */ + SRF_RETURN_DONE(funcctx); + + while ((de = readdir(fctx->dirdesc)) != NULL) + { + char *values[3]; + HeapTuple tuple; + int prefixLen=strlen(Log_filename_prefix); + + char *field[MAXDATEFIELDS]; + char lowstr[MAXDATELEN + 1]; + int dtype; + int nf, ftype[MAXDATEFIELDS]; + fsec_t fsec; + int tz = 0; + struct pg_tm date; + int i; + + /* + * format as created in logfile_getname(): + * prefix_YYYY-MM-DD_HHMMSS_PPPPP.log + * prefixLen ^ + * prefixLen+17 ^ + * prefixLen+23 ^ + */ + + if (strlen(de->d_name) != prefixLen + 27 + || memcmp(de->d_name, Log_filename_prefix, prefixLen) + || de->d_name[prefixLen + 17] != '_' + || strcmp(de->d_name + prefixLen + 23, ".log")) + continue; + + values[2] = palloc(strlen(fctx->location) + strlen(de->d_name) + 2); + sprintf(values[2], "%s/%s", fctx->location, de->d_name); + + values[0] = de->d_name + prefixLen; /* timestamp */ + values[0][17] = 0; + + values[1] = de->d_name + prefixLen + 18; /* pid */ + values[1][5] = 0; + + /* check if pid is purely numeric as expected */ + for (i = 0 ; i < 5 ; i++) + if (!isdigit(values[0][i])) + continue; + + /* parse and decode expected timestamp */ + if (ParseDateTime(values[0], lowstr, field, ftype, MAXDATEFIELDS, &nf)) + continue; + + if (DecodeDateTime(field, ftype, nf, &dtype, &date, &fsec, &tz)) + continue; + + /* Seems the format fits the expected format; feed it into the tuple */ + + + tuple = BuildTupleFromCStrings(funcctx->attinmeta, values); + + SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); + } + + FreeDir(fctx->dirdesc); + SRF_RETURN_DONE(funcctx); +} Index: src/backend/utils/error/elog.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/error/elog.c,v retrieving revision 1.142 diff -u -r1.142 elog.c --- src/backend/utils/error/elog.c 24 Jun 2004 21:03:13 -0000 1.142 +++ src/backend/utils/error/elog.c 20 Jul 2004 21:32:11 -0000 @@ -84,6 +84,10 @@ static void write_eventlog(int level, const char *line); #endif +/* in syslogger.c */ +extern FILE *syslogFile; +extern FILE *realStdErr; +extern pid_t SysLoggerPID; /* * ErrorData holds the data accumulated during any one ereport() cycle. * Any non-NULL pointers must point to palloc'd data in ErrorContext. @@ -1451,10 +1455,31 @@ write_eventlog(eventlog_level, buf.data); } #endif /* WIN32 */ - /* Write to stderr, if enabled */ - if ((Log_destination & LOG_DESTINATION_STDERR) || whereToSendOutput == Debug) + + /* + * Write to stderr. If Log_destination is file or stderr + * if file is target, the logger process will handle this + */ + if ((Log_destination & (LOG_DESTINATION_STDERR | LOG_DESTINATION_FILE)) + || whereToSendOutput == Debug) { - fprintf(stderr, "%s", buf.data); + if (SysLoggerPID == MyProcPid && realStdErr != 0) + { + /* + * If realStdErr is not null in the SysLogger process, + * there's something really wrong because stderr is probably + * redirected to the pipe. To avoid circular writes, we + * write to realStdErr which is hopefully the stderr the postmaster + * was started with. + */ + fprintf(realStdErr, "%s", buf.data); + } + else + fprintf(stderr, "%s", buf.data) ; + + /* syslogFile is open in SysLogger only */ + if (syslogFile != 0) + fprintf(syslogFile, "%s", buf.data) ; } pfree(buf.data); Index: src/backend/utils/misc/guc.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/misc/guc.c,v retrieving revision 1.221 diff -u -r1.221 guc.c --- src/backend/utils/misc/guc.c 19 Jul 2004 21:39:47 -0000 1.221 +++ src/backend/utils/misc/guc.c 20 Jul 2004 21:32:20 -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 @@ -1627,13 +1645,32 @@ { {"log_destination", PGC_POSTMASTER, LOGGING_WHERE, gettext_noop("Sets the target for log output."), - gettext_noop("Valid values are combinations of stderr, syslog " + gettext_noop("Valid values are combinations of stderr, file, syslog " "and eventlog, depending on platform."), GUC_LIST_INPUT }, &log_destination_string, "stderr", assign_log_destination, NULL }, + { + {"log_directory", PGC_SIGHUP, LOGGING_WHERE, + gettext_noop("Sets the target directory for log output."), + gettext_noop("May be specified as relative to the cluster directory " + "or as absolute path."), + GUC_LIST_INPUT | GUC_REPORT + }, + &Log_directory, + "pg_log", NULL, NULL + }, + { + {"log_filename_prefix", PGC_SIGHUP, LOGGING_WHERE, + gettext_noop("prefix for logfile names created in the log_directory."), + NULL, + GUC_LIST_INPUT | GUC_REPORT + }, + &Log_filename_prefix, + "postgresql-", NULL, NULL + }, #ifdef HAVE_SYSLOG { @@ -5079,6 +5116,8 @@ if (pg_strcasecmp(tok,"stderr") == 0) newlogdest |= LOG_DESTINATION_STDERR; + else if (pg_strcasecmp(tok,"file") == 0) + newlogdest |= LOG_DESTINATION_FILE; #ifdef HAVE_SYSLOG else if (pg_strcasecmp(tok,"syslog") == 0) newlogdest |= LOG_DESTINATION_SYSLOG; Index: src/backend/utils/misc/postgresql.conf.sample =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/misc/postgresql.conf.sample,v retrieving revision 1.116 diff -u -r1.116 postgresql.conf.sample --- src/backend/utils/misc/postgresql.conf.sample 19 Jul 2004 02:47:10 -0000 1.116 +++ src/backend/utils/misc/postgresql.conf.sample 20 Jul 2004 21:32:21 -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'
---------------------------(end of broadcast)--------------------------- TIP 8: explain analyze is your friend