1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-28 23:42:10 +03:00

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
This commit is contained in:
Alexander Korotkov
2018-09-01 19:46:49 +03:00
parent ab0ed6153a
commit ec74369931
8 changed files with 230 additions and 19 deletions

View File

@ -1268,6 +1268,9 @@ PostmasterMain(int argc, char *argv[])
*/
RemovePromoteSignalFiles();
/* Do the same for logrotate signal file */
RemoveLogrotateSignalFiles();
/* Remove any outdated file holding the current log filenames. */
if (unlink(LOG_METAINFO_DATAFILE) < 0 && errno != ENOENT)
ereport(LOG,
@ -5100,11 +5103,18 @@ sigusr1_handler(SIGNAL_ARGS)
signal_child(PgArchPID, SIGUSR1);
}
if (CheckPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE) &&
SysLoggerPID != 0)
/* Tell syslogger to rotate logfile if requested */
if (SysLoggerPID != 0)
{
/* Tell syslogger to rotate logfile */
signal_child(SysLoggerPID, SIGUSR1);
if (CheckLogrotateSignal())
{
signal_child(SysLoggerPID, SIGUSR1);
RemoveLogrotateSignalFiles();
}
else if (CheckPostmasterSignal(PMSIGNAL_ROTATE_LOGFILE))
{
signal_child(SysLoggerPID, SIGUSR1);
}
}
if (CheckPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER) &&

View File

@ -57,6 +57,9 @@
*/
#define READ_BUF_SIZE (2 * PIPE_CHUNK_SIZE)
/* Log rotation signal file path, relative to $PGDATA */
#define LOGROTATE_SIGNAL_FILE "logrotate"
/*
* GUC parameters. Logging_collector cannot be changed after postmaster
@ -405,7 +408,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;
@ -1506,6 +1509,30 @@ update_metainfo_datafile(void)
* --------------------------------
*/
/*
* Check to see if a log rotation request has arrived. Should be
* called by postmaster after receiving SIGUSR1.
*/
bool
CheckLogrotateSignal(void)
{
struct stat stat_buf;
if (stat(LOGROTATE_SIGNAL_FILE, &stat_buf) == 0)
return true;
return false;
}
/*
* Remove the file signaling a log rotateion request.
*/
void
RemoveLogrotateSignalFiles(void)
{
unlink(LOGROTATE_SIGNAL_FILE);
}
/* SIGHUP: set flag to reload config file */
static void
sigHupHandler(SIGNAL_ARGS)

View File

@ -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;

View File

@ -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();

View File

@ -87,6 +87,9 @@ extern void write_syslogger_file(const char *buffer, int count, int dest);
extern void SysLoggerMain(int argc, char *argv[]) pg_attribute_noreturn();
#endif
extern bool CheckLogrotateSignal(void);
extern void RemoveLogrotateSignalFiles(void);
/*
* Name of files saving meta-data information about the log
* files currently in use by the syslogger

View File

@ -804,6 +804,27 @@ sub promote
return;
}
=pod
=item $node->logrotate()
Wrapper for pg_ctl logrotate
=cut
sub logrotate
{
my ($self) = @_;
my $port = $self->port;
my $pgdata = $self->data_dir;
my $logfile = $self->logfile;
my $name = $self->name;
print "### Rotating log in node \"$name\"\n";
TestLib::system_or_bail('pg_ctl', '-D', $pgdata, '-l', $logfile,
'logrotate');
return;
}
# Internal routine to enable streaming replication on a standby node.
sub enable_streaming
{