Re: serverlog rotation/functions

From: Bruce Momjian <pgman(at)candle(dot)pha(dot)pa(dot)us>
To: Andreas Pflug <pgadmin(at)pse-consulting(dot)de>
Cc: PostgreSQL Patches <pgsql-patches(at)postgresql(dot)org>, Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>
Subject: Re: serverlog rotation/functions
Date: 2004-07-13 21:24:55
Message-ID: 200407132124.i6DLOtd00413@candle.pha.pa.us
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers pgsql-patches


How is this patch supposed to work? Do people need to modify
postgresql.conf and then sighup the postmaster? It seems more logical
for the super-user to call a server-side function. You have
pg_logfile_rotate(), but that doesn't send a sighup to the postmaster so
all the backends will reread the global log file name.

Also, what mechanism is there to prevent backends from reading the log
filename _while_ it is being modified?

Also there are no documenttion changes.

However, looking at the issue of backends all reloading their
postgresql.conf files at different times and sending output to different
files, I wonder if it would be best to create a log process and have
each backend connect to that. That way, all the logging happens in one
process.

---------------------------------------------------------------------------

Andreas Pflug wrote:
> 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(&timestamp));
> +
> + 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

--
Bruce Momjian | http://candle.pha.pa.us
pgman(at)candle(dot)pha(dot)pa(dot)us | (610) 359-1001
+ If your life is a hard drive, | 13 Roberts Road
+ Christ can be your backup. | Newtown Square, Pennsylvania 19073

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Magnus Hagander 2004-07-13 21:26:35 Re: [HACKERS] Is "trust" really a good default?
Previous Message Tom Lane 2004-07-13 21:19:56 Re: Point in Time Recovery

Browse pgsql-patches by date

  From Date Subject
Next Message Magnus Hagander 2004-07-13 21:26:35 Re: [HACKERS] Is "trust" really a good default?
Previous Message Tom Lane 2004-07-13 21:19:56 Re: Point in Time Recovery