Re: serverlog rotation/functions - Mailing list pgsql-patches

From Andreas Pflug
Subject Re: serverlog rotation/functions
Date
Msg-id 40EB255A.8060002@pse-consulting.de
Whole thread Raw
In response to Re: serverlog rotation/functions  (Andreas Pflug <pgadmin@pse-consulting.de>)
Responses Re: serverlog rotation/functions
Re: serverlog rotation/functions
List pgsql-patches
Updated version.

Only timestamp of fresh logfile in shared mem, with sanity checks.
On SIGHUP, timestamp is checked if rotation was issued, as well as
changed log_filename setting from postgresql.conf.

Regards,
Andreas



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

pgsql-patches by date:

Previous
From: Bruce Momjian
Date:
Subject: Re: [PATCH] s_lock support for win32
Next
From: Markus Bertheau
Date:
Subject: Re: First attempt: support for '\dg' in psql