diff --git a/doc/src/sgml/maintenance.sgml b/doc/src/sgml/maintenance.sgml index 4a68ec3..5bd7b45 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,36 @@ pg_ctl start | rotatelogs /var/log/pgsql_log 86400 + You can combine these approaches by setting up logrotate + to collect log files produced by PostgreSQL built-in logging + collector. In this case, the logging collector defines the names and + location of the log files, while logrotate + periodically archives these files. When initiating log rotation, + logrotate must ensure that the application + sends further output to the 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, you can run pg_ctl + with the logrotate option instead. When the server receives + this command, the server either switches to a new log file or reopens the + existing file, depending on the logging configuration + (see ). + + + + + When using static log file names, the server might fail to reopen the log + file if the max open file limit is reached or a file table overflow occurs. + In this case, log messages are sent to the old log file until a + successful log rotation. If logrotate is + configured to compress the log file and delete it, the server may lose + the messages logged in this timeframe. To avoid this issue, you can + configure the logging collector to dynamically assign log file names + 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 +988,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..235c8ba 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_name process_id + + + + pg_ctl + + datadir On Microsoft Windows, also: @@ -234,6 +240,12 @@ PostgreSQL documentation + mode rotates the server log file. + For details on how to use this mode 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 a4b53b3..e7e779e 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); @@ -5003,6 +5007,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 @@ -5100,7 +5122,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 ed2396a..71792ed 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 @@ -2333,6 +2392,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) @@ -2443,6 +2504,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;