diff --git a/doc/src/sgml/ref/pg_createsubscriber.sgml b/doc/src/sgml/ref/pg_createsubscriber.sgml index 67a683e66c7..2898a5ea111 100644 --- a/doc/src/sgml/ref/pg_createsubscriber.sgml +++ b/doc/src/sgml/ref/pg_createsubscriber.sgml @@ -141,16 +141,22 @@ PostgreSQL documentation - Specify the name of the log directory. A new directory is created with this name if it does not exist. A subdirectory with a timestamp indicating the time at which pg_createsubscriber was run will be created. The following two log files will be createdin the subdirectory with a umask of 077 so that access is disallowed to other users by default. + Specify the name of the log directory. A new directory is created with + this name if it does not exist. A subdirectory with a timestamp + indicating the time at which pg_createsubscriber was run will be created. + The following two log files will be created in the subdirectory with a + umask of 077 so that access is disallowed to other users by default. - pg_createsubscriber_server.log which captures logs related to stopping and starting the standby server, + pg_createsubscriber_server.log which captures logs related to stopping + and starting the standby server, - pg_createsubscriber_internal.log which captures internal diagnostic output (validations, checks, etc.) + pg_createsubscriber_internal.log which captures internal diagnostic + output (validations, checks, etc.) diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c index 3495bdb88ce..4450842d2b5 100644 --- a/src/bin/pg_basebackup/pg_createsubscriber.c +++ b/src/bin/pg_basebackup/pg_createsubscriber.c @@ -150,8 +150,9 @@ static void drop_existing_subscription(PGconn *conn, const char *subname, const char *dbname); static void get_publisher_databases(struct CreateSubscriberOptions *opt, bool dbnamespecified); -static void - internal_log_file_write(const char *format,...) __attribute__((format(printf, 1, 2))); +static void internal_log_file_write(enum pg_log_level level, + const char *format,...) + pg_attribute_printf(2, 3); #define WAIT_INTERVAL 1 /* 1 second */ @@ -185,52 +186,75 @@ static bool recovery_ended = false; static bool standby_running = false; static bool recovery_params_set = false; +#undef pg_log_warning +#define pg_log_warning(...) \ + if (internal_log_file_fp != NULL) \ + internal_log_file_write(PG_LOG_WARNING, __VA_ARGS__); \ + pg_log_generic(PG_LOG_WARNING, PG_LOG_PRIMARY, __VA_ARGS__) + +#undef pg_log_warning_detail +#define pg_log_warning_detail(...) \ + if (internal_log_file_fp != NULL) \ + internal_log_file_write(PG_LOG_WARNING, __VA_ARGS__); \ + pg_log_generic(PG_LOG_WARNING, PG_LOG_DETAIL, __VA_ARGS__) + +#undef pg_log_warning_hint +#define pg_log_warning_hint(...) \ + if (internal_log_file_fp != NULL) \ + internal_log_file_write(PG_LOG_WARNING, __VA_ARGS__); \ + pg_log_generic(PG_LOG_WARNING, PG_LOG_HINT, __VA_ARGS__) + #undef pg_log_info -#define pg_log_info(...) do{\ +#define pg_log_info(...) do { \ if (internal_log_file_fp != NULL) \ - internal_log_file_write(__VA_ARGS__); \ + internal_log_file_write(PG_LOG_INFO, __VA_ARGS__); \ else \ - pg_log_generic(PG_LOG_INFO, PG_LOG_PRIMARY, __VA_ARGS__);\ + pg_log_generic(PG_LOG_INFO, PG_LOG_PRIMARY, __VA_ARGS__); \ } while(0) #undef pg_log_info_hint -#define pg_log_info_hint(...) do{\ +#define pg_log_info_hint(...) do { \ if (internal_log_file_fp != NULL) \ - internal_log_file_write(__VA_ARGS__); \ + internal_log_file_write(PG_LOG_INFO, __VA_ARGS__); \ else \ - pg_log_generic(PG_LOG_INFO, PG_LOG_HINT, __VA_ARGS__);\ + pg_log_generic(PG_LOG_INFO, PG_LOG_HINT, __VA_ARGS__); \ } while(0) #undef pg_log_debug -#define pg_log_debug(...) do{\ - if (internal_log_file_fp != NULL) \ - internal_log_file_write(__VA_ARGS__); \ - else \ - if (unlikely(__pg_log_level <= PG_LOG_DEBUG)) \ +#define pg_log_debug(...) do { \ + if (unlikely(__pg_log_level <= PG_LOG_DEBUG)) { \ + if (internal_log_file_fp != NULL) \ + internal_log_file_write(PG_LOG_DEBUG, __VA_ARGS__); \ + else \ pg_log_generic(PG_LOG_DEBUG, PG_LOG_PRIMARY, __VA_ARGS__); \ + } \ } while(0) #undef pg_fatal -#define pg_fatal(...) do{\ +#define pg_fatal(...) do { \ if (internal_log_file_fp != NULL) \ - internal_log_file_write(__VA_ARGS__); \ + internal_log_file_write(PG_LOG_ERROR, __VA_ARGS__); \ pg_log_generic(PG_LOG_ERROR, PG_LOG_PRIMARY, __VA_ARGS__); \ exit(1); \ } while(0) static void -internal_log_file_write(const char *format,...) +internal_log_file_write(enum pg_log_level level, const char *format,...) { - if (internal_log_file_fp != NULL) - { - va_list args; + va_list args; - va_start(args, format); - vfprintf(internal_log_file_fp, format, args); - fprintf(internal_log_file_fp, "\n"); - fflush(internal_log_file_fp); - va_end(args); - } + if (level < __pg_log_level) + return; + + if (internal_log_file_fp == NULL) + return; + + va_start(args, format); + vfprintf(internal_log_file_fp, format, args); + va_end(args); + + fprintf(internal_log_file_fp, "\n"); + fflush(internal_log_file_fp); } /* @@ -264,22 +288,27 @@ logfile_open(const char *filename, const char *mode) } static void -make_dir(char *dir) +make_dir(const char *dir) { struct stat statbuf; - if (stat(dir, &statbuf) != 0) - if (errno == ENOENT) - { - if (mkdir(dir, S_IRWXU) == 0) - pg_log_info("directory %s created", dir); - else - pg_fatal("could not create log directory \"%s\": %m", dir); - } + if (stat(dir, &statbuf) == 0) + return; + + if (errno != ENOENT) + pg_fatal("could not stat directory \"%s\": %m", dir); + + if (mkdir(dir, S_IRWXU) == 0) + { + pg_log_info("directory %s created", dir); + return; + } + + pg_fatal("could not create log directory \"%s\": %m", dir); } static void -make_output_dirs(char *log_dir) +make_output_dirs(const char *log_dir) { char timestamp[128]; struct timeval tval; @@ -288,18 +317,31 @@ make_output_dirs(char *log_dir) char timestamp_dir[MAXPGPATH]; int len; + /* Generate timestamp */ gettimeofday(&tval, NULL); now = tval.tv_sec; - strftime(timestamp, sizeof(timestamp), "%Y%m%dT%H%M%S", localtime_r(&now, &tmbuf)); + + strftime(timestamp, sizeof(timestamp), "%Y%m%dT%H%M%S", + localtime_r(&now, &tmbuf)); + /* append milliseconds */ - snprintf(timestamp + strlen(timestamp), sizeof(timestamp) - strlen(timestamp), - ".%03u", (unsigned int) (tval.tv_usec / 1000)); + snprintf(timestamp + strlen(timestamp), + sizeof(timestamp) - strlen(timestamp), ".%03u", + (unsigned int) (tval.tv_usec / 1000)); + log_timestamp = pg_strdup(timestamp); + /* Create base directory (ignore if exists) */ make_dir(log_dir); + + /* Build timestamp directory path */ len = snprintf(timestamp_dir, MAXPGPATH, "%s/%s", log_dir, timestamp); + if (len >= MAXPGPATH) - pg_fatal("directory path for log files, %s/%s, is too long", log_dir, timestamp); + pg_fatal("directory path for log files, %s/%s, is too long", + log_dir, timestamp); + + /* Create timestamp directory */ make_dir(timestamp_dir); } @@ -2361,7 +2403,6 @@ main(int argc, char **argv) char *consistent_lsn; char pidfile[MAXPGPATH]; - char *internal_log_file; pg_logging_init(argv[0]); pg_logging_set_level(PG_LOG_WARNING); @@ -2439,10 +2480,6 @@ main(int argc, char **argv) case 'l': opt.log_dir = pg_strdup(optarg); canonicalize_path(opt.log_dir); - make_output_dirs(opt.log_dir); - internal_log_file = psprintf("%s/%s/%s.log", opt.log_dir, log_timestamp, INTERNAL_LOG_FILE_NAME); - if ((internal_log_file_fp = logfile_open(internal_log_file, "a")) == NULL) - pg_fatal("could not open log file \"%s\": %m", internal_log_file); break; case 'n': dry_run = true; @@ -2512,6 +2549,18 @@ main(int argc, char **argv) } } + if (opt.log_dir != NULL) + { + char *internal_log_file; + + make_output_dirs(opt.log_dir); + internal_log_file = psprintf("%s/%s/%s.log", opt.log_dir, log_timestamp, + INTERNAL_LOG_FILE_NAME); + + if ((internal_log_file_fp = logfile_open(internal_log_file, "a")) == NULL) + pg_fatal("could not open log file \"%s\": %m", internal_log_file); + } + /* Validate that --all is not used with incompatible options */ if (opt.all_dbs) { diff --git a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl index 3a82f893e28..4ddfb621a5d 100644 --- a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl +++ b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl @@ -371,16 +371,26 @@ command_ok( my @server_log_files = glob "$logdir/*/pg_createsubscriber_server.log"; is( scalar(@server_log_files), 1, " pg_createsubscriber_server.log file was created"); -my $server_log_file_size = -s shift(@server_log_files); +my $server_log_file_size = -s $server_log_files[0]; isnt($server_log_file_size, 0, "pg_createsubscriber_server.log file not empty"); +my $server_log = slurp_file($server_log_files[0]); +like( + $server_log, + qr/consistent recovery state reached/, + "server reached consistent recovery state"); my @internal_log_files = glob "$logdir/*/pg_createsubscriber_internal.log"; is( scalar(@internal_log_files), 1, " pg_createsubscriber_internal.log file was created"); -my $internal_log_file_size = -s shift(@internal_log_files); +my $internal_log_file_size = -s $internal_log_files[0]; isnt($internal_log_file_size, 0, "pg_createsubscriber_internal.log file not empty"); +my $internal_log = slurp_file($internal_log_files[0]); +like( + $internal_log, + qr/target server reached the consistent state/, + "log shows consistent state reached"); # Check if node S is still a standby $node_s->start; @@ -461,7 +471,8 @@ is(scalar(() = $stderr =~ /would create subscription/g), # Create a user-defined publication, and a table that is not a member of that # publication. -$node_p->safe_psql($db1, qq( +$node_p->safe_psql( + $db1, qq( CREATE PUBLICATION test_pub3 FOR TABLE tbl1; CREATE TABLE not_replicated (a int); )); @@ -557,8 +568,7 @@ second row third row), "logical replication works in database $db1"); $result = $node_s->safe_psql($db1, 'SELECT * FROM not_replicated'); -is($result, qq(), - "table is not replicated in database $db1"); +is($result, qq(), "table is not replicated in database $db1"); # Check result in database $db2 $result = $node_s->safe_psql($db2, 'SELECT * FROM tbl2'); @@ -572,8 +582,10 @@ my $sysid_s = $node_s->safe_psql('postgres', isnt($sysid_p, $sysid_s, 'system identifier was changed'); # Verify that pub2 was created in $db2 -is($node_p->safe_psql($db2, "SELECT COUNT(*) FROM pg_publication WHERE pubname = 'pub2'"), - '1', "publication pub2 was created in $db2"); +is( $node_p->safe_psql( + $db2, "SELECT COUNT(*) FROM pg_publication WHERE pubname = 'pub2'"), + '1', + "publication pub2 was created in $db2"); # Get subscription and publication names $result = $node_s->safe_psql( @@ -598,7 +610,7 @@ $result = $node_s->safe_psql( ) ); -is($result, qq($db1|{test_pub3} +is( $result, qq($db1|{test_pub3} $db2|{pub2}), "subscriptions use the correct publications");