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