public inbox for [email protected]
help / color / mirror / Atom feedFrom: Gyan Sreejith <[email protected]>
To: [email protected]
Cc: vignesh C <[email protected]>
Subject: [Proposal] Adding Log File Capability to pg_createsubscriber
Date: Tue, 9 Dec 2025 17:16:40 -0500
Message-ID: <CAEqnbaUthOQARV1dscGvB_EsqC-YfxiM6rWkVDHc+G+f4oSUHw@mail.gmail.com> (raw)
Background:
-
pg_createsubscriber currently outputs all messages (internal validation
messages, standby server start/stop logs, recovery progress output, and
output from utilities) directly to the console. As a result, users may find
debugging and handling errors difficult. It would be more convenient if
messages were separated and stored in different log files. There is already
a similar implementation in pg_upgrade.
Proposed Solution:
-
Based on issues mentioned previously, I would like to propose a new
argument -l <logdir> which can be specified for pg_createsubscriber. Using
it would create the following log files:
-
logdir/pg_createsubscriber_server.log which captures all logs related
to starting and stopping the standby server.
-
logdir/pg_createsubscriber_resetwal.log which captures the output of
pg_resetwal
-
logdir/pg_createsubscriber_internal.log which captures internal
diagnostic output from pg_createsubscriber (validations, checks, etc.)
Overall, this proposed solution could make the pg_createsubscriber command
output messages more organized. The command would be easier to use as users
will only have to read individual log files rather than parse through lots
of possibly irrelevant output messages. I have attached the patch for this
change.
Special thanks to Vignesh C. for his offlist guidance on this project.
Regards, Gyan Sreejith
Attachments:
[application/octet-stream] diff.patch (7.6K, 3-diff.patch)
download | inline diff:
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index f59c293d875..e8d7b0730e9 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -36,6 +36,7 @@
struct CreateSubscriberOptions
{
char *config_file; /* configuration file */
+ char *log_dir; /* log directory */
char *pub_conninfo_str; /* publisher connection string */
char *socket_dir; /* directory for Unix-domain socket, if any */
char *sub_port; /* subscriber port number */
@@ -150,6 +151,8 @@ static pg_prng_state prng_state;
static char *pg_ctl_path = NULL;
static char *pg_resetwal_path = NULL;
+static char *internal_log_file = NULL;
+
/* standby / subscriber data directory */
static char *subscriber_dir = NULL;
@@ -251,6 +254,7 @@ usage(void)
" databases and databases that don't allow connections\n"));
printf(_(" -d, --database=DBNAME database in which to create a subscription\n"));
printf(_(" -D, --pgdata=DATADIR location for the subscriber data directory\n"));
+ printf(_(" -l, --logdir=LOGDIR location for the new log directory\n"));
printf(_(" -n, --dry-run dry run, just show what would be done\n"));
printf(_(" -p, --subscriber-port=PORT subscriber port number (default %s)\n"), DEFAULT_SUB_PORT);
printf(_(" -P, --publisher-server=CONNSTR publisher connection string\n"));
@@ -670,6 +674,7 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
bool crc_ok;
struct timeval tv;
+ char *out_file;
char *cmd_str;
pg_log_info("modifying system identifier of subscriber");
@@ -703,8 +708,14 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
else
pg_log_info("running pg_resetwal on the subscriber");
+
+ if (opt->log_dir != NULL)
+ out_file = psprintf("%s/pg_createsubscriber_resetwal.log", opt->log_dir);
+ else
+ out_file = DEVNULL;
+
cmd_str = psprintf("\"%s\" -D \"%s\" > \"%s\"", pg_resetwal_path,
- subscriber_dir, DEVNULL);
+ subscriber_dir, out_file);
pg_log_debug("pg_resetwal command is: %s", cmd_str);
@@ -893,8 +904,18 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
int cur_walsenders;
int max_prepared_transactions;
char *max_slot_wal_keep_size;
+ FILE *fp;
- pg_log_info("checking settings on publisher");
+ if (internal_log_file != NULL)
+ {
+ if ((fp = fopen(internal_log_file, "a")) == NULL)
+ pg_fatal("could not write to log file \"%s\": %m", internal_log_file);
+
+ fprintf(fp, "checking settings on publisher\n");
+ fclose(fp);
+ }
+ else
+ pg_log_info("checking settings on publisher");
conn = connect_database(dbinfo[0].pubconninfo, true);
@@ -1028,8 +1049,18 @@ check_subscriber(const struct LogicalRepInfo *dbinfo)
int max_lrworkers;
int max_reporigins;
int max_wprocs;
+ FILE *fp;
- pg_log_info("checking settings on subscriber");
+ if (internal_log_file != NULL)
+ {
+ if ((fp = fopen(internal_log_file, "a")) == NULL)
+ pg_fatal("could not write to log file \"%s\": %m", internal_log_file);
+
+ fprintf(fp, "checking settings on subscriber\n");
+ fclose(fp);
+ }
+ else
+ pg_log_info("checking settings on subscriber");
conn = connect_database(dbinfo[0].subconninfo, true);
@@ -1548,6 +1579,11 @@ start_standby_server(const struct CreateSubscriberOptions *opt, bool restricted_
if (restrict_logical_worker)
appendPQExpBufferStr(pg_ctl_cmd, " -o \"-c max_logical_replication_workers=0\"");
+ if (opt->log_dir != NULL)
+ {
+ appendPQExpBuffer(pg_ctl_cmd, " -l %s/pg_createsubscriber_server.log", opt->log_dir);
+ }
+
pg_log_debug("pg_ctl command is: %s", pg_ctl_cmd->data);
rc = system(pg_ctl_cmd->data);
pg_ctl_status(pg_ctl_cmd->data, rc);
@@ -2071,6 +2107,7 @@ main(int argc, char **argv)
{"all", no_argument, NULL, 'a'},
{"database", required_argument, NULL, 'd'},
{"pgdata", required_argument, NULL, 'D'},
+ {"logdir", required_argument, NULL, 'l'},
{"dry-run", no_argument, NULL, 'n'},
{"subscriber-port", required_argument, NULL, 'p'},
{"publisher-server", required_argument, NULL, 'P'},
@@ -2129,6 +2166,7 @@ main(int argc, char **argv)
/* Default settings */
subscriber_dir = NULL;
opt.config_file = NULL;
+ opt.log_dir = NULL;
opt.pub_conninfo_str = NULL;
opt.socket_dir = NULL;
opt.sub_port = DEFAULT_SUB_PORT;
@@ -2157,7 +2195,7 @@ main(int argc, char **argv)
get_restricted_token();
- while ((c = getopt_long(argc, argv, "ad:D:np:P:s:t:TU:v",
+ while ((c = getopt_long(argc, argv, "ad:D:l:np:P:s:t:TU:v",
long_options, &option_index)) != -1)
{
switch (c)
@@ -2178,6 +2216,23 @@ main(int argc, char **argv)
subscriber_dir = pg_strdup(optarg);
canonicalize_path(subscriber_dir);
break;
+ case 'l':
+ opt.log_dir = pg_strdup(optarg);
+ canonicalize_path(opt.log_dir);
+
+ if (stat(opt.log_dir, &statbuf) != 0)
+ {
+ if (errno == ENOENT)
+ {
+ mkdir(opt.log_dir, S_IRWXU);
+ pg_log_info("log directory created");
+ }
+ else
+ pg_fatal("could not access directory \"%s\": %m", opt.log_dir);
+ }
+
+ internal_log_file = psprintf("%s/pg_createsubscriber_internal.log", opt.log_dir);
+ break;
case 'n':
dry_run = true;
break;
diff --git a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
index 3d6086dc489..e1ef49a6c3c 100644
--- a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
+++ b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
@@ -13,7 +13,9 @@ program_help_ok('pg_createsubscriber');
program_version_ok('pg_createsubscriber');
program_options_handling_ok('pg_createsubscriber');
-my $datadir = PostgreSQL::Test::Utils::tempdir;
+my $datadir = PostgreSQL::Test::Utils::tempdir + "/datadir";
+my $logdir = PostgreSQL::Test::Utils::tempdir + "/logdir";
+
# Generate a database with a name made of a range of ASCII characters.
# Extracted from 002_pg_upgrade.pl.
@@ -121,6 +123,7 @@ command_fails(
# Set up node P as primary
my $node_p = PostgreSQL::Test::Cluster->new('node_p');
my $pconnstr = $node_p->connstr;
+
$node_p->init(allows_streaming => 'logical');
# Disable autovacuum to avoid generating xid during stats update as otherwise
# the new XID could then be replicated to standby at some random point making
@@ -537,10 +540,44 @@ my $sysid_s = $node_s->safe_psql('postgres',
'SELECT system_identifier FROM pg_control_system()');
isnt($sysid_p, $sysid_s, 'system identifier was changed');
+$node_p->backup('backup_3');
+
+# Set up node R as a logical replica node
+my $node_r = PostgreSQL::Test::Cluster->new('node_r');
+$node_r->init_from_backup($node_p, 'backup_3', has_streaming => 1);
+$node_r->append_conf(
+ 'postgresql.conf', qq[
+primary_conninfo = '$pconnstr dbname=postgres'
+hot_standby_feedback = on
+]);
+$node_r->set_standby_mode();
+
+# Test that --logdir works for pg_createsubscriber
+command_ok(
+ [
+ 'pg_createsubscriber',
+ '--verbose',
+ '--pgdata' => $node_r->data_dir,
+ '--publisher-server' => $pconnstr,
+ '--database' => 'postgres',
+ '--logdir' => $logdir,
+ ],
+ 'check for log file creation for pg_createSubscriber');
+
+# Check that all log files were created
+ok( -f "$logdir/pg_createsubscriber_server.log",
+ 'pg_createsubscriber_server.log file was created');
+ok(-f "$logdir/pg_createsubscriber_resetwal.log",
+ 'pg_resetwal.log file was created');
+ok( -f "$logdir/pg_createsubscriber_internal.log",
+ 'pg_createsubsriber.log file was created');
+
+
# clean up
$node_p->teardown_node;
$node_s->teardown_node;
$node_t->teardown_node;
$node_f->teardown_node;
+$node_r->teardown_node;
done_testing();
view thread (55+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected], [email protected]
Subject: Re: [Proposal] Adding Log File Capability to pg_createsubscriber
In-Reply-To: <CAEqnbaUthOQARV1dscGvB_EsqC-YfxiM6rWkVDHc+G+f4oSUHw@mail.gmail.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox