Index: doc/src/sgml/func.sgml =================================================================== RCS file: /projects/cvsroot/pgsql-server/doc/src/sgml/func.sgml,v retrieving revision 1.211 diff -u -r1.211 func.sgml --- doc/src/sgml/func.sgml 25 Jun 2004 17:20:21 -0000 1.211 +++ doc/src/sgml/func.sgml 28 Jun 2004 10:35:09 -0000 @@ -7430,6 +7430,80 @@ + pg_logfile_get + + + pg_logfile_length + + + pg_logfile_name + + + pg_logfile_rotate + + + The functions shown in + deal with the server log file if configured with log_destination + file. + + + + Server Logfile Functions + + + Name Return Type Description + + + + + pg_logfile_get(size_int4, + offset_int4,filename_text) + cstring + get a part of the current server log file + + + pg_logfile_length(filename_text) + int4 + return the current length of the server log file + + + pg_logfile_rotate() + cstring + rotates the server log file and returns the new log file + name + + + pg_logfile_name() + cstring + returns the current server log file name + + + pg_logfile_rotate() + cstring + rotates the server log file and returns the previous log file + name + + + +
+ +The pg_logfile_get function will return the + contents of the current server log file, limited by the size + parameter. If size is NULL, a server internal limit (currently + 50000) is applied. The position parameter specifies the + starting position of the server log chunk to be returned. A + positive number or 0 will be counted from the start of the file, + a negative number from the end; if NULL, -size is assumed + (i.e. the tail of the log file). + + +Both pg_logfile_get and + pg_logfile_length have a filename + parameter which may specify the logfile to examine or the + current logfile if NULL. + + + pg_cancel_backend Index: doc/src/sgml/runtime.sgml =================================================================== RCS file: /projects/cvsroot/pgsql-server/doc/src/sgml/runtime.sgml,v retrieving revision 1.268 diff -u -r1.268 runtime.sgml --- doc/src/sgml/runtime.sgml 27 Jun 2004 22:58:19 -0000 1.268 +++ doc/src/sgml/runtime.sgml 28 Jun 2004 10:35:19 -0000 @@ -1721,14 +1721,25 @@ PostgreSQL supports several methods - for loggning, including stderr and - syslog. On Windows, - eventlog is also supported. Set this + for logging, including stderr, + file> and syslog. + On Windows, eventlog is also supported. Set this option to a list of desired log destinations separated by a comma. The default is to log to stderr only. This option must be set at server start. + + + + log_filename (string) + + + This option sets the target filename for the log destination + file option. It may be specified as absolute + path or relative to the cluster directory. + + 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 28 Jun 2004 10:35:26 -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.34 diff -u -r1.34 misc.c --- src/backend/utils/adt/misc.c 2 Jun 2004 21:29:29 -0000 1.34 +++ src/backend/utils/adt/misc.c 28 Jun 2004 10:35:33 -0000 @@ -103,3 +103,138 @@ { PG_RETURN_INT32(pg_signal_backend(PG_GETARG_INT32(0),SIGINT)); } + + + + +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 28 Jun 2004 10:35:36 -0000 @@ -63,7 +63,6 @@ #include "utils/memutils.h" #include "utils/guc.h" - /* Global variables */ ErrorContextCallback *error_context_stack = NULL; @@ -71,9 +70,18 @@ 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 long logfileVersion=0; /* logfile version this backend is currently using */ + +static FILE *globalLogfileNameFH = NULL; /* this file contains the current logfile name */ +static long *globalLogfileVersion = NULL; /* logfile version the backend should be using (shared mem) */ + + #ifdef HAVE_SYSLOG char *Syslog_facility; /* openlog() parameters */ char *Syslog_ident; @@ -140,6 +148,11 @@ static const char *error_severity(int elevel); static void append_with_tabs(StringInfo buf, const char *str); +static char *logfile_newname(void); +static void logfile_reopen(void); +static char *logfile_readname(void); +static bool logfile_writename(char *fn); + /* * errstart --- begin an error-reporting cycle @@ -931,11 +944,216 @@ /* * And let errfinish() finish up. */ + errfinish(0); } /* + * Initialize shared mem for logfile rotation + */ + +void +LogFileInit(void) +{ + if (!globalLogfileVersion && Log_filename && (Log_destination & LOG_DESTINATION_FILE)) + { + char buffer[MAXPGPATH]; + char *fn = logfile_newname(); + if (fn) + { + /* create file for logfilename distribution and write initial filename */ + snprintf(buffer, sizeof(buffer), "%s/global/pglogfile.name", DataDir); + + globalLogfileNameFH = fopen(buffer, "w+"); + if (!globalLogfileNameFH) + ereport(FATAL, + (errcode_for_file_access(), + errmsg("Logfile distribution file %s couldn't be opened.", buffer))); + + if (!logfile_writename(fn)) + return; + + /* allocate logfile version shared memory segment for rotation signaling */ + globalLogfileVersion = ShmemAlloc(sizeof(long)); + if (!globalLogfileVersion) + { + ereport(FATAL, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("Out of shared memory"))); + return; + } + + *globalLogfileVersion = 0; + + + /* open logfile after we successfully initialized */ + logfile_reopen(); + pfree(fn); + } + } +} + + +/* + * Rotate log file + */ +char * +LogFileRotate(void) +{ + char *filename; + char *oldFilename; + + if (!globalLogfileVersion || !logfileName || !(Log_destination & LOG_DESTINATION_FILE)) + return NULL; + + filename = logfile_newname(); + 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); + + if (logfile_writename(filename)) + { + *globalLogfileVersion++; + + ereport(NOTICE, + (errcode(ERRCODE_WARNING), + errmsg("Opened new log file %s; previous logfile %s", filename, oldFilename))); + + return oldFilename; + } + + return NULL; +} + + +/* + * return current log file name + */ +char* +LogFileName(void) +{ + if (logfileName) + return pstrdup(logfileName); + return NULL; +} + + +/* + * creates a new logfile name using current timestamp information + */ +static char* +logfile_newname() +{ + char *filetemplate; + char *filename; + pg_time_t now = time(NULL); + + 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(&now)); + + pfree(filetemplate); + + return filename; +} + + +/* + * return current global logfile name + */ +static char* +logfile_readname(void) +{ + if (globalLogfileNameFH) + { + char *fn=palloc(MAXPGPATH); + + fseek(globalLogfileNameFH, 0, SEEK_SET); + if (fread(fn, 1, MAXPGPATH, globalLogfileNameFH) != MAXPGPATH) + ereport(FATAL, + (errcode_for_file_access(), + errmsg("Logfile distribution file couldn't be read."))); + + fn[MAXPGPATH-1]=0; /* in case file was corrupted, making sure we're not longer than MAXPGPATH */ + + return fn; + } + return NULL; +} + + +/* + * write global logfile name + */ +static bool +logfile_writename(char *fn) +{ + char buffer[MAXPGPATH]; + memset(buffer, 0, sizeof(buffer)); + strcpy(buffer, fn); + + fseek(globalLogfileNameFH, 0, SEEK_SET); + if (fwrite(buffer, 1, sizeof(buffer), globalLogfileNameFH) != MAXPGPATH) + { + ereport(FATAL, + (errcode_for_file_access(), + errmsg("Logfile distribution file couldn't be written."))); + return false; + } + + return true; +} + + +/* + * reopen log file. + */ +static void +logfile_reopen(void) +{ + if (logfile) + { + fclose(logfile); + logfile = NULL; + } + + if ((Log_destination & LOG_DESTINATION_FILE) && globalLogfileVersion && globalLogfileNameFH) + { + char *fn=logfile_readname(); + logfileVersion = *globalLogfileVersion; + 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 +1673,24 @@ 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 */ + if (globalLogfileVersion && *globalLogfileVersion != logfileVersion) + { + logfileVersion = *globalLogfileVersion; + logfile_reopen(); + } + + if (logfile) + { + fseek(logfile, 0, SEEK_END); + fprintf(logfile, "%s", buf.data); + fflush(logfile); + } } 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.211 diff -u -r1.211 guc.c --- src/backend/utils/misc/guc.c 11 Jun 2004 03:54:54 -0000 1.211 +++ src/backend/utils/misc/guc.c 28 Jun 2004 10:35:45 -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; @@ -1644,13 +1646,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 | GUC_REPORT }, &log_destination_string, "stderr", assign_log_destination, NULL }, + { + {"log_filename", PGC_POSTMASTER, 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 { @@ -4779,6 +4791,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 28 Jun 2004 10:35:45 -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.339 diff -u -r1.339 pg_proc.h --- src/include/catalog/pg_proc.h 25 Jun 2004 17:20:28 -0000 1.339 +++ src/include/catalog/pg_proc.h 28 Jun 2004 10:36:00 -0000 @@ -3595,6 +3595,18 @@ DATA(insert OID = 2243 ( bit_or PGNSP PGUID 12 t f f f i 1 1560 "1560" _null_ aggregate_dummy - _null_)); DESCR("bitwise-or bit aggregate"); +DATA(insert OID = 2546( int2array_int2vector_eq PGNSP PGUID 12 f f t f i 2 16 "1005 22" _null_ int2array_int2vector_eq - _null_ )); +DESCR("int2array int2vector equal"); + +DATA(insert OID = 2550( 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 = 2551( 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 = 2552( 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 = 2553( 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 * of a function is dependent *only* on the values of its explicit arguments, Index: src/include/utils/builtins.h =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/include/utils/builtins.h,v retrieving revision 1.244 diff -u -r1.244 builtins.h --- src/include/utils/builtins.h 25 Jun 2004 17:20:29 -0000 1.244 +++ src/include/utils/builtins.h 28 Jun 2004 10:36:03 -0000 @@ -356,6 +356,10 @@ 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_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); 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 28 Jun 2004 10:36:04 -0000 @@ -182,9 +182,13 @@ #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 char *LogFileRotate(void); +extern char *LogFileName(void); /* * Write errors to stderr (or by equal means when stderr is