Index: src/backend/postmaster/postmaster.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/postmaster/postmaster.c,v retrieving revision 1.405 diff -u -r1.405 postmaster.c --- src/backend/postmaster/postmaster.c 24 Jun 2004 21:02:55 -0000 1.405 +++ src/backend/postmaster/postmaster.c 6 Jul 2004 22:12:22 -0000 @@ -729,6 +729,11 @@ reset_shared(PostPortNumber); /* + * Opens alternate log file + */ + LogFileInit(); + + /* * Estimate number of openable files. This must happen after setting * up semaphores, because on some platforms semaphores count as open * files. 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 6 Jul 2004 22:12:34 -0000 @@ -202,3 +202,137 @@ FreeDir(fctx->dirdesc); SRF_RETURN_DONE(funcctx); } + + +extern FILE *logfile; // in elog.c +#define MAXLOGFILECHUNK 50000 + +static char *absClusterPath(text *arg) +{ + char *filename; + + if (is_absolute_path(VARDATA(arg))) + filename=VARDATA(arg); + else + { + filename = palloc(strlen(DataDir)+VARSIZE(arg)+2); + sprintf(filename, "%s/%s", DataDir, VARDATA(arg)); + } + return filename; +} + + +Datum pg_logfile_get(PG_FUNCTION_ARGS) +{ + size_t size=MAXLOGFILECHUNK; + char *buf=0; + size_t nbytes; + FILE *f; + + if (!PG_ARGISNULL(0)) + size = PG_GETARG_INT32(0); + if (size > MAXLOGFILECHUNK) + { + size = MAXLOGFILECHUNK; + ereport(WARNING, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("Maximum size is %d.", size))); + } + + if (PG_ARGISNULL(2)) + f = logfile; + else + { + /* explicitely named logfile */ + char *filename = absClusterPath(PG_GETARG_TEXT_P(2)); + f = fopen(filename, "r"); + if (!f) + { + ereport(WARNING, + (errcode_for_file_access(), + errmsg("file not found %s", filename))); + PG_RETURN_NULL(); + } + } + + if (f) + { + + if (PG_ARGISNULL(1)) + fseek(f, -size, SEEK_END); + else + { + long pos = PG_GETARG_INT32(1); + if (pos >= 0) + fseek(f, pos, SEEK_SET); + else + fseek(f, pos, SEEK_END); + } + buf = palloc(size+1); + nbytes = fread(buf, 1, size, f); + buf[nbytes] = 0; + + fseek(f, 0, SEEK_END); + + if (!PG_ARGISNULL(2)) + fclose(f); + } + + if (buf) + PG_RETURN_CSTRING(buf); + else + PG_RETURN_NULL(); +} + + +Datum pg_logfile_length(PG_FUNCTION_ARGS) +{ + if (PG_ARGISNULL(0)) + { + if (logfile) + { + fflush(logfile); + PG_RETURN_INT32(ftell(logfile)); + } + } + else + { + struct stat fst; + fst.st_size=0; + stat(absClusterPath(PG_GETARG_TEXT_P(0)), &fst); + + PG_RETURN_INT32(fst.st_size); + } + PG_RETURN_INT32(0); +} + + +Datum pg_logfile_name(PG_FUNCTION_ARGS) +{ + char *filename=LogFileName(); + if (filename) + { + if (strncmp(filename, DataDir, strlen(DataDir))) + PG_RETURN_CSTRING(filename); + else + PG_RETURN_CSTRING(filename+strlen(DataDir)+1); + } + PG_RETURN_NULL(); +} + + +Datum pg_logfile_rotate(PG_FUNCTION_ARGS) +{ + char *renamedFile = LogFileRotate(); + + if (renamedFile) + { + if (strncmp(renamedFile, DataDir, strlen(DataDir))) + PG_RETURN_CSTRING(renamedFile); + else + PG_RETURN_CSTRING(renamedFile+strlen(DataDir)+1); + } + else + PG_RETURN_NULL(); +} + 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 6 Jul 2004 22:12:37 -0000 @@ -63,7 +63,6 @@ #include "utils/memutils.h" #include "utils/guc.h" - /* Global variables */ ErrorContextCallback *error_context_stack = NULL; @@ -71,9 +70,17 @@ PGErrorVerbosity Log_error_verbosity = PGERROR_VERBOSE; char *Log_line_prefix = NULL; /* format for extra log line info */ unsigned int Log_destination = LOG_DESTINATION_STDERR; +char *Log_filename = NULL; bool in_fatal_exit = false; +FILE *logfile = NULL; /* the logfile we're writing to */ +static char logfileName[MAXPGPATH]; /* current filename */ +static pg_time_t logfileTimestamp=0; /* logfile version this backend is currently using */ + +static pg_time_t *globalLogfileTimestamp = NULL; /* logfile version the backend should be using (shared mem) */ + + #ifdef HAVE_SYSLOG char *Syslog_facility; /* openlog() parameters */ char *Syslog_ident; @@ -140,6 +147,9 @@ static const char *error_severity(int elevel); static void append_with_tabs(StringInfo buf, const char *str); +static char *logfile_getname(pg_time_t timestamp); +static void logfile_reopen(void); + /* * errstart --- begin an error-reporting cycle @@ -931,11 +941,181 @@ /* * And let errfinish() finish up. */ + errfinish(0); } /* + * Initialize shared mem for logfile rotation + */ + +void +LogFileInit(void) +{ + if (!globalLogfileTimestamp && Log_filename && (Log_destination & LOG_DESTINATION_FILE)) + { + /* allocate logfile version shared memory segment for rotation signaling */ + globalLogfileTimestamp = ShmemAlloc(sizeof(pg_time_t)); + if (!globalLogfileTimestamp) + { + ereport(FATAL, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("Out of shared memory"))); + return; + } + + *globalLogfileTimestamp = time(NULL); + + /* open logfile after we successfully initialized */ + logfile_reopen(); + } +} + + +/* + * Rotate log file + */ +char * +LogFileRotate(void) +{ + char *filename; + char *oldFilename; + pg_time_t now; + + if (!globalLogfileTimestamp || !logfileName || !(Log_destination & LOG_DESTINATION_FILE)) + return NULL; + + now = time(NULL); + + filename = logfile_getname(now); + if (!filename) + return NULL; + + if (!strcmp(filename, logfileName)) + { + ereport(ERROR, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("Log_filename not suitable for rotation."))); + return NULL; + } + + oldFilename = pstrdup(logfileName); + *globalLogfileTimestamp = now; + + ereport(NOTICE, + (errcode(ERRCODE_WARNING), + errmsg("Opened new log file %s; previous logfile %s", filename, oldFilename))); + + return oldFilename; +} + + +/* + * return current log file name + */ +char* +LogFileName(void) +{ + if (logfileName) + return pstrdup(logfileName); + return NULL; +} + + +/* + * check if logfile has to be reopened + * if called from ProcessConfigFile after SIGHUP, also check for filename template change + */ +void LogFileCheckReopen(bool fromSIGHUP) +{ + if (globalLogfileTimestamp) + { + if (*globalLogfileTimestamp != logfileTimestamp) + { + /* sanity check: if it's in the future, shmem probably corrupted */ + pg_time_t now=time(NULL); + if (*globalLogfileTimestamp > now) + *globalLogfileTimestamp = now; + + logfile_reopen(); + } + else if (fromSIGHUP) + { + char *filename = logfile_getname(logfileTimestamp); + if (filename && strcmp(filename, logfileName)) + { + /* template for logfile was changed */ + logfile_reopen(); + pfree(filename); + } + } + } +} + + +/* + * creates logfile name using timestamp information + */ +static char* +logfile_getname(pg_time_t timestamp) +{ + char *filetemplate; + char *filename; + + if (is_absolute_path(Log_filename)) + filetemplate = pstrdup(Log_filename); + else + { + filetemplate = palloc(strlen(DataDir) + strlen(Log_filename) + 2); + sprintf(filetemplate, "%s/%s", DataDir, Log_filename); + } + filename = palloc(MAXPGPATH); + pg_strftime(filename, MAXPGPATH, filetemplate, pg_localtime(×tamp)); + + pfree(filetemplate); + + return filename; +} + + +/* + * reopen log file. + */ +static void +logfile_reopen(void) +{ + if (logfile) + { + fclose(logfile); + logfile = NULL; + } + + if ((Log_destination & LOG_DESTINATION_FILE) && globalLogfileTimestamp) + { + char *fn; + + logfileTimestamp = *globalLogfileTimestamp; + + fn=logfile_getname(logfileTimestamp); + + if (fn) + { + logfile = fopen(fn, "a+"); + + if (!logfile) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("failed to open log file %s", fn))); + + strcpy(logfileName, fn); + pfree(fn); + } + } +} + + +/* * Initialization of error output file */ void @@ -1455,6 +1635,20 @@ if ((Log_destination & LOG_DESTINATION_STDERR) || whereToSendOutput == Debug) { fprintf(stderr, "%s", buf.data); + } + + /* Write to file, if enabled */ + if (logfile && (Log_destination & LOG_DESTINATION_FILE)) + { + /* check if logfile changed */ + LogFileCheckReopen(false); + + if (logfile) + { + fseek(logfile, 0, SEEK_END); + fprintf(logfile, "%s", buf.data); + fflush(logfile); + } } pfree(buf.data); Index: src/backend/utils/misc/guc-file.l =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/misc/guc-file.l,v retrieving revision 1.22 diff -u -r1.22 guc-file.l --- src/backend/utils/misc/guc-file.l 26 May 2004 15:07:38 -0000 1.22 +++ src/backend/utils/misc/guc-file.l 6 Jul 2004 22:12:38 -0000 @@ -276,6 +276,9 @@ set_config_option(item->name, item->value, context, PGC_S_FILE, false, true); + if (context == PGC_SIGHUP) + LogFileCheckReopen(true); + cleanup_exit: free_name_value_list(head); return; Index: src/backend/utils/misc/guc.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/utils/misc/guc.c,v retrieving revision 1.213 diff -u -r1.213 guc.c --- src/backend/utils/misc/guc.c 5 Jul 2004 23:14:14 -0000 1.213 +++ src/backend/utils/misc/guc.c 6 Jul 2004 22:12:46 -0000 @@ -76,6 +76,8 @@ static const char *assign_log_destination(const char *value, bool doit, GucSource source); +extern char *Log_filename; + #ifdef HAVE_SYSLOG extern char *Syslog_facility; extern char *Syslog_ident; @@ -1606,13 +1608,23 @@ { {"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_filename", PGC_SIGHUP, LOGGING_WHERE, + gettext_noop("Sets the target filename for log output."), + gettext_noop("May be specified as relative to the cluster directory " + "or as absolute path."), + GUC_LIST_INPUT | GUC_REPORT + }, + &Log_filename, + "postgresql.log.%Y-%m-%d_%H%M%S", NULL, NULL + }, #ifdef HAVE_SYSLOG { @@ -5033,6 +5045,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.113 diff -u -r1.113 postgresql.conf.sample --- src/backend/utils/misc/postgresql.conf.sample 7 Apr 2004 05:05:50 -0000 1.113 +++ src/backend/utils/misc/postgresql.conf.sample 6 Jul 2004 22:12:47 -0000 @@ -147,9 +147,12 @@ # - Where to Log - -#log_destination = 'stderr' # Valid values are combinations of stderr, +#log_destination = 'stderr' # Valid values are combinations of stderr, file, # syslog and eventlog, depending on # platform. +#log_filename = 'postgresql.log.%Y-%m-%d_%H%M%S' # filename if + # 'file' log_destination is used. + #syslog_facility = 'LOCAL0' #syslog_ident = 'postgres' Index: src/include/catalog/pg_proc.h =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/include/catalog/pg_proc.h,v retrieving revision 1.341 diff -u -r1.341 pg_proc.h --- src/include/catalog/pg_proc.h 2 Jul 2004 22:49:48 -0000 1.341 +++ src/include/catalog/pg_proc.h 6 Jul 2004 22:13:02 -0000 @@ -3595,6 +3595,14 @@ 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_logfile_get PGNSP PGUID 12 f f f f v 3 2275 "23 23 25" _null_ pg_logfile_get - _null_ )); +DESCR("return log file contents"); +DATA(insert OID = 2558( pg_logfile_length PGNSP PGUID 12 f f f f v 1 23 "25" _null_ pg_logfile_length - _null_ )); +DESCR("name of log file"); +DATA(insert OID = 2559( pg_logfile_name PGNSP PGUID 12 f f f f v 0 2275 "" _null_ pg_logfile_name - _null_ )); +DESCR("length of log file"); +DATA(insert OID = 2560( pg_logfile_rotate PGNSP PGUID 12 f f f f v 0 2275 "" _null_ pg_logfile_rotate - _null_ )); +DESCR("rotate log file"); /* * Symbolic values for provolatile column: these indicate whether the result Index: src/include/utils/builtins.h =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/include/utils/builtins.h,v retrieving revision 1.245 diff -u -r1.245 builtins.h --- src/include/utils/builtins.h 2 Jul 2004 18:59:25 -0000 1.245 +++ src/include/utils/builtins.h 6 Jul 2004 22:13:05 -0000 @@ -358,6 +358,11 @@ extern Datum pg_cancel_backend(PG_FUNCTION_ARGS); extern Datum pg_tablespace_databases(PG_FUNCTION_ARGS); +extern Datum pg_logfile_get(PG_FUNCTION_ARGS); +extern Datum pg_logfile_length(PG_FUNCTION_ARGS); +extern Datum pg_logfile_name(PG_FUNCTION_ARGS); +extern Datum pg_logfile_rotate(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.69 diff -u -r1.69 elog.h --- src/include/utils/elog.h 24 Jun 2004 21:03:42 -0000 1.69 +++ src/include/utils/elog.h 6 Jul 2004 22:13:05 -0000 @@ -182,10 +182,14 @@ #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); - +extern void LogFileInit(void); +extern void LogFileCheckReopen(bool fromSIGHUP); +extern char *LogFileRotate(void); +extern char *LogFileName(void); /* * Write errors to stderr (or by equal means when stderr is * not available). Used before ereport/elog can be used