Index: doc/src/sgml/func.sgml =================================================================== RCS file: /projects/cvsroot/pgsql-server/doc/src/sgml/func.sgml,v retrieving revision 1.206 diff -u -r1.206 func.sgml --- doc/src/sgml/func.sgml 2 Jun 2004 21:34:49 -0000 1.206 +++ doc/src/sgml/func.sgml 13 Jun 2004 15:57:06 -0000 @@ -7308,6 +7308,80 @@ columns do not have OIDs of their own. + + 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. + + Index: doc/src/sgml/runtime.sgml =================================================================== RCS file: /projects/cvsroot/pgsql-server/doc/src/sgml/runtime.sgml,v retrieving revision 1.266 diff -u -r1.266 runtime.sgml --- doc/src/sgml/runtime.sgml 10 Jun 2004 22:26:17 -0000 1.266 +++ doc/src/sgml/runtime.sgml 13 Jun 2004 15:57:17 -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.403 diff -u -r1.403 postmaster.c --- src/backend/postmaster/postmaster.c 11 Jun 2004 03:54:43 -0000 1.403 +++ src/backend/postmaster/postmaster.c 13 Jun 2004 15:57:24 -0000 @@ -727,6 +727,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/storage/ipc/ipc.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/storage/ipc/ipc.c,v retrieving revision 1.87 diff -u -r1.87 ipc.c --- src/backend/storage/ipc/ipc.c 12 Dec 2003 18:45:09 -0000 1.87 +++ src/backend/storage/ipc/ipc.c 13 Jun 2004 15:57:24 -0000 @@ -111,6 +111,8 @@ on_proc_exit_list[on_proc_exit_index].arg); elog(DEBUG3, "exit(%d)", code); + +// LogFileClose(); exit(code); } 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 13 Jun 2004 15:57:30 -0000 @@ -103,3 +103,130 @@ { 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) + 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) + PG_RETURN_CSTRING(renamedFile); + 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.140 diff -u -r1.140 elog.c --- src/backend/utils/error/elog.c 3 Jun 2004 02:08:04 -0000 1.140 +++ src/backend/utils/error/elog.c 13 Jun 2004 15:57:33 -0000 @@ -63,7 +63,6 @@ #include "utils/memutils.h" #include "utils/guc.h" - /* Global variables */ ErrorContextCallback *error_context_stack = NULL; @@ -71,9 +70,22 @@ 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; +typedef struct +{ + long fileversion; + char filename[MAXPGPATH]; +} +logfileShmemStruct; + +FILE *logfile = NULL; +static long logfileVersion=0; +static logfileShmemStruct *logfileShmem = NULL; + + #ifdef HAVE_SYSLOG char *Syslog_facility; /* openlog() parameters */ char *Syslog_ident; @@ -140,6 +152,9 @@ 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); + /* * errstart --- begin an error-reporting cycle @@ -931,11 +946,145 @@ /* * And let errfinish() finish up. */ + errfinish(0); } /* + * Initialize shared mem for logfile rotation + */ + +void +LogFileInit(void) +{ + if (Log_filename && (Log_destination & LOG_DESTINATION_FILE)) + { + logfileShmem = ShmemAlloc(sizeof(logfileShmemStruct)); + if (!logfileShmem) + { + ereport(FATAL, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("Out of shared memory"))); + return; + } + + memset(logfileShmem, 0, sizeof(logfileShmemStruct)); + + char *fn = logfile_newname(); + if (fn) + { + strcpy(logfileShmem->filename, fn); + logfile_reopen(); + pfree(fn); + } + } +} + + +/* + * Rotate log file + */ +char * +LogFileRotate(void) +{ + char *oldFilename; + char *filename; + + if (!logfileShmem || !(Log_destination & LOG_DESTINATION_FILE)) + return NULL; + + filename = logfile_newname(); + if (!filename) + return NULL; + + + if (!strcmp(filename, logfileShmem->filename)) + { + ereport(ERROR, + (errcode(ERRCODE_CONFIG_FILE_ERROR), + errmsg("Log_filename not suitable for rotation."))); + return NULL; + } + + oldFilename = pstrdup(logfileShmem->filename); + strcpy(logfileShmem->filename, filename); + + logfileShmem->fileversion++; + + ereport(NOTICE, + (errcode(ERRCODE_WARNING), + errmsg("Opened new log file %s; previous logfile %s", logfileShmem->filename, oldFilename))); + + return oldFilename; +} + + +/* + * return current log file name + */ +char* +LogFileName(void) +{ + if (logfileShmem) + return logfileShmem->filename; + 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; +} + + +/* + * reopen log file. + */ +static void +logfile_reopen(void) +{ + if (logfile) + { + fclose(logfile); + logfile = NULL; + } + + if ((Log_destination & LOG_DESTINATION_FILE) && logfileShmem) + { + logfileVersion = logfileShmem->fileversion; + + logfile = fopen(logfileShmem->filename, "a+"); + + if (!logfile) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("failed to open log file %s", logfileShmem->filename))); + } +} + + +/* * Initialization of error output file */ void @@ -1445,6 +1594,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 (logfileShmem && logfileShmem->fileversion != logfileVersion) + { + logfileVersion = logfileShmem->fileversion; + 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 13 Jun 2004 15:57:42 -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 13 Jun 2004 15:57:42 -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.335 diff -u -r1.335 pg_proc.h --- src/include/catalog/pg_proc.h 6 Jun 2004 19:07:00 -0000 1.335 +++ src/include/catalog/pg_proc.h 13 Jun 2004 15:57:57 -0000 @@ -3588,6 +3588,16 @@ 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 = 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.241 diff -u -r1.241 builtins.h --- src/include/utils/builtins.h 2 Jun 2004 21:29:29 -0000 1.241 +++ src/include/utils/builtins.h 13 Jun 2004 15:58:00 -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.68 diff -u -r1.68 elog.h --- src/include/utils/elog.h 5 Apr 2004 03:02:10 -0000 1.68 +++ src/include/utils/elog.h 13 Jun 2004 15:58:01 -0000 @@ -182,8 +182,11 @@ #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); #endif /* ELOG_H */