From 8c603f2c95e34f8c06994c0454779b76e7f6c012 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Tue, 31 Aug 2004 04:53:44 +0000 Subject: [PATCH] Replace log_filename_prefix with more general log_filename parameter, to allow DBA to choose the form in which log filenames reflect the current time. Also allow for truncating instead of appending to pre-existing files --- this is convenient when the log filename pattern rewrites the same names cyclically. Per Ed L. --- doc/src/sgml/runtime.sgml | 39 ++++- src/backend/postmaster/syslogger.c | 157 ++++++++++++------ src/backend/utils/misc/guc.c | 20 ++- src/backend/utils/misc/postgresql.conf.sample | 12 +- src/include/postmaster/syslogger.h | 5 +- 5 files changed, 166 insertions(+), 67 deletions(-) diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml index bdf029fa64..1a2f24500f 100644 --- a/doc/src/sgml/runtime.sgml +++ b/doc/src/sgml/runtime.sgml @@ -1,5 +1,5 @@ @@ -1925,14 +1925,21 @@ archive_command = 'copy "%p" /mnt/server/archivedir/"%f"' # Win32 - - log_filename_prefix (string) + + log_filename (string) When redirect_stderr is enabled, this option - sets the prefix of the file names of the created log files. - The postmaster PID and the current time are appended to this - prefix to form an exact log file name. + sets the file names of the created log files. The value + is treated as a strftime pattern, + so %-escapes + can be used to specify time-varying file names. + If no %-escapes are present, + PostgreSQL will + append the epoch of the new log file's open time. For example, + if log_filename were server_log, then the + chosen file name would be server_log.1093827753 + for a log starting at Sun Aug 29 19:02:33 2004 MST. This option can only be set at server start or in the postgresql.conf configuration file. @@ -1969,6 +1976,26 @@ archive_command = 'copy "%p" /mnt/server/archivedir/"%f"' # Win32 + + log_truncate_on_rotation (boolean) + + + When redirect_stderr is enabled, this option will cause + PostgreSQL to truncate (overwrite), + rather than append to, any existing log file of the same name. + However, truncation will occur only when a new file is being opened + due to time-based rotation, not during server startup or size-based + rotation. When false, pre-existing files will be appended to in + all cases. For example, using this option in combination with + a log_filename like postgresql-%H.log + would result in generating twenty-four hourly log files and then + cyclically overwriting them. + This option can only be set at server start or in the + postgresql.conf configuration file. + + + + syslog_facility (string) diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c index 56bb381388..a1e8870e92 100644 --- a/src/backend/postmaster/syslogger.c +++ b/src/backend/postmaster/syslogger.c @@ -18,7 +18,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/postmaster/syslogger.c,v 1.7 2004/08/29 05:06:46 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/postmaster/syslogger.c,v 1.8 2004/08/31 04:53:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -61,8 +61,9 @@ bool Redirect_stderr = false; int Log_RotationAge = 24 * 60; int Log_RotationSize = 10 * 1024; -char *Log_directory = "pg_log"; -char *Log_filename_prefix = "postgresql-"; +char *Log_directory = NULL; +char *Log_filename = NULL; +bool Log_truncate_on_rotation = false; /* * Globally visible state (used by elog.c) @@ -72,7 +73,7 @@ bool am_syslogger = false; /* * Private state */ -static pg_time_t last_rotation_time = 0; +static pg_time_t next_rotation_time; static bool redirection_done = false; @@ -109,8 +110,9 @@ static void write_syslogger_file_binary(const char *buffer, int count); #ifdef WIN32 static unsigned int __stdcall pipeThread(void *arg); #endif -static void logfile_rotate(void); +static void logfile_rotate(bool time_based_rotation); static char *logfile_getname(pg_time_t timestamp); +static void set_next_rotation_time(void); static void sigHupHandler(SIGNAL_ARGS); @@ -121,7 +123,9 @@ static void sigHupHandler(SIGNAL_ARGS); NON_EXEC_STATIC void SysLoggerMain(int argc, char *argv[]) { - char currentLogDir[MAXPGPATH]; + char *currentLogDir; + char *currentLogFilename; + int currentLogRotationAge; IsUnderPostmaster = true; /* we are a postmaster subprocess now */ @@ -218,15 +222,18 @@ SysLoggerMain(int argc, char *argv[]) } #endif /* WIN32 */ - /* remember age of initial logfile */ - last_rotation_time = time(NULL); - /* remember active logfile directory */ - strncpy(currentLogDir, Log_directory, MAXPGPATH); + /* remember active logfile parameters */ + currentLogDir = pstrdup(Log_directory); + currentLogFilename = pstrdup(Log_filename); + currentLogRotationAge = Log_RotationAge; + /* set next planned rotation time */ + set_next_rotation_time(); /* main worker loop */ for (;;) { bool rotation_requested = false; + bool time_based_rotation = false; #ifndef WIN32 char logbuffer[1024]; @@ -242,46 +249,51 @@ SysLoggerMain(int argc, char *argv[]) ProcessConfigFile(PGC_SIGHUP); /* - * Check if the log directory changed in postgresql.conf. If - * so, force rotation to make sure we're writing the logfiles - * in the right place. - * - * XXX is it worth responding similarly to a change of - * Log_filename_prefix? + * Check if the log directory or filename pattern changed in + * postgresql.conf. If so, force rotation to make sure we're + * writing the logfiles in the right place. */ - if (strncmp(Log_directory, currentLogDir, MAXPGPATH) != 0) + if (strcmp(Log_directory, currentLogDir) != 0) { - strncpy(currentLogDir, Log_directory, MAXPGPATH); + pfree(currentLogDir); + currentLogDir = pstrdup(Log_directory); rotation_requested = true; } + if (strcmp(Log_filename, currentLogFilename) != 0) + { + pfree(currentLogFilename); + currentLogFilename = pstrdup(Log_filename); + rotation_requested = true; + } + /* + * If rotation time parameter changed, reset next rotation time, + * but don't immediately force a rotation. + */ + if (currentLogRotationAge != Log_RotationAge) + { + currentLogRotationAge = Log_RotationAge; + set_next_rotation_time(); + } } - if (!rotation_requested && - last_rotation_time != 0 && - Log_RotationAge > 0) + if (!rotation_requested && Log_RotationAge > 0) { - /* - * Do a logfile rotation if too much time has elapsed since - * the last one. - */ + /* Do a logfile rotation if it's time */ pg_time_t now = time(NULL); - int elapsed_secs = now - last_rotation_time; - if (elapsed_secs >= Log_RotationAge * 60) - rotation_requested = true; + if (now >= next_rotation_time) + rotation_requested = time_based_rotation = true; } if (!rotation_requested && Log_RotationSize > 0) { - /* - * Do a rotation if file is too big - */ + /* Do a rotation if file is too big */ if (ftell(syslogFile) >= Log_RotationSize * 1024L) rotation_requested = true; } if (rotation_requested) - logfile_rotate(); + logfile_rotate(time_based_rotation); #ifndef WIN32 @@ -365,7 +377,6 @@ int SysLogger_Start(void) { pid_t sysloggerPid; - pg_time_t now; char *filename; if (!Redirect_stderr) @@ -424,8 +435,7 @@ SysLogger_Start(void) * The initial logfile is created right in the postmaster, to verify * that the Log_directory is writable. */ - now = time(NULL); - filename = logfile_getname(now); + filename = logfile_getname(time(NULL)); syslogFile = fopen(filename, "a"); @@ -736,16 +746,26 @@ pipeThread(void *arg) * perform logfile rotation */ static void -logfile_rotate(void) +logfile_rotate(bool time_based_rotation) { char *filename; - pg_time_t now; FILE *fh; - now = time(NULL); - filename = logfile_getname(now); + /* + * When doing a time-based rotation, invent the new logfile name based + * on the planned rotation time, not current time, to avoid "slippage" + * in the file name when we don't do the rotation immediately. + */ + if (time_based_rotation) + filename = logfile_getname(next_rotation_time); + else + filename = logfile_getname(time(NULL)); + + if (Log_truncate_on_rotation && time_based_rotation) + fh = fopen(filename, "w"); + else + fh = fopen(filename, "a"); - fh = fopen(filename, "a"); if (!fh) { int saveerrno = errno; @@ -784,7 +804,7 @@ logfile_rotate(void) LeaveCriticalSection(&sysfileSection); #endif - last_rotation_time = now; + set_next_rotation_time(); pfree(filename); } @@ -799,25 +819,60 @@ static char * logfile_getname(pg_time_t timestamp) { char *filename; - char stamptext[128]; - - pg_strftime(stamptext, sizeof(stamptext), "%Y-%m-%d_%H%M%S", - pg_localtime(×tamp)); + int len; + struct pg_tm *tm; filename = palloc(MAXPGPATH); if (is_absolute_path(Log_directory)) - snprintf(filename, MAXPGPATH, "%s/%s%05u_%s.log", - Log_directory, Log_filename_prefix, - (unsigned int) PostmasterPid, stamptext); + snprintf(filename, MAXPGPATH, "%s/", Log_directory); else - snprintf(filename, MAXPGPATH, "%s/%s/%s%05u_%s.log", - DataDir, Log_directory, Log_filename_prefix, - (unsigned int) PostmasterPid, stamptext); + snprintf(filename, MAXPGPATH, "%s/%s/", DataDir, Log_directory); + + len = strlen(filename); + + if (strchr(Log_filename, '%')) + { + /* treat it as a strftime pattern */ + tm = pg_localtime(×tamp); + pg_strftime(filename + len, MAXPGPATH - len, Log_filename, tm); + } + else + { + /* no strftime escapes, so append timestamp to new filename */ + snprintf(filename + len, MAXPGPATH - len, "%s.%lu", + Log_filename, (unsigned long) timestamp); + } return filename; } +/* + * Determine the next planned rotation time, and store in next_rotation_time. + */ +static void +set_next_rotation_time(void) +{ + pg_time_t now; + int rotinterval; + + /* nothing to do if time-based rotation is disabled */ + if (Log_RotationAge <= 0) + return; + + /* + * The requirements here are to choose the next time > now that is a + * "multiple" of the log rotation interval. "Multiple" can be interpreted + * fairly loosely --- in particular, for intervals larger than an hour, + * it might be interesting to align to local time instead of GMT. + */ + rotinterval = Log_RotationAge * 60; /* convert to seconds */ + now = time(NULL); + now -= now % rotinterval; + now += rotinterval; + next_rotation_time = now; +} + /* -------------------------------- * signal handler routines * -------------------------------- diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 252d4a7f9e..c5919c47fe 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -10,7 +10,7 @@ * Written by Peter Eisentraut . * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.235 2004/08/30 02:54:40 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.236 2004/08/31 04:53:44 tgl Exp $ * *-------------------------------------------------------------------- */ @@ -812,6 +812,14 @@ static struct config_bool ConfigureNamesBool[] = &Redirect_stderr, false, NULL, NULL }, + { + {"log_truncate_on_rotation", PGC_SIGHUP, LOGGING_WHERE, + gettext_noop("Truncate existing log files of same name during log rotation"), + NULL + }, + &Log_truncate_on_rotation, + false, NULL, NULL + }, #ifdef WAL_DEBUG { @@ -1665,7 +1673,7 @@ static struct config_string ConfigureNamesString[] = }, { {"log_directory", PGC_SIGHUP, LOGGING_WHERE, - gettext_noop("Sets the destination directory for logfiles."), + gettext_noop("Sets the destination directory for log files."), gettext_noop("May be specified as relative to the cluster directory " "or as absolute path.") }, @@ -1673,12 +1681,12 @@ static struct config_string ConfigureNamesString[] = "pg_log", NULL, NULL }, { - {"log_filename_prefix", PGC_SIGHUP, LOGGING_WHERE, - gettext_noop("Prefix for file names created in the log_directory."), + {"log_filename", PGC_SIGHUP, LOGGING_WHERE, + gettext_noop("Sets the file name pattern for log files."), NULL }, - &Log_filename_prefix, - "postgresql-", NULL, NULL + &Log_filename, + "postgresql-%Y-%m-%d_%H%M%S.log", NULL, NULL }, #ifdef HAVE_SYSLOG diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index cdd427f04b..67b9dbadaf 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -171,9 +171,17 @@ # This is relevant when logging to stderr: #redirect_stderr = false # Enable capturing of stderr into log files. # These are only relevant if redirect_stderr is true: -#log_directory = 'pg_log' # Directory where logfiles are written. +#log_directory = 'pg_log' # Directory where log files are written. # May be specified absolute or relative to PGDATA -#log_filename_prefix = 'postgresql_' # Prefix for logfile names. +#log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log' # Log file name pattern. + # May include strftime() escapes +#log_truncate_on_rotation = false # If true, any existing log file of the + # same name as the new log file will be truncated + # rather than appended to. But such truncation + # only occurs on time-driven rotation, + # not on restarts or size-driven rotation. + # Default is false, meaning append to existing + # files in all cases. #log_rotation_age = 1440 # Automatic rotation of logfiles will happen after # so many minutes. 0 to disable. #log_rotation_size = 10240 # Automatic rotation of logfiles will happen after diff --git a/src/include/postmaster/syslogger.h b/src/include/postmaster/syslogger.h index 2e1adc6900..12f2ffe5cf 100644 --- a/src/include/postmaster/syslogger.h +++ b/src/include/postmaster/syslogger.h @@ -5,7 +5,7 @@ * * Copyright (c) 2004, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/include/postmaster/syslogger.h,v 1.2 2004/08/29 05:06:58 momjian Exp $ + * $PostgreSQL: pgsql/src/include/postmaster/syslogger.h,v 1.3 2004/08/31 04:53:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,7 +17,8 @@ extern bool Redirect_stderr; extern int Log_RotationAge; extern int Log_RotationSize; extern char *Log_directory; -extern char *Log_filename_prefix; +extern char *Log_filename; +extern bool Log_truncate_on_rotation; extern bool am_syslogger; -- GitLab