summaryrefslogtreecommitdiff
path: root/src/bin
diff options
context:
space:
mode:
authorAlexander Korotkov2018-09-01 16:46:49 +0000
committerAlexander Korotkov2018-09-01 16:46:49 +0000
commitec74369931687885cfb6ce9dac55deefdb410086 (patch)
treee40f466f4c0354fb679d72ad8a0785086dd673a3 /src/bin
parentab0ed6153a58294143d6d66ec5f3471477c59d57 (diff)
Implement "pg_ctl logrotate" command
Currently there are two ways to trigger log rotation in logging collector process: call pg_rotate_logfile() SQL-function or send SIGUSR1 signal directly to logging collector process. However, it's nice to have more suitable way for external tools to do that, which wouldn't require SQL connection or knowledge of logging collector pid. This commit implements triggering log rotation by "pg_ctl logrotate" command. Discussion: https://postgr.es/m/20180416.115435.28153375.horiguchi.kyotaro%40lab.ntt.co.jp Author: Kyotaro Horiguchi, Alexander Kuzmenkov, Alexander Korotkov
Diffstat (limited to 'src/bin')
-rw-r--r--src/bin/pg_ctl/pg_ctl.c89
-rw-r--r--src/bin/pg_ctl/t/004_logrotate.pl42
2 files changed, 119 insertions, 12 deletions
diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c
index ed2396aa6c5..1d0b056dde0 100644
--- a/src/bin/pg_ctl/pg_ctl.c
+++ b/src/bin/pg_ctl/pg_ctl.c
@@ -61,6 +61,7 @@ typedef enum
RELOAD_COMMAND,
STATUS_COMMAND,
PROMOTE_COMMAND,
+ LOGROTATE_COMMAND,
KILL_COMMAND,
REGISTER_COMMAND,
UNREGISTER_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(_("server signaled to rotate log file\n"));
+}
+
/*
* utility routines
@@ -1912,19 +1971,20 @@ do_help(void)
{
printf(_("%s is a utility to initialize, start, stop, or control a PostgreSQL server.\n\n"), progname);
printf(_("Usage:\n"));
- printf(_(" %s init[db] [-D DATADIR] [-s] [-o OPTIONS]\n"), progname);
- printf(_(" %s start [-D DATADIR] [-l FILENAME] [-W] [-t SECS] [-s]\n"
- " [-o OPTIONS] [-p PATH] [-c]\n"), progname);
- printf(_(" %s stop [-D DATADIR] [-m SHUTDOWN-MODE] [-W] [-t SECS] [-s]\n"), progname);
- printf(_(" %s restart [-D DATADIR] [-m SHUTDOWN-MODE] [-W] [-t SECS] [-s]\n"
- " [-o OPTIONS] [-c]\n"), progname);
- printf(_(" %s reload [-D DATADIR] [-s]\n"), progname);
- printf(_(" %s status [-D DATADIR]\n"), progname);
- printf(_(" %s promote [-D DATADIR] [-W] [-t SECS] [-s]\n"), progname);
- printf(_(" %s kill SIGNALNAME PID\n"), progname);
+ printf(_(" %s init[db] [-D DATADIR] [-s] [-o OPTIONS]\n"), progname);
+ printf(_(" %s start [-D DATADIR] [-l FILENAME] [-W] [-t SECS] [-s]\n"
+ " [-o OPTIONS] [-p PATH] [-c]\n"), progname);
+ printf(_(" %s stop [-D DATADIR] [-m SHUTDOWN-MODE] [-W] [-t SECS] [-s]\n"), progname);
+ printf(_(" %s restart [-D DATADIR] [-m SHUTDOWN-MODE] [-W] [-t SECS] [-s]\n"
+ " [-o OPTIONS] [-c]\n"), progname);
+ printf(_(" %s reload [-D DATADIR] [-s]\n"), progname);
+ printf(_(" %s status [-D DATADIR]\n"), progname);
+ printf(_(" %s promote [-D DATADIR] [-W] [-t SECS] [-s]\n"), progname);
+ printf(_(" %s logrotate [-D DATADIR] [-s]\n"), progname);
+ printf(_(" %s kill SIGNALNAME PID\n"), progname);
#ifdef WIN32
- printf(_(" %s register [-D DATADIR] [-N SERVICENAME] [-U USERNAME] [-P PASSWORD]\n"
- " [-S START-TYPE] [-e SOURCE] [-W] [-t SECS] [-s] [-o OPTIONS]\n"), progname);
+ printf(_(" %s register [-D DATADIR] [-N SERVICENAME] [-U USERNAME] [-P PASSWORD]\n"
+ " [-S START-TYPE] [-e SOURCE] [-W] [-t SECS] [-s] [-o OPTIONS]\n"), progname);
printf(_(" %s unregister [-N SERVICENAME]\n"), progname);
#endif
@@ -2337,6 +2397,8 @@ main(int argc, char **argv)
ctl_command = STATUS_COMMAND;
else if (strcmp(argv[optind], "promote") == 0)
ctl_command = PROMOTE_COMMAND;
+ else if (strcmp(argv[optind], "logrotate") == 0)
+ ctl_command = LOGROTATE_COMMAND;
else if (strcmp(argv[optind], "kill") == 0)
{
if (argc - optind < 3)
@@ -2443,6 +2505,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;
diff --git a/src/bin/pg_ctl/t/004_logrotate.pl b/src/bin/pg_ctl/t/004_logrotate.pl
new file mode 100644
index 00000000000..fa5ab748177
--- /dev/null
+++ b/src/bin/pg_ctl/t/004_logrotate.pl
@@ -0,0 +1,42 @@
+use strict;
+use warnings;
+
+use PostgresNode;
+use TestLib;
+use Test::More tests => 1;
+use Time::HiRes qw(usleep);
+
+my $tempdir = TestLib::tempdir;
+
+my $node = get_new_node('primary');
+$node->init(allows_streaming => 1);
+$node->append_conf(
+ 'postgresql.conf', qq(
+logging_collector = on
+log_directory = 'log'
+log_filename = 'postgresql.log'
+));
+
+$node->start();
+
+# Rename log file and rotate log. Then log file should appear again.
+
+my $logfile = $node->data_dir . '/log/postgresql.log';
+my $old_logfile = $node->data_dir . '/log/postgresql.old';
+rename($logfile, $old_logfile);
+
+$node->logrotate();
+
+# pg_ctl logrotate doesn't wait until rotation request being completed. So
+# we have to wait some time until log file appears.
+my $attempts = 0;
+my $max_attempts = 180 * 10;
+while (not -e $logfile and $attempts < $max_attempts)
+{
+ usleep(100_000);
+ $attempts++;
+}
+
+ok(-e $logfile, "log file exists");
+
+$node->stop();