diff --git a/doc/src/sgml/maintenance.sgml b/doc/src/sgml/maintenance.sgml
index 4a68ec3..b028246 100644
--- a/doc/src/sgml/maintenance.sgml
+++ b/doc/src/sgml/maintenance.sgml
@@ -932,8 +932,8 @@ analyze threshold = analyze base threshold + analyze scale factor * number of tu
program if you have one that you are already using with other
server software. For example, the rotatelogs
tool included in the Apache distribution
- can be used with PostgreSQL. To do this,
- just pipe the server's
+ can be used with PostgreSQL. One way to
+ do this is to pipe the server's
stderr output to the desired program.
If you start the server with
pg_ctl, then stderr
@@ -946,6 +946,34 @@ pg_ctl start | rotatelogs /var/log/pgsql_log 86400
+ A combined approach is also possible, where logrotate
+ collects log files produced by the built-in logging collector of
+ PostgreSQL. In this setup, the location and names of
+ the log files are determined by logging collector, and
+ logrotate periodically archives these files. There
+ must be some way for logrotate to inform the
+ application that the log file it is using is going to be archived, and that it
+ must direct further output to a new file. This is commonly done with a
+ postrotate script that sends a SIGHUP
+ signal to the application, which then reopens the log file. In
+ PostgreSQL, the same effect is achieved with the
+ logrotate command of pg_ctl. When
+ the server receives this command, it switches to the new log files according to
+ its configuration (see ). If the
+ server is configured to use a static log file name, it just reopens the file.
+ Note that a setup with a static log file name might lose some log
+ messages on busy systems, where the server might temporarily fail to reopen
+ the log file due to max open file limit or file table overflow. When the server
+ encounters these errors, it continues to use the old log file. Given this behavior,
+ if logrotate is configured, for example, to compress
+ the log file and delete it, the server might continue using the already
+ unlinked file, and the messages logged until the next successful rotation will
+ be lost. One way to avoid this problem is to configure the logging collector to
+ use a dynamic log file name, and use a prerotate script to
+ ignore open log files.
+
+
+
Another production-grade approach to managing log output is to
send it to syslog and let
syslog deal with file rotation. To do this, set the
@@ -958,7 +986,6 @@ pg_ctl start | rotatelogs /var/log/pgsql_log 86400
configured to work with log files from
syslog.
-
On many systems, however, syslog is not very reliable,
particularly with large log messages; it might truncate or drop messages
diff --git a/doc/src/sgml/ref/pg_ctl-ref.sgml b/doc/src/sgml/ref/pg_ctl-ref.sgml
index 0816bc1..acd1d91 100644
--- a/doc/src/sgml/ref/pg_ctl-ref.sgml
+++ b/doc/src/sgml/ref/pg_ctl-ref.sgml
@@ -102,6 +102,12 @@ PostgreSQL documentation
signal_nameprocess_id
+
+
+
+ pg_ctl
+
+ datadirOn Microsoft Windows, also:
@@ -234,6 +240,12 @@ PostgreSQL documentation
+ mode commands the server to rotate its log file.
+ For the discussion of how to use this with external log rotation tools, see
+ .
+
+
+
mode registers the PostgreSQL
server as a system service on Microsoft Windows.
The option allows selection of service start type,
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index d948369..4549f2e 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -385,6 +385,9 @@ static bool LoadedSSL = false;
static DNSServiceRef bonjour_sdref = NULL;
#endif
+/* Log rotation signal file path, relative to $PGDATA */
+#define LOGROTATE_SIGNAL_FILE "logrotate"
+
/*
* postmaster.c - function prototypes
*/
@@ -398,6 +401,7 @@ static void reset_shared(int port);
static void SIGHUP_handler(SIGNAL_ARGS);
static void pmdie(SIGNAL_ARGS);
static void reaper(SIGNAL_ARGS);
+static bool checkLogrotateSignal(void);
static void sigusr1_handler(SIGNAL_ARGS);
static void startup_die(SIGNAL_ARGS);
static void dummy_handler(SIGNAL_ARGS);
@@ -4995,6 +4999,24 @@ ExitPostmaster(int status)
}
/*
+ * Check to see if a log rotation request has arrived. Should be
+ * called by postmaster after receiving SIGUSR1.
+ */
+static bool
+checkLogrotateSignal(void)
+{
+ struct stat stat_buf;
+
+ if (stat(LOGROTATE_SIGNAL_FILE, &stat_buf) == 0)
+ {
+ unlink(LOGROTATE_SIGNAL_FILE);
+ return true;
+ }
+
+ return false;
+}
+
+/*
* sigusr1_handler - handle signal conditions from child processes
*/
static void
@@ -5092,7 +5114,8 @@ sigusr1_handler(SIGNAL_ARGS)
signal_child(PgArchPID, SIGUSR1);
}
- if (CheckPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE) &&
+ if ((checkLogrotateSignal() ||
+ CheckPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE)) &&
SysLoggerPID != 0)
{
/* Tell syslogger to rotate logfile */
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index 58b759f..41f036f 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -388,7 +388,7 @@ SysLoggerMain(int argc, char *argv[])
{
/*
* Force rotation when both values are zero. It means the request
- * was sent by pg_rotate_logfile.
+ * was sent by pg_rotate_logfile or pg_ctl logrotate.
*/
if (!time_based_rotation && size_rotation_for == 0)
size_rotation_for = LOG_DESTINATION_STDERR | LOG_DESTINATION_CSVLOG;
diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c
index 143021d..5abafb2 100644
--- a/src/bin/pg_ctl/pg_ctl.c
+++ b/src/bin/pg_ctl/pg_ctl.c
@@ -59,6 +59,7 @@ typedef enum
STOP_COMMAND,
RESTART_COMMAND,
RELOAD_COMMAND,
+ LOGROTATE_COMMAND,
STATUS_COMMAND,
PROMOTE_COMMAND,
KILL_COMMAND,
@@ -100,6 +101,7 @@ static char version_file[MAXPGPATH];
static char pid_file[MAXPGPATH];
static char backup_file[MAXPGPATH];
static char promote_file[MAXPGPATH];
+static char logrotate_file[MAXPGPATH];
#ifdef WIN32
static DWORD pgctl_start_type = SERVICE_AUTO_START;
@@ -125,6 +127,7 @@ static void do_restart(void);
static void do_reload(void);
static void do_status(void);
static void do_promote(void);
+static void do_logrotate(void);
static void do_kill(pgpid_t pid);
static void print_msg(const char *msg);
static void adjust_data_dir(void);
@@ -1171,6 +1174,62 @@ do_promote(void)
print_msg(_("server promoting\n"));
}
+/*
+ * log rotate
+ */
+
+static void
+do_logrotate(void)
+{
+ FILE *logrotatefile;
+ pgpid_t pid;
+
+ pid = get_pgpid(false);
+
+ if (pid == 0) /* no pid file */
+ {
+ write_stderr(_("%s: PID file \"%s\" does not exist\n"), progname, pid_file);
+ write_stderr(_("Is server running?\n"));
+ exit(1);
+ }
+ else if (pid < 0) /* standalone backend, not postmaster */
+ {
+ pid = -pid;
+ write_stderr(_("%s: cannot rotate log file; "
+ "single-user server is running (PID: %ld)\n"),
+ progname, pid);
+ exit(1);
+ }
+
+ snprintf(logrotate_file, MAXPGPATH, "%s/logrotate", pg_data);
+
+ if ((logrotatefile = fopen(logrotate_file, "w")) == NULL)
+ {
+ write_stderr(_("%s: could not create log rotation signal file \"%s\": %s\n"),
+ progname, logrotate_file, strerror(errno));
+ exit(1);
+ }
+ if (fclose(logrotatefile))
+ {
+ write_stderr(_("%s: could not write log rotation signal file \"%s\": %s\n"),
+ progname, logrotate_file, strerror(errno));
+ exit(1);
+ }
+
+ sig = SIGUSR1;
+ if (kill((pid_t) pid, sig) != 0)
+ {
+ write_stderr(_("%s: could not send log rotation signal (PID: %ld): %s\n"),
+ progname, pid, strerror(errno));
+ if (unlink(logrotate_file) != 0)
+ write_stderr(_("%s: could not remove log rotation signal file \"%s\": %s\n"),
+ progname, logrotate_file, strerror(errno));
+ exit(1);
+ }
+
+ print_msg(_("commanded to rotate log file\n"));
+}
+
/*
* utility routines
@@ -2332,6 +2391,8 @@ main(int argc, char **argv)
ctl_command = RESTART_COMMAND;
else if (strcmp(argv[optind], "reload") == 0)
ctl_command = RELOAD_COMMAND;
+ else if (strcmp(argv[optind], "logrotate") == 0)
+ ctl_command = LOGROTATE_COMMAND;
else if (strcmp(argv[optind], "status") == 0)
ctl_command = STATUS_COMMAND;
else if (strcmp(argv[optind], "promote") == 0)
@@ -2442,6 +2503,9 @@ main(int argc, char **argv)
case PROMOTE_COMMAND:
do_promote();
break;
+ case LOGROTATE_COMMAND:
+ do_logrotate();
+ break;
case KILL_COMMAND:
do_kill(killproc);
break;