public inbox for [email protected]
help / color / mirror / Atom feed[Proposal] Adding Log File Capability to pg_createsubscriber
55+ messages / 12 participants
[nested] [flat]
* [Proposal] Adding Log File Capability to pg_createsubscriber
@ 2025-12-09 22:16 Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
0 siblings, 1 reply; 55+ messages in thread
From: Gyan Sreejith @ 2025-12-09 22:16 UTC (permalink / raw)
To: [email protected]; +Cc: vignesh C <[email protected]>
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();
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
@ 2025-12-11 07:29 ` Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
0 siblings, 1 reply; 55+ messages in thread
From: Peter Smith @ 2025-12-11 07:29 UTC (permalink / raw)
To: Gyan Sreejith <[email protected]>; +Cc: [email protected]; vignesh C <[email protected]>
On Wed, Dec 10, 2025 at 9:17 AM Gyan Sreejith <[email protected]> wrote:
>
> 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
>
Hi Gyan.
I haven't yet looked at this patch in any detail, but here are some
quick comments:
======
1.
+ printf(_(" -l, --logdir=LOGDIR location for the new log
directory\n"));
The patch is missing SGML docs updates for pg_createsubscriber new
option, and any explanation of the split of logfiles.
2.
I might be mistaken, but IIUC it seems the splitting of the logfile
only works when --logdir is specified. Is that correct?
Why should --logdir have any side-effect other than assigning the log
destination folder?
======
Kind Regards,
Peter Smith.
Fujitsu Australia
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
@ 2025-12-12 01:33 ` Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 23:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
0 siblings, 2 replies; 55+ messages in thread
From: Gyan Sreejith @ 2025-12-12 01:33 UTC (permalink / raw)
To: Peter Smith <[email protected]>; +Cc: [email protected]; vignesh C <[email protected]>
Thanks for the feedback, Peter.
I am currently working on the SGML docs update, and will promptly get back
with an update.
For your second point, currently, all output goes directly to the console.
I thought it made more sense to break it up into multiple files depending
on what was being invoked. Do you have another opinion?
Thank you once again,
Gyan Sreejith
On Thu, Dec 11, 2025 at 2:29 AM Peter Smith <[email protected]> wrote:
> On Wed, Dec 10, 2025 at 9:17 AM Gyan Sreejith <[email protected]>
> wrote:
> >
> > 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
> >
>
> Hi Gyan.
>
> I haven't yet looked at this patch in any detail, but here are some
> quick comments:
>
> ======
>
> 1.
> + printf(_(" -l, --logdir=LOGDIR location for the new log
> directory\n"));
>
> The patch is missing SGML docs updates for pg_createsubscriber new
> option, and any explanation of the split of logfiles.
>
> 2.
> I might be mistaken, but IIUC it seems the splitting of the logfile
> only works when --logdir is specified. Is that correct?
> Why should --logdir have any side-effect other than assigning the log
> destination folder?
>
> ======
> Kind Regards,
> Peter Smith.
> Fujitsu Australia
>
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
@ 2025-12-14 15:47 ` Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
1 sibling, 1 reply; 55+ messages in thread
From: Gyan Sreejith @ 2025-12-14 15:47 UTC (permalink / raw)
To: Peter Smith <[email protected]>; +Cc: [email protected]; vignesh C <[email protected]>
I have included the patch file after making the changes to the SGML docs.
Thanks for your help,
Gyan Sreejith
On Thu, Dec 11, 2025 at 8:33 PM Gyan Sreejith <[email protected]>
wrote:
> Thanks for the feedback, Peter.
>
> I am currently working on the SGML docs update, and will promptly get back
> with an update.
>
> For your second point, currently, all output goes directly to the console.
> I thought it made more sense to break it up into multiple files depending
> on what was being invoked. Do you have another opinion?
>
> Thank you once again,
> Gyan Sreejith
>
> On Thu, Dec 11, 2025 at 2:29 AM Peter Smith <[email protected]> wrote:
>
>> On Wed, Dec 10, 2025 at 9:17 AM Gyan Sreejith <[email protected]>
>> wrote:
>> >
>> > 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
>> >
>>
>> Hi Gyan.
>>
>> I haven't yet looked at this patch in any detail, but here are some
>> quick comments:
>>
>> ======
>>
>> 1.
>> + printf(_(" -l, --logdir=LOGDIR location for the new log
>> directory\n"));
>>
>> The patch is missing SGML docs updates for pg_createsubscriber new
>> option, and any explanation of the split of logfiles.
>>
>> 2.
>> I might be mistaken, but IIUC it seems the splitting of the logfile
>> only works when --logdir is specified. Is that correct?
>> Why should --logdir have any side-effect other than assigning the log
>> destination folder?
>>
>> ======
>> Kind Regards,
>> Peter Smith.
>> Fujitsu Australia
>>
>
Attachments:
[application/x-patch] diff.patch (8.6K, 3-diff.patch)
download | inline diff:
diff --git a/doc/src/sgml/ref/pg_createsubscriber.sgml b/doc/src/sgml/ref/pg_createsubscriber.sgml
index bb9cc72576c..5e24a297566 100644
--- a/doc/src/sgml/ref/pg_createsubscriber.sgml
+++ b/doc/src/sgml/ref/pg_createsubscriber.sgml
@@ -136,6 +136,33 @@ PostgreSQL documentation
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>-l <replaceable class="parameter">directory</replaceable></option></term>
+ <term><option>--logdir=<replaceable class="parameter">directory</replaceable></option></term>
+ <listitem>
+ <para>
+ The target directory in which the following three log files will be created:
+ <itemizedlist>
+ <listitem>
+ <para>
+ pg_createsubscriber_server.log which captures logs related to stopping and starting the standby server,
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ pg_createsubscriber_resetwal.log which captures the output of pg_resetwal, and
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ pg_createsubscriber_internal.log which captures internal diagnostic output (validations, checks, etc.)
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>-n</option></term>
<term><option>--dry-run</option></term>
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..1712adc0439 100644
--- a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
+++ b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
@@ -13,7 +13,8 @@ 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.
@@ -537,10 +538,43 @@ 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();
^ permalink raw reply [nested|flat] 55+ messages in thread
* RE: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
@ 2025-12-16 07:01 ` Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
0 siblings, 1 reply; 55+ messages in thread
From: Hayato Kuroda (Fujitsu) @ 2025-12-16 07:01 UTC (permalink / raw)
To: 'Gyan Sreejith' <[email protected]>; +Cc: [email protected] <[email protected]>; vignesh C <[email protected]>; Peter Smith <[email protected]>
Dear Gyan,
+1 for the idea. This point has already been discussed since the initial commit
[1], but it has left till now. I'm happy if you can take initiative.
Of course I can review your patches.
Per my understanding, pg_upgrade puts logfiles at the directory, under
"${PGDATANEW}/pg_upgrade_output.d/${timestamp}". See Note part in [2].
I feel more straightforward way is to follow that approach:
1. pg_createsubscriber creates a directory pg_createsubscriber_output.d/${timestamp}.
${timestamp} has the same format as ISO 8601 (%Y%m%dT%H%M%S).
2. pg_craetesubscriber saves outputs under the directory.
3. Outputs can be retained when the command failed or --retain is specified.
Otherwise, they are removed at the end.
Are there benefits to provide -l option?
Regarding the patch format, our community prefers patches generated by
git format-patch. Can you see the blogpost [3] and try to create patches based on the command?
One benefit is we can easily do versioning.
[1]: https://www.postgresql.org/message-id/60b45b8a-3047-4a21-ba2a-ddb15daa638f%40eisentraut.org
[2]: https://www.postgresql.org/docs/devel/pgupgrade.html
[3]: https://peter.eisentraut.org/blog/2023/05/09/how-to-submit-a-patch-by-email-2023-edition
Best regards,
Hayato Kuroda
FUJITSU LIMITED
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
@ 2025-12-17 10:07 ` vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
0 siblings, 1 reply; 55+ messages in thread
From: vignesh C @ 2025-12-17 10:07 UTC (permalink / raw)
To: Hayato Kuroda (Fujitsu) <[email protected]>; +Cc: Gyan Sreejith <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
On Tue, 16 Dec 2025 at 12:31, Hayato Kuroda (Fujitsu)
<[email protected]> wrote:
>
> Dear Gyan,
>
> +1 for the idea. This point has already been discussed since the initial commit
> [1], but it has left till now. I'm happy if you can take initiative.
> Of course I can review your patches.
>
> Per my understanding, pg_upgrade puts logfiles at the directory, under
> "${PGDATANEW}/pg_upgrade_output.d/${timestamp}". See Note part in [2].
> I feel more straightforward way is to follow that approach:
>
> 1. pg_createsubscriber creates a directory pg_createsubscriber_output.d/${timestamp}.
> ${timestamp} has the same format as ISO 8601 (%Y%m%dT%H%M%S).
> 2. pg_craetesubscriber saves outputs under the directory.
> 3. Outputs can be retained when the command failed or --retain is specified.
> Otherwise, they are removed at the end.
If I recall correctly, this was implemented that way earlier, but the
approach was abandoned around [1]. The primary reason was that when
users take a backup of the data directory, they would need to
explicitly manage the exclusion of this data, which was considered
undesirable.
> Are there benefits to provide -l option?
By providing this as an option, users can store the log files outside
the data directory, eliminating the need for any additional handling
during backups.
[1] - https://www.postgresql.org/message-id/d546c4bb-92d1-4e2d-898f-48234b12ed25%40app.fastmail.com
Regards,
Vignesh
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
@ 2025-12-18 01:28 ` Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
0 siblings, 1 reply; 55+ messages in thread
From: Euler Taveira @ 2025-12-18 01:28 UTC (permalink / raw)
To: vignesh C <[email protected]>; [email protected] <[email protected]>; +Cc: Gyan Sreejith <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
On Wed, Dec 17, 2025, at 7:07 AM, vignesh C wrote:
>
> By providing this as an option, users can store the log files outside
> the data directory, eliminating the need for any additional handling
> during backups.
>
Do we really need an option to capture the stdout / stderr output to a file? I
doubt it. There is already various ways to capture. psql and pg_upgrade are the
only tools that have this option. And honestly, I've never seen these options
used in the field.
--
Euler Taveira
EDB https://www.enterprisedb.com/
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
@ 2025-12-18 06:49 ` Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
0 siblings, 1 reply; 55+ messages in thread
From: Amit Kapila @ 2025-12-18 06:49 UTC (permalink / raw)
To: Euler Taveira <[email protected]>; +Cc: vignesh C <[email protected]>; [email protected] <[email protected]>; Gyan Sreejith <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
On Thu, Dec 18, 2025 at 6:59 AM Euler Taveira <[email protected]> wrote:
>
> On Wed, Dec 17, 2025, at 7:07 AM, vignesh C wrote:
> >
> > By providing this as an option, users can store the log files outside
> > the data directory, eliminating the need for any additional handling
> > during backups.
> >
>
> Do we really need an option to capture the stdout / stderr output to a file? I
> doubt it. There is already various ways to capture. psql and pg_upgrade are the
> only tools that have this option.
>
pg_ctl also has the -l option. I think any place where long
text/errors can be outputted, a log file is preferred because one
could later parse it to know the exact details. Also, splitting the
log as proposed here or in pg_upgrade helps to navigate the LOG like
is the problem in start/stop of the server or a pub-sub setup?
Similarly the log can be splitted for pub/sub specific information.
There appears to be some useful information like:
pg_createsubscriber: warning: two_phase option will not be enabled for
replication slots
pg_createsubscriber: detail: Subscriptions will be created with the
two_phase option disabled. Prepared transactions will be replicated at
COMMIT PREPARED.
pg_createsubscriber: hint: You can use the command-line option
--enable-two-phase to enable two_phase.
I think it will be useful to LOG this separately from the main LOG [1]
(which can contain server specific info as follows) so that users can
consider running pg_createsubscriber with additional options or
changing the subscriber configuration once setup is complete.
[1]:
[startup] LOG: database system was interrupted; last known up at
2025-12-17 14:46:07 IST
[startup] LOG: starting backup recovery with redo LSN 0/06000028,
checkpoint LSN 0/06000080, on timeline ID 1
[startup] LOG: entering standby mode
[startup] LOG: redo starts at 0/06000028
[startup] LOG: completed backup recovery with redo LSN 0/06000028 and
end LSN 0/06000120
[startup] LOG: consistent recovery state reached at 0/06000120
--
With Regards,
Amit Kapila.
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
@ 2025-12-23 23:22 ` Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
0 siblings, 1 reply; 55+ messages in thread
From: Gyan Sreejith @ 2025-12-23 23:22 UTC (permalink / raw)
To: Amit Kapila <[email protected]>; +Cc: Euler Taveira <[email protected]>; vignesh C <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
Thank you for the feedback everybody. As I read through this email chain, I
found differing opinions on how logging should be implemented. This
ambiguity leaves me unsure as to which solution(s) to pursue. As of right
now, I have attached the git-format patch like Hayato Kuroda recommended
(but it does not have any new changes). I am willing to implement whatever
solution when we reach a consensus.
Thank you for all of the help,
Gyan Sreejith
On Thu, Dec 18, 2025 at 1:49 AM Amit Kapila <[email protected]> wrote:
> On Thu, Dec 18, 2025 at 6:59 AM Euler Taveira <[email protected]> wrote:
> >
> > On Wed, Dec 17, 2025, at 7:07 AM, vignesh C wrote:
> > >
> > > By providing this as an option, users can store the log files outside
> > > the data directory, eliminating the need for any additional handling
> > > during backups.
> > >
> >
> > Do we really need an option to capture the stdout / stderr output to a
> file? I
> > doubt it. There is already various ways to capture. psql and pg_upgrade
> are the
> > only tools that have this option.
> >
>
> pg_ctl also has the -l option. I think any place where long
> text/errors can be outputted, a log file is preferred because one
> could later parse it to know the exact details. Also, splitting the
> log as proposed here or in pg_upgrade helps to navigate the LOG like
> is the problem in start/stop of the server or a pub-sub setup?
> Similarly the log can be splitted for pub/sub specific information.
> There appears to be some useful information like:
>
> pg_createsubscriber: warning: two_phase option will not be enabled for
> replication slots
> pg_createsubscriber: detail: Subscriptions will be created with the
> two_phase option disabled. Prepared transactions will be replicated at
> COMMIT PREPARED.
> pg_createsubscriber: hint: You can use the command-line option
> --enable-two-phase to enable two_phase.
>
> I think it will be useful to LOG this separately from the main LOG [1]
> (which can contain server specific info as follows) so that users can
> consider running pg_createsubscriber with additional options or
> changing the subscriber configuration once setup is complete.
>
> [1]:
> [startup] LOG: database system was interrupted; last known up at
> 2025-12-17 14:46:07 IST
> [startup] LOG: starting backup recovery with redo LSN 0/06000028,
> checkpoint LSN 0/06000080, on timeline ID 1
> [startup] LOG: entering standby mode
> [startup] LOG: redo starts at 0/06000028
> [startup] LOG: completed backup recovery with redo LSN 0/06000028 and
> end LSN 0/06000120
> [startup] LOG: consistent recovery state reached at 0/06000120
>
> --
> With Regards,
> Amit Kapila.
>
Attachments:
[application/octet-stream] 0001-Add-a-new-argument-l-logdir-to-pg_createsubscriber.patch (9.5K, 3-0001-Add-a-new-argument-l-logdir-to-pg_createsubscriber.patch)
download | inline diff:
From cf182c65d2add37042f88515508e96439982ee44 Mon Sep 17 00:00:00 2001
From: Gyan Sreejith <[email protected]>
Date: Tue, 23 Dec 2025 18:07:10 -0500
Subject: [PATCH] Add a new argument -l <logdir> to pg_createsubscriber.
Enabling the option will create three new log files -
1. logdir/pg_createsubscriber_server.log - captures messages related to starting and stopping the standby server.
2. logdir/pg_createsubscriber_resetwal.log - captures the output of pg_resetwal.
3. logdir/pg_createsubscriber_internal.log - captures internal diagnostic output from pg_createsubscriber.
---
doc/src/sgml/ref/pg_createsubscriber.sgml | 27 ++++++++
src/bin/pg_basebackup/pg_createsubscriber.c | 63 +++++++++++++++++--
.../t/040_pg_createsubscriber.pl | 36 ++++++++++-
3 files changed, 121 insertions(+), 5 deletions(-)
diff --git a/doc/src/sgml/ref/pg_createsubscriber.sgml b/doc/src/sgml/ref/pg_createsubscriber.sgml
index bb9cc72576c..5e24a297566 100644
--- a/doc/src/sgml/ref/pg_createsubscriber.sgml
+++ b/doc/src/sgml/ref/pg_createsubscriber.sgml
@@ -136,6 +136,33 @@ PostgreSQL documentation
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>-l <replaceable class="parameter">directory</replaceable></option></term>
+ <term><option>--logdir=<replaceable class="parameter">directory</replaceable></option></term>
+ <listitem>
+ <para>
+ The target directory in which the following three log files will be created:
+ <itemizedlist>
+ <listitem>
+ <para>
+ pg_createsubscriber_server.log which captures logs related to stopping and starting the standby server,
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ pg_createsubscriber_resetwal.log which captures the output of pg_resetwal, and
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ pg_createsubscriber_internal.log which captures internal diagnostic output (validations, checks, etc.)
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>-n</option></term>
<term><option>--dry-run</option></term>
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;
+
+ 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);
- pg_log_info("checking settings on publisher");
+ 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;
+
+ 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);
- pg_log_info("checking settings on subscriber");
+ 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..1712adc0439 100644
--- a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
+++ b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
@@ -13,7 +13,8 @@ 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.
@@ -537,10 +538,43 @@ 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();
--
2.43.0
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
@ 2025-12-29 11:10 ` vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
0 siblings, 1 reply; 55+ messages in thread
From: vignesh C @ 2025-12-29 11:10 UTC (permalink / raw)
To: Gyan Sreejith <[email protected]>; +Cc: Amit Kapila <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
On Wed, 24 Dec 2025 at 04:52, Gyan Sreejith <[email protected]> wrote:
>
> Thank you for the feedback everybody. As I read through this email chain, I found differing opinions on how logging should be implemented. This ambiguity leaves me unsure as to which solution(s) to pursue. As of right now, I have attached the git-format patch like Hayato Kuroda recommended (but it does not have any new changes). I am willing to implement whatever solution when we reach a consensus.
Few comments:
1) The file permissions are 664 for pg_createsubscriber_internal.log,
pg_createsubscriber_resetwal.log but 600 for
pg_createsubscriber_server.log. The permissions should be the same for
all the files.
...
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, out_file);
pg_log_debug("pg_resetwal command is: %s", cmd_str);
...
...
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);
...
2) Can you gracefully handle the case where permissions are not
enough in the directory and throw proper error:
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);
}
3) Currently there is no timestamp included for
pg_createsubscriber_internal and pg_createsubscriber_resetwal log file
contents. Without that it is difficult to tell when the operations
were done. It will be good to include them.
4) The patch does not apply on the head, kindly rebase on top of head.
5) Do you need to open and close the log file each time?
...
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");
...
...
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");
...
Regards,
Vignesh
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
@ 2026-03-05 14:48 ` Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
0 siblings, 1 reply; 55+ messages in thread
From: Euler Taveira @ 2026-03-05 14:48 UTC (permalink / raw)
To: Gyan Sreejith <[email protected]>; vignesh C <[email protected]>; +Cc: Amit Kapila <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
On Tue, Feb 24, 2026, at 8:55 PM, Gyan Sreejith wrote:
> I have made the changes you suggested and have attached the patch below.
>
[Avoid top-posting ...]
I took another look at this patch. Comments are below:
+/*
+ * Open a new logfile with proper permissions.
+ * From src/backend/postmaster/syslogger.c
+ */
+static FILE *
+logfile_open(const char *filename, const char *mode)
+{
Don't duplicate code. If you are reusing a function, my advice is to move it to
src/common. You can always use "ifdef FRONTEND" to use the appropriate log
message (elog/ereport vs pg_error, for example).
- * XXX this code was extracted from BootStrapXLOG().
+ * XXX this code was extracted from BootStrapXpg_log_info().
This is a typo.
+ case 'l':
+ {
+ char timestamp[128];
+ struct timeval tval;
+ time_t now;
+ struct tm tmbuf;
I would expect to have this code in a function. That's the pattern it is using.
+ strftime(timestamp, sizeof(timestamp), "%Y-%m-%d-%H-%M-%S", localtime_r(&now, &tmbuf));
+ /* append microseconds */
+ snprintf(timestamp + strlen(timestamp), sizeof(timestamp) - strlen(timestamp),
+ ".%06u", (unsigned int) (tval.tv_usec));
+ log_timestamp = pg_strdup(timestamp);
Do we really need microseconds? I would say milliseconds is sufficient. I
suggest to remove the dash (-); it is using a different style from existing
code (pg_upgrade).
+ opt.log_dir = pg_strdup(optarg);
+ canonicalize_path(opt.log_dir);
You didn't have a check for long path. It is not a good idea in general. See
MAXPGPATH examples. It would be a good idea if the path are restricted to
MAXPGPATH length.
+ if (errno == ENOENT)
+ {
+ if (mkdir(opt.log_dir, S_IRWXU) == 0)
+ pg_log_info("log directory created");
+ else if (errno == EACCES)
+ pg_fatal("permission denied trying to create log directory \"%s\": %m", opt.log_dir);
+ else
+ pg_fatal("could not create log directory \"%s\": %m", opt.log_dir);
+ }
+ else if (errno == EACCES)
+ pg_fatal("permission denied trying to access directory \"%s\": %m", opt.log_dir);
+ else
+ pg_fatal("could not access directory \"%s\": %m", opt.log_dir);
The "permission denied" is redundant here because it will be in %m. Instead, I
suggest that you use
could not create directory \"%s\": %m
The main advantage is that this sentence is already available. It avoids
translation effort.
+#undef pg_log_info
+#define pg_log_info(...) do{\
+ if (internal_log_file_fp != NULL) \
+ internal_log_file_write(__VA_ARGS__); \
+ else \
+ pg_log_generic(PG_LOG_INFO,PG_LOG_PRIMARY,__VA_ARGS__);\
+} while(0)
I don't like the fact that internal_log_file_fp is not declared before this
#define.
One of the arguments to have this feature was that pg_createsubscriber mixes the
server and tool messages. Couldn't we fix it adding "marks" on the output saying
the server log messages starts here and the server log messages ends here?
--
Euler Taveira
EDB https://www.enterprisedb.com/
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
@ 2026-03-06 10:50 ` Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
0 siblings, 1 reply; 55+ messages in thread
From: Amit Kapila @ 2026-03-06 10:50 UTC (permalink / raw)
To: Euler Taveira <[email protected]>; +Cc: Gyan Sreejith <[email protected]>; vignesh C <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
On Thu, Mar 5, 2026 at 8:19 PM Euler Taveira <[email protected]> wrote:
>
> One of the arguments to have this feature was that pg_createsubscriber mixes the
> server and tool messages. Couldn't we fix it adding "marks" on the output saying
> the server log messages starts here and the server log messages ends here?
>
It will simplify the output to some extent but still it will be mixed
at different intervals in the LOG which is not good for grepping
required info. Also, I am not sure if we use such markers at other
places.
--
With Regards,
Amit Kapila.
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
@ 2026-03-09 22:55 ` Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
0 siblings, 1 reply; 55+ messages in thread
From: Gyan Sreejith @ 2026-03-09 22:55 UTC (permalink / raw)
To: Amit Kapila <[email protected]>; +Cc: Euler Taveira <[email protected]>; vignesh C <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
On Thu, Mar 5, 2026 at 9:49 AM Euler Taveira <[email protected]> wrote:
Don't duplicate code. If you are reusing a function, my advice is to move
> it to
> src/common. You can always use "ifdef FRONTEND" to use the appropriate log
> message (elog/ereport vs pg_error, for example).
I have made all the changes except for this one, and I am deferring to Amit
Kapila regarding the marks.
Regarding duplicating code - I was trying to keep the change contained. Is
it okay to make that change in a separate check in since it would also
touch src/backend/postmaster/syslogger.c?
Thank you!
Gyan Sreejith
On Fri, Mar 6, 2026 at 5:51 AM Amit Kapila <[email protected]> wrote:
> On Thu, Mar 5, 2026 at 8:19 PM Euler Taveira <[email protected]> wrote:
> >
> > One of the arguments to have this feature was that pg_createsubscriber
> mixes the
> > server and tool messages. Couldn't we fix it adding "marks" on the
> output saying
> > the server log messages starts here and the server log messages ends
> here?
> >
>
> It will simplify the output to some extent but still it will be mixed
> at different intervals in the LOG which is not good for grepping
> required info. Also, I am not sure if we use such markers at other
> places.
>
> --
> With Regards,
> Amit Kapila.
>
Attachments:
[application/octet-stream] v7-0001-Add-a-new-argument-l-logdir-to-pg_createsubscribe.patch (12.6K, 3-v7-0001-Add-a-new-argument-l-logdir-to-pg_createsubscribe.patch)
download | inline diff:
From 862ba2419febe70c581631478677f7f083478115 Mon Sep 17 00:00:00 2001
From: Gyan Sreejith <[email protected]>
Date: Sun, 8 Mar 2026 20:06:49 -0400
Subject: [PATCH v7] Add a new argument -l <logdir> to pg_createsubscriber.
Enabling the option to write messages to log files in the specified directory.
A new directory is created if required. A subdirectory is created with timestamp as its name, and it will contain two new logfiles:
1. pg_createsubscriber_server.log - captures messages related to starting and stopping the standby server.
2. pg_createsubscriber_internal.log - captures internal diagnostic output from pg_createsubscriber.
For example, if we specify -l abc as an argument, and if the timestamp on running it is 20260119T204317.204, a directory abc is created if it doesn't exist already, with 20260119T204317.204 as its subdirectory and it will contain the two log files pg_createsubscriber_server.log and pg_createsubscriber_internal.log
---
doc/src/sgml/ref/pg_createsubscriber.sgml | 22 +++
src/bin/pg_basebackup/pg_createsubscriber.c | 167 +++++++++++++++++-
.../t/040_pg_createsubscriber.pl | 12 +-
3 files changed, 197 insertions(+), 4 deletions(-)
diff --git a/doc/src/sgml/ref/pg_createsubscriber.sgml b/doc/src/sgml/ref/pg_createsubscriber.sgml
index cf45ff3573d..67a683e66c7 100644
--- a/doc/src/sgml/ref/pg_createsubscriber.sgml
+++ b/doc/src/sgml/ref/pg_createsubscriber.sgml
@@ -136,6 +136,28 @@ PostgreSQL documentation
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>-l <replaceable class="parameter">directory</replaceable></option></term>
+ <term><option>--logdir=<replaceable class="parameter">directory</replaceable></option></term>
+ <listitem>
+ <para>
+ 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.
+ <itemizedlist>
+ <listitem>
+ <para>
+ pg_createsubscriber_server.log which captures logs related to stopping and starting the standby server,
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ pg_createsubscriber_internal.log which captures internal diagnostic output (validations, checks, etc.)
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>-n</option></term>
<term><option>--dry-run</option></term>
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index 2bc84505aab..c152467b09a 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -49,10 +49,14 @@
#define INCLUDED_CONF_FILE "pg_createsubscriber.conf"
#define INCLUDED_CONF_FILE_DISABLED INCLUDED_CONF_FILE ".disabled"
+#define SERVER_LOG_FILE_NAME "pg_createsubscriber_server"
+#define INTERNAL_LOG_FILE_NAME "pg_createsubscriber_internal"
+
/* Command-line options */
struct CreateSubscriberOptions
{
char *config_file; /* configuration file */
+ char *log_dir; /* log directory name */
char *pub_conninfo_str; /* publisher connection string */
char *socket_dir; /* directory for Unix-domain socket, if any */
char *sub_port; /* subscriber port number */
@@ -146,6 +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)));
+
#define WAIT_INTERVAL 1 /* 1 second */
@@ -167,6 +174,10 @@ static pg_prng_state prng_state;
static char *pg_ctl_path = NULL;
static char *pg_resetwal_path = NULL;
+static FILE *internal_log_file_fp = NULL; /* File ptr to log all messages to */
+static char *log_timestamp = NULL; /* Timestamp to be used in all log file
+ * names */
+
/* standby / subscriber data directory */
static char *subscriber_dir = NULL;
@@ -174,6 +185,123 @@ static bool recovery_ended = false;
static bool standby_running = false;
static bool recovery_params_set = false;
+#undef pg_log_info
+#define pg_log_info(...) do{\
+ if (internal_log_file_fp != NULL) \
+ internal_log_file_write(__VA_ARGS__); \
+ else \
+ pg_log_generic(PG_LOG_INFO,PG_LOG_PRIMARY,__VA_ARGS__);\
+} while(0)
+
+#undef pg_log_info_hint
+#define pg_log_info_hint(...) do{\
+ if (internal_log_file_fp != NULL) \
+ internal_log_file_write(__VA_ARGS__); \
+ else \
+ 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)) \
+ pg_log_generic(PG_LOG_DEBUG, PG_LOG_PRIMARY, __VA_ARGS__); \
+} while(0)
+
+#undef pg_fatal
+#define pg_fatal(...) do{\
+ if (internal_log_file_fp != NULL) \
+ internal_log_file_write(__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,...)
+{
+ if (internal_log_file_fp != NULL)
+ {
+ 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);
+ }
+}
+
+/*
+ * Open a new logfile with proper permissions.
+ * From src/backend/postmaster/syslogger.c
+ */
+static FILE *
+logfile_open(const char *filename, const char *mode)
+{
+ FILE *fh;
+ mode_t oumask;
+
+ oumask = umask((mode_t) ((~(S_IRUSR | S_IWUSR)) & (S_IRWXU | S_IRWXG | S_IRWXO)));
+ fh = fopen(filename, mode);
+ umask(oumask);
+
+ if (fh)
+ {
+ setvbuf(fh, NULL, PG_IOLBF, 0);
+
+#ifdef WIN32
+ /* use CRLF line endings on Windows */
+ _setmode(_fileno(fh), _O_TEXT);
+#endif
+ }
+ else
+ pg_fatal("could not open log file \"%s\": %m",
+ filename);
+
+ return fh;
+}
+
+static void
+make_dir(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);
+ }
+}
+
+static void
+make_output_dirs(char *log_dir)
+{
+ char timestamp[128];
+ struct timeval tval;
+ time_t now;
+ struct tm tmbuf;
+ char timestamp_dir[MAXPGPATH];
+ int len;
+
+ gettimeofday(&tval, NULL);
+ now = tval.tv_sec;
+ 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));
+ log_timestamp = pg_strdup(timestamp);
+
+ make_dir(log_dir);
+ 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);
+ make_dir(timestamp_dir);
+}
/*
* Clean up objects created by pg_createsubscriber.
@@ -269,6 +397,12 @@ cleanup_objects_atexit(void)
if (standby_running)
stop_standby_server(subscriber_dir);
+
+ if (internal_log_file_fp != NULL)
+ {
+ fclose(internal_log_file_fp);
+ internal_log_file_fp = NULL;
+ }
}
static void
@@ -283,6 +417,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"));
@@ -702,6 +837,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");
@@ -735,8 +871,14 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
else
pg_log_info("running pg_resetwal on the subscriber");
- cmd_str = psprintf("\"%s\" -D \"%s\" > \"%s\"", pg_resetwal_path,
- subscriber_dir, DEVNULL);
+
+ if (opt->log_dir != NULL)
+ out_file = psprintf("%s/%s/%s.log", opt->log_dir, log_timestamp, SERVER_LOG_FILE_NAME);
+ else
+ out_file = DEVNULL;
+
+ cmd_str = psprintf("\"%s\" -D \"%s\" >> \"%s\"", pg_resetwal_path,
+ subscriber_dir, out_file);
pg_log_debug("pg_resetwal command is: %s", cmd_str);
@@ -1650,6 +1792,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/%s/%s.log", opt->log_dir, log_timestamp, SERVER_LOG_FILE_NAME);
+ }
+
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);
@@ -2181,6 +2328,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'},
@@ -2215,6 +2363,7 @@ 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);
@@ -2239,6 +2388,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;
@@ -2267,7 +2417,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)
@@ -2288,6 +2438,14 @@ 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);
+ 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;
break;
@@ -2621,5 +2779,8 @@ main(int argc, char **argv)
pg_log_info("Done!");
+ if (internal_log_file_fp != NULL)
+ fclose(internal_log_file_fp);
+
return 0;
}
diff --git a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
index 0c27fca7bb7..7a4b9ca433a 100644
--- a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
+++ b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
@@ -13,7 +13,8 @@ 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.
@@ -362,9 +363,18 @@ command_ok(
'--subscription' => 'sub2',
'--database' => $db1,
'--database' => $db2,
+ '--logdir' => $logdir,
],
'run pg_createsubscriber --dry-run on node S');
+# Check that the log files were created
+my @server_log_files = glob "$logdir/*/pg_createsubscriber_server.log";
+is( scalar(@server_log_files), 1, "
+ pg_createsubscriber_server.log file was created");
+my @internal_log_files = glob "$logdir/*/pg_createsubscriber_internal.log";
+is( scalar(@internal_log_files), 1, "
+ pg_createsubscriber_internal.log file was created");
+
# Check if node S is still a standby
$node_s->start;
is($node_s->safe_psql('postgres', 'SELECT pg_catalog.pg_is_in_recovery()'),
--
2.43.0
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
@ 2026-03-11 10:04 ` vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
0 siblings, 1 reply; 55+ messages in thread
From: vignesh C @ 2026-03-11 10:04 UTC (permalink / raw)
To: Gyan Sreejith <[email protected]>; +Cc: Amit Kapila <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
On Tue, 10 Mar 2026 at 04:26, Gyan Sreejith <[email protected]> wrote:
>
> On Thu, Mar 5, 2026 at 9:49 AM Euler Taveira <[email protected]> wrote:
>
>> Don't duplicate code. If you are reusing a function, my advice is to move it to
>> src/common. You can always use "ifdef FRONTEND" to use the appropriate log
>> message (elog/ereport vs pg_error, for example).
>
>
> I have made all the changes except for this one, and I am deferring to Amit Kapila regarding the marks.
Few comments:
1) You are not checking log level because of which the contents are
logged irrespective of the log level:
+#undef pg_log_info
+#define pg_log_info(...) do{\
+ if (internal_log_file_fp != NULL) \
+ internal_log_file_write(__VA_ARGS__); \
+ else \
+ pg_log_generic(PG_LOG_INFO,PG_LOG_PRIMARY,__VA_ARGS__);\
+} while(0)
+
+#undef pg_log_info_hint
+#define pg_log_info_hint(...) do{\
+ if (internal_log_file_fp != NULL) \
+ internal_log_file_write(__VA_ARGS__); \
+ else \
+ 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)) \
+ pg_log_generic(PG_LOG_DEBUG, PG_LOG_PRIMARY,
__VA_ARGS__); \
+} while(0)
2) Instead of just checking if the file is created or not, let's check
for some contents from the file:
+# Check that the log files were created
+my @server_log_files = glob "$logdir/*/pg_createsubscriber_server.log";
+is( scalar(@server_log_files), 1, "
+ pg_createsubscriber_server.log file was created");
+my @internal_log_files = glob "$logdir/*/pg_createsubscriber_internal.log";
+is( scalar(@internal_log_files), 1, "
+ pg_createsubscriber_internal.log file was created");
3) This change is not required, let's remove this:
--- a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
+++ b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
@@ -13,7 +13,8 @@ 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";
4) No need of '{' as it is a single line statement
if (opt->log_dir != NULL)
{
appendPQExpBuffer(pg_ctl_cmd, " -l %s/%s/%s.log", opt->log_dir,
log_timestamp, SERVER_LOG_FILE_NAME);
}
Regards,
Vignesh
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
@ 2026-03-15 23:23 ` Gyan Sreejith <[email protected]>
2026-03-17 06:05 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber shveta malik <[email protected]>
2026-03-17 12:18 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
0 siblings, 3 replies; 55+ messages in thread
From: Gyan Sreejith @ 2026-03-15 23:23 UTC (permalink / raw)
To: vignesh C <[email protected]>; +Cc: Amit Kapila <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
On Wed, Mar 11, 2026 at 6:05 AM vignesh C <[email protected]> wrote:
> On Tue, 10 Mar 2026 at 04:26, Gyan Sreejith <[email protected]>
> wrote:
> >
> > On Thu, Mar 5, 2026 at 9:49 AM Euler Taveira <[email protected]> wrote:
> >
> >> Don't duplicate code. If you are reusing a function, my advice is to
> move it to
> >> src/common. You can always use "ifdef FRONTEND" to use the appropriate
> log
> >> message (elog/ereport vs pg_error, for example).
> >
> >
> > I have made all the changes except for this one, and I am deferring to
> Amit Kapila regarding the marks.
>
> Few comments:
> 1) You are not checking log level because of which the contents are
> logged irrespective of the log level:
> +#undef pg_log_info
> +#define pg_log_info(...) do{\
> + if (internal_log_file_fp != NULL) \
> + internal_log_file_write(__VA_ARGS__); \
> + else \
> + pg_log_generic(PG_LOG_INFO,PG_LOG_PRIMARY,__VA_ARGS__);\
> +} while(0)
> +
> +#undef pg_log_info_hint
> +#define pg_log_info_hint(...) do{\
> + if (internal_log_file_fp != NULL) \
> + internal_log_file_write(__VA_ARGS__); \
> + else \
> + 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)) \
> + pg_log_generic(PG_LOG_DEBUG, PG_LOG_PRIMARY,
> __VA_ARGS__); \
> +} while(0)
The log level is passed to and checked by pg_log_generic_v() which is
called by pg_log_generic().
>
>
2) Instead of just checking if the file is created or not, let's check
> for some contents from the file:
>
Added checks to ensure that the log files are not empty, thanks!
>
> 3) This change is not required, let's remove this:
> --- a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
> +++ b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
> @@ -13,7 +13,8 @@ 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";
Fixed
>
>
4) No need of '{' as it is a single line statement
> if (opt->log_dir != NULL)
> {
> appendPQExpBuffer(pg_ctl_cmd, " -l %s/%s/%s.log", opt->log_dir,
> log_timestamp, SERVER_LOG_FILE_NAME);
> }
>
Fixed
Thank you! I have attached the changes.
Regards,
Gyan
Attachments:
[application/x-patch] v8-0001-Add-a-new-argument-l-logdir-to-pg_createsubscribe.patch (12.8K, 3-v8-0001-Add-a-new-argument-l-logdir-to-pg_createsubscribe.patch)
download | inline diff:
From 7a2073c4be92e73619e3f19ea175dc5e224c4a18 Mon Sep 17 00:00:00 2001
From: Gyan Sreejith <[email protected]>
Date: Sun, 15 Mar 2026 18:40:15 -0400
Subject: [PATCH v8] Add a new argument -l <logdir> to pg_createsubscriber.
Enabling the option to write messages to log files in the specified directory.
A new directory is created if required. A subdirectory is created with timestamp as its name, and it will contain two new logfiles:
1. pg_createsubscriber_server.log - captures messages related to starting and stopping the standby server.
2. pg_createsubscriber_internal.log - captures internal diagnostic output from pg_createsubscriber.
For example, if we specify -l abc as an argument, and if the timestamp on running it is 20260119T204317.204, a directory abc is created if it doesn't exist already, with 20260119T204317.204 as its subdirectory and it will contain the two log files pg_createsubscriber_server.log and pg_createsubscriber_internal.log
---
doc/src/sgml/ref/pg_createsubscriber.sgml | 22 +++
src/bin/pg_basebackup/pg_createsubscriber.c | 165 +++++++++++++++++-
.../t/040_pg_createsubscriber.pl | 17 ++
3 files changed, 201 insertions(+), 3 deletions(-)
diff --git a/doc/src/sgml/ref/pg_createsubscriber.sgml b/doc/src/sgml/ref/pg_createsubscriber.sgml
index cf45ff3573d..67a683e66c7 100644
--- a/doc/src/sgml/ref/pg_createsubscriber.sgml
+++ b/doc/src/sgml/ref/pg_createsubscriber.sgml
@@ -136,6 +136,28 @@ PostgreSQL documentation
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>-l <replaceable class="parameter">directory</replaceable></option></term>
+ <term><option>--logdir=<replaceable class="parameter">directory</replaceable></option></term>
+ <listitem>
+ <para>
+ 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.
+ <itemizedlist>
+ <listitem>
+ <para>
+ pg_createsubscriber_server.log which captures logs related to stopping and starting the standby server,
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ pg_createsubscriber_internal.log which captures internal diagnostic output (validations, checks, etc.)
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>-n</option></term>
<term><option>--dry-run</option></term>
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index 2bc84505aab..3495bdb88ce 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -49,10 +49,14 @@
#define INCLUDED_CONF_FILE "pg_createsubscriber.conf"
#define INCLUDED_CONF_FILE_DISABLED INCLUDED_CONF_FILE ".disabled"
+#define SERVER_LOG_FILE_NAME "pg_createsubscriber_server"
+#define INTERNAL_LOG_FILE_NAME "pg_createsubscriber_internal"
+
/* Command-line options */
struct CreateSubscriberOptions
{
char *config_file; /* configuration file */
+ char *log_dir; /* log directory name */
char *pub_conninfo_str; /* publisher connection string */
char *socket_dir; /* directory for Unix-domain socket, if any */
char *sub_port; /* subscriber port number */
@@ -146,6 +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)));
+
#define WAIT_INTERVAL 1 /* 1 second */
@@ -167,6 +174,10 @@ static pg_prng_state prng_state;
static char *pg_ctl_path = NULL;
static char *pg_resetwal_path = NULL;
+static FILE *internal_log_file_fp = NULL; /* File ptr to log all messages to */
+static char *log_timestamp = NULL; /* Timestamp to be used in all log file
+ * names */
+
/* standby / subscriber data directory */
static char *subscriber_dir = NULL;
@@ -174,6 +185,123 @@ static bool recovery_ended = false;
static bool standby_running = false;
static bool recovery_params_set = false;
+#undef pg_log_info
+#define pg_log_info(...) do{\
+ if (internal_log_file_fp != NULL) \
+ internal_log_file_write(__VA_ARGS__); \
+ else \
+ pg_log_generic(PG_LOG_INFO, PG_LOG_PRIMARY, __VA_ARGS__);\
+} while(0)
+
+#undef pg_log_info_hint
+#define pg_log_info_hint(...) do{\
+ if (internal_log_file_fp != NULL) \
+ internal_log_file_write(__VA_ARGS__); \
+ else \
+ 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)) \
+ pg_log_generic(PG_LOG_DEBUG, PG_LOG_PRIMARY, __VA_ARGS__); \
+} while(0)
+
+#undef pg_fatal
+#define pg_fatal(...) do{\
+ if (internal_log_file_fp != NULL) \
+ internal_log_file_write(__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,...)
+{
+ if (internal_log_file_fp != NULL)
+ {
+ 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);
+ }
+}
+
+/*
+ * Open a new logfile with proper permissions.
+ * From src/backend/postmaster/syslogger.c
+ */
+static FILE *
+logfile_open(const char *filename, const char *mode)
+{
+ FILE *fh;
+ mode_t oumask;
+
+ oumask = umask((mode_t) ((~(S_IRUSR | S_IWUSR)) & (S_IRWXU | S_IRWXG | S_IRWXO)));
+ fh = fopen(filename, mode);
+ umask(oumask);
+
+ if (fh)
+ {
+ setvbuf(fh, NULL, PG_IOLBF, 0);
+
+#ifdef WIN32
+ /* use CRLF line endings on Windows */
+ _setmode(_fileno(fh), _O_TEXT);
+#endif
+ }
+ else
+ pg_fatal("could not open log file \"%s\": %m",
+ filename);
+
+ return fh;
+}
+
+static void
+make_dir(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);
+ }
+}
+
+static void
+make_output_dirs(char *log_dir)
+{
+ char timestamp[128];
+ struct timeval tval;
+ time_t now;
+ struct tm tmbuf;
+ char timestamp_dir[MAXPGPATH];
+ int len;
+
+ gettimeofday(&tval, NULL);
+ now = tval.tv_sec;
+ 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));
+ log_timestamp = pg_strdup(timestamp);
+
+ make_dir(log_dir);
+ 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);
+ make_dir(timestamp_dir);
+}
/*
* Clean up objects created by pg_createsubscriber.
@@ -269,6 +397,12 @@ cleanup_objects_atexit(void)
if (standby_running)
stop_standby_server(subscriber_dir);
+
+ if (internal_log_file_fp != NULL)
+ {
+ fclose(internal_log_file_fp);
+ internal_log_file_fp = NULL;
+ }
}
static void
@@ -283,6 +417,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"));
@@ -702,6 +837,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");
@@ -735,8 +871,14 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
else
pg_log_info("running pg_resetwal on the subscriber");
- cmd_str = psprintf("\"%s\" -D \"%s\" > \"%s\"", pg_resetwal_path,
- subscriber_dir, DEVNULL);
+
+ if (opt->log_dir != NULL)
+ out_file = psprintf("%s/%s/%s.log", opt->log_dir, log_timestamp, SERVER_LOG_FILE_NAME);
+ else
+ out_file = DEVNULL;
+
+ cmd_str = psprintf("\"%s\" -D \"%s\" >> \"%s\"", pg_resetwal_path,
+ subscriber_dir, out_file);
pg_log_debug("pg_resetwal command is: %s", cmd_str);
@@ -1650,6 +1792,9 @@ 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/%s/%s.log", opt->log_dir, log_timestamp, SERVER_LOG_FILE_NAME);
+
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);
@@ -2181,6 +2326,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'},
@@ -2215,6 +2361,7 @@ 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);
@@ -2239,6 +2386,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;
@@ -2267,7 +2415,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)
@@ -2288,6 +2436,14 @@ 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);
+ 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;
break;
@@ -2621,5 +2777,8 @@ main(int argc, char **argv)
pg_log_info("Done!");
+ if (internal_log_file_fp != NULL)
+ fclose(internal_log_file_fp);
+
return 0;
}
diff --git a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
index 0c27fca7bb7..3a82f893e28 100644
--- a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
+++ b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
@@ -14,6 +14,7 @@ program_version_ok('pg_createsubscriber');
program_options_handling_ok('pg_createsubscriber');
my $datadir = PostgreSQL::Test::Utils::tempdir;
+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.
@@ -362,9 +363,25 @@ command_ok(
'--subscription' => 'sub2',
'--database' => $db1,
'--database' => $db2,
+ '--logdir' => $logdir,
],
'run pg_createsubscriber --dry-run on node S');
+# Check that the log files were created
+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);
+isnt($server_log_file_size, 0,
+ "pg_createsubscriber_server.log file not empty");
+
+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);
+isnt($internal_log_file_size, 0,
+ "pg_createsubscriber_internal.log file not empty");
+
# Check if node S is still a standby
$node_s->start;
is($node_s->safe_psql('postgres', 'SELECT pg_catalog.pg_is_in_recovery()'),
--
2.43.0
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
@ 2026-03-17 06:05 ` shveta malik <[email protected]>
2026-03-17 10:40 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2 siblings, 1 reply; 55+ messages in thread
From: shveta malik @ 2026-03-17 06:05 UTC (permalink / raw)
To: Gyan Sreejith <[email protected]>; +Cc: vignesh C <[email protected]>; Amit Kapila <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>; shveta malik <[email protected]>
On Mon, Mar 16, 2026 at 4:53 AM Gyan Sreejith <[email protected]> wrote:
>
>
> Thank you! I have attached the changes.
Thank You for the patch.
1)
I observed that when we give both --verbose and --logdir options, it
skips verbose and only dumps logs to logdir. Is this intentional?
I don't see any other postgres command with both the options provided
and thus cannot compare the behaviour. But IMO, if the user has
explicitly given both options, we should dump the log on both (stdout
and logfile). Thoughts? (Apologies if this has already been discussed
and I missed it.)
2)
I noticed a minor difference when running the command with both
--verbose and --logdir, depending on the order in which they are
specified.
If --verbose is specified first, I see the following message on the console:
pg_createsubscriber: directory ../../pg_createsub/20260317T112204.848 created
But if --logdir is specified first, this message does not appear on
the console. Is this behavior intentional?
thanks
Shveta
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 06:05 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber shveta malik <[email protected]>
@ 2026-03-17 10:40 ` Amit Kapila <[email protected]>
0 siblings, 0 replies; 55+ messages in thread
From: Amit Kapila @ 2026-03-17 10:40 UTC (permalink / raw)
To: shveta malik <[email protected]>; +Cc: Gyan Sreejith <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
On Tue, Mar 17, 2026 at 11:36 AM shveta malik <[email protected]> wrote:
>
> 1)
> I observed that when we give both --verbose and --logdir options, it
> skips verbose and only dumps logs to logdir. Is this intentional?
>
> I don't see any other postgres command with both the options provided
> and thus cannot compare the behaviour. But IMO, if the user has
> explicitly given both options, we should dump the log on both (stdout
> and logfile).
>
The entire log should be dumped in the specified log_file. I think
this is similar to one setting server's log_level to debug1 and then
start server with -l option in which case the entire log is dumped to
server log file.
--
With Regards,
Amit Kapila.
^ permalink raw reply [nested|flat] 55+ messages in thread
* RE: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
@ 2026-03-17 12:18 ` Hayato Kuroda (Fujitsu) <[email protected]>
2 siblings, 0 replies; 55+ messages in thread
From: Hayato Kuroda (Fujitsu) @ 2026-03-17 12:18 UTC (permalink / raw)
To: 'Gyan Sreejith' <[email protected]>; vignesh C <[email protected]>; +Cc: Amit Kapila <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
Dear Gayn,
Thanks for updating the patch. I resumed reviewing.
01.
```
+static void
+ internal_log_file_write(const char *format,...) __attribute__((format(printf, 1, 2)));
```
pg_attribute_printf seems to be used in postgres.
02.
```
+static void
+make_dir(char *dir)
```
Since there are only two callers, I think no need to introduce the funciton.
E.g, make_outpudirs in pg_upgrade does mkdir() four times.
03.
```
#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)) \
pg_log_generic(PG_LOG_DEBUG, PG_LOG_PRIMARY, __VA_ARGS__); \
} while(0)
```
The patch ignores setting of log level if -l is specified. By default only
warnings/errors/fatals should be output, but even debug messages are output with
-l option. Checking logic is in pg_log_generic()->pg_log_generic_v() but we
cannot reach if internal_log_file_fp is set.
Also, are there any examples to undefine these macros? It's bit surprising for
me; I prefer some inline functions instead.
04.
```
+ 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);
```
I feel creating the log file is too early, otherwise the issue Shveta raised the
at #2 could happen [1].
I feel it's OK putting after the if (opt.pub_conninfo_str == NULL). Because
there is the first place where call pg_log_info, after doing very simple validation.
05.
Let me confirm one point. IIUC, freopen() can be used to replace the stderr to
the file, and this may able to remove all #undef macros. The downside of this
approach is that even ERROR/FATAL messages would be on the file and nothing
appears on the terminal. See DebugFileOpen() the example.
Do you think that we may not use freopen() as-is? Do others have variants?
06.
I added this thread and the cfbot cannot accept your patch [2]. Please fix.
[1]: https://www.postgresql.org/message-id/CAJpy0uBPvz6S9VE8sLYmoju4BGYh94uks%2BUTocPdD094xqmZ2w%40mail.g...
[2]: https://commitfest.postgresql.org/patch/6592/
Best regards,
Hayato Kuroda
FUJITSU LIMITED
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
@ 2026-03-17 12:48 ` Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2 siblings, 1 reply; 55+ messages in thread
From: Shlok Kyal @ 2026-03-17 12:48 UTC (permalink / raw)
To: Gyan Sreejith <[email protected]>; +Cc: vignesh C <[email protected]>; Amit Kapila <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
Hi Gyan,
Thanks for working on the patch.
On Mon, 16 Mar 2026 at 04:53, Gyan Sreejith <[email protected]> wrote:
>
>
>
> On Wed, Mar 11, 2026 at 6:05 AM vignesh C <[email protected]> wrote:
>>
>> On Tue, 10 Mar 2026 at 04:26, Gyan Sreejith <[email protected]> wrote:
>> >
>> > On Thu, Mar 5, 2026 at 9:49 AM Euler Taveira <[email protected]> wrote:
>> >
>> >> Don't duplicate code. If you are reusing a function, my advice is to move it to
>> >> src/common. You can always use "ifdef FRONTEND" to use the appropriate log
>> >> message (elog/ereport vs pg_error, for example).
>> >
>> >
>> > I have made all the changes except for this one, and I am deferring to Amit Kapila regarding the marks.
>>
>> Few comments:
>> 1) You are not checking log level because of which the contents are
>> logged irrespective of the log level:
>> +#undef pg_log_info
>> +#define pg_log_info(...) do{\
>> + if (internal_log_file_fp != NULL) \
>> + internal_log_file_write(__VA_ARGS__); \
>> + else \
>> + pg_log_generic(PG_LOG_INFO,PG_LOG_PRIMARY,__VA_ARGS__);\
>> +} while(0)
>> +
>> +#undef pg_log_info_hint
>> +#define pg_log_info_hint(...) do{\
>> + if (internal_log_file_fp != NULL) \
>> + internal_log_file_write(__VA_ARGS__); \
>> + else \
>> + 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)) \
>> + pg_log_generic(PG_LOG_DEBUG, PG_LOG_PRIMARY,
>> __VA_ARGS__); \
>> +} while(0)
>
> The log level is passed to and checked by pg_log_generic_v() which is called by pg_log_generic().
But we are not checking the log level when we are writing the logs in
the logfiles.
Due to this, extra logs can appear.
>>
>>
>>
>> 2) Instead of just checking if the file is created or not, let's check
>> for some contents from the file:
>
> Added checks to ensure that the log files are not empty, thanks!
I think along with it we should also check the actual contents of each file.
>>
>>
>> 3) This change is not required, let's remove this:
>> --- a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
>> +++ b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
>> @@ -13,7 +13,8 @@ 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";
>
> Fixed
>>
>>
>>
>> 4) No need of '{' as it is a single line statement
>> if (opt->log_dir != NULL)
>> {
>> appendPQExpBuffer(pg_ctl_cmd, " -l %s/%s/%s.log", opt->log_dir,
>> log_timestamp, SERVER_LOG_FILE_NAME);
>> }
>
> Fixed
>
> Thank you! I have attached the changes.
I noticed that we do not define pg_log_warning, pg_log_warning_detail
and pg_log_warning_hint, due to this the contents when option
'--logdir' is specified and when it is not defined can differ. Should
we define these as well?
Also internal_log_file_write is declared as:
+static void
+ internal_log_file_write(const char *format,...)
__attribute__((format(printf, 1, 2)));
Should we use 'pg_attribute_printf' instead of
__attribute__((format(printf, 1, 2)))?
I have added the changes for above in the topup patch. I also did some
cosmetic changes.
I have also addressed the comment (2) by Shveta in [1].
If you agree with these changes, please feel free to include them in the patch.
[1]: https://www.postgresql.org/message-id/CAJpy0uBPvz6S9VE8sLYmoju4BGYh94uks%2BUTocPdD094xqmZ2w%40mail.g...
Thanks,
Shlok Kyal
Attachments:
[application/octet-stream] topup.patch (11.3K, 2-topup.patch)
download | inline diff:
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
<term><option>--logdir=<replaceable class="parameter">directory</replaceable></option></term>
<listitem>
<para>
- 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.
<itemizedlist>
<listitem>
<para>
- 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,
</para>
</listitem>
<listitem>
<para>
- pg_createsubscriber_internal.log which captures internal diagnostic output (validations, checks, etc.)
+ pg_createsubscriber_internal.log which captures internal diagnostic
+ output (validations, checks, etc.)
</para>
</listitem>
</itemizedlist>
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");
[application/octet-stream] v9-0001-Add-a-new-argument-l-logdir-to-pg_createsubscribe.patch (12.8K, 3-v9-0001-Add-a-new-argument-l-logdir-to-pg_createsubscribe.patch)
download | inline diff:
From f10eab6b1cef5907be90fa66b879cddb679dfa82 Mon Sep 17 00:00:00 2001
From: Gyan Sreejith <[email protected]>
Date: Sun, 15 Mar 2026 18:40:15 -0400
Subject: [PATCH v9] Add a new argument -l <logdir> to pg_createsubscriber.
Enabling the option to write messages to log files in the specified directory.
A new directory is created if required. A subdirectory is created with timestamp as its name, and it will contain two new logfiles:
1. pg_createsubscriber_server.log - captures messages related to starting and stopping the standby server.
2. pg_createsubscriber_internal.log - captures internal diagnostic output from pg_createsubscriber.
For example, if we specify -l abc as an argument, and if the timestamp on running it is 20260119T204317.204, a directory abc is created if it doesn't exist already, with 20260119T204317.204 as its subdirectory and it will contain the two log files pg_createsubscriber_server.log and pg_createsubscriber_internal.log
---
doc/src/sgml/ref/pg_createsubscriber.sgml | 22 +++
src/bin/pg_basebackup/pg_createsubscriber.c | 165 +++++++++++++++++-
.../t/040_pg_createsubscriber.pl | 17 ++
3 files changed, 201 insertions(+), 3 deletions(-)
diff --git a/doc/src/sgml/ref/pg_createsubscriber.sgml b/doc/src/sgml/ref/pg_createsubscriber.sgml
index cf45ff3573d..67a683e66c7 100644
--- a/doc/src/sgml/ref/pg_createsubscriber.sgml
+++ b/doc/src/sgml/ref/pg_createsubscriber.sgml
@@ -136,6 +136,28 @@ PostgreSQL documentation
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>-l <replaceable class="parameter">directory</replaceable></option></term>
+ <term><option>--logdir=<replaceable class="parameter">directory</replaceable></option></term>
+ <listitem>
+ <para>
+ 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.
+ <itemizedlist>
+ <listitem>
+ <para>
+ pg_createsubscriber_server.log which captures logs related to stopping and starting the standby server,
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ pg_createsubscriber_internal.log which captures internal diagnostic output (validations, checks, etc.)
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>-n</option></term>
<term><option>--dry-run</option></term>
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index 2bc84505aab..3495bdb88ce 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -49,10 +49,14 @@
#define INCLUDED_CONF_FILE "pg_createsubscriber.conf"
#define INCLUDED_CONF_FILE_DISABLED INCLUDED_CONF_FILE ".disabled"
+#define SERVER_LOG_FILE_NAME "pg_createsubscriber_server"
+#define INTERNAL_LOG_FILE_NAME "pg_createsubscriber_internal"
+
/* Command-line options */
struct CreateSubscriberOptions
{
char *config_file; /* configuration file */
+ char *log_dir; /* log directory name */
char *pub_conninfo_str; /* publisher connection string */
char *socket_dir; /* directory for Unix-domain socket, if any */
char *sub_port; /* subscriber port number */
@@ -146,6 +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)));
+
#define WAIT_INTERVAL 1 /* 1 second */
@@ -167,6 +174,10 @@ static pg_prng_state prng_state;
static char *pg_ctl_path = NULL;
static char *pg_resetwal_path = NULL;
+static FILE *internal_log_file_fp = NULL; /* File ptr to log all messages to */
+static char *log_timestamp = NULL; /* Timestamp to be used in all log file
+ * names */
+
/* standby / subscriber data directory */
static char *subscriber_dir = NULL;
@@ -174,6 +185,123 @@ static bool recovery_ended = false;
static bool standby_running = false;
static bool recovery_params_set = false;
+#undef pg_log_info
+#define pg_log_info(...) do{\
+ if (internal_log_file_fp != NULL) \
+ internal_log_file_write(__VA_ARGS__); \
+ else \
+ pg_log_generic(PG_LOG_INFO, PG_LOG_PRIMARY, __VA_ARGS__);\
+} while(0)
+
+#undef pg_log_info_hint
+#define pg_log_info_hint(...) do{\
+ if (internal_log_file_fp != NULL) \
+ internal_log_file_write(__VA_ARGS__); \
+ else \
+ 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)) \
+ pg_log_generic(PG_LOG_DEBUG, PG_LOG_PRIMARY, __VA_ARGS__); \
+} while(0)
+
+#undef pg_fatal
+#define pg_fatal(...) do{\
+ if (internal_log_file_fp != NULL) \
+ internal_log_file_write(__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,...)
+{
+ if (internal_log_file_fp != NULL)
+ {
+ 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);
+ }
+}
+
+/*
+ * Open a new logfile with proper permissions.
+ * From src/backend/postmaster/syslogger.c
+ */
+static FILE *
+logfile_open(const char *filename, const char *mode)
+{
+ FILE *fh;
+ mode_t oumask;
+
+ oumask = umask((mode_t) ((~(S_IRUSR | S_IWUSR)) & (S_IRWXU | S_IRWXG | S_IRWXO)));
+ fh = fopen(filename, mode);
+ umask(oumask);
+
+ if (fh)
+ {
+ setvbuf(fh, NULL, PG_IOLBF, 0);
+
+#ifdef WIN32
+ /* use CRLF line endings on Windows */
+ _setmode(_fileno(fh), _O_TEXT);
+#endif
+ }
+ else
+ pg_fatal("could not open log file \"%s\": %m",
+ filename);
+
+ return fh;
+}
+
+static void
+make_dir(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);
+ }
+}
+
+static void
+make_output_dirs(char *log_dir)
+{
+ char timestamp[128];
+ struct timeval tval;
+ time_t now;
+ struct tm tmbuf;
+ char timestamp_dir[MAXPGPATH];
+ int len;
+
+ gettimeofday(&tval, NULL);
+ now = tval.tv_sec;
+ 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));
+ log_timestamp = pg_strdup(timestamp);
+
+ make_dir(log_dir);
+ 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);
+ make_dir(timestamp_dir);
+}
/*
* Clean up objects created by pg_createsubscriber.
@@ -269,6 +397,12 @@ cleanup_objects_atexit(void)
if (standby_running)
stop_standby_server(subscriber_dir);
+
+ if (internal_log_file_fp != NULL)
+ {
+ fclose(internal_log_file_fp);
+ internal_log_file_fp = NULL;
+ }
}
static void
@@ -283,6 +417,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"));
@@ -702,6 +837,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");
@@ -735,8 +871,14 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
else
pg_log_info("running pg_resetwal on the subscriber");
- cmd_str = psprintf("\"%s\" -D \"%s\" > \"%s\"", pg_resetwal_path,
- subscriber_dir, DEVNULL);
+
+ if (opt->log_dir != NULL)
+ out_file = psprintf("%s/%s/%s.log", opt->log_dir, log_timestamp, SERVER_LOG_FILE_NAME);
+ else
+ out_file = DEVNULL;
+
+ cmd_str = psprintf("\"%s\" -D \"%s\" >> \"%s\"", pg_resetwal_path,
+ subscriber_dir, out_file);
pg_log_debug("pg_resetwal command is: %s", cmd_str);
@@ -1650,6 +1792,9 @@ 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/%s/%s.log", opt->log_dir, log_timestamp, SERVER_LOG_FILE_NAME);
+
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);
@@ -2181,6 +2326,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'},
@@ -2215,6 +2361,7 @@ 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);
@@ -2239,6 +2386,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;
@@ -2267,7 +2415,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)
@@ -2288,6 +2436,14 @@ 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);
+ 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;
break;
@@ -2621,5 +2777,8 @@ main(int argc, char **argv)
pg_log_info("Done!");
+ if (internal_log_file_fp != NULL)
+ fclose(internal_log_file_fp);
+
return 0;
}
diff --git a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
index 0c27fca7bb7..3a82f893e28 100644
--- a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
+++ b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
@@ -14,6 +14,7 @@ program_version_ok('pg_createsubscriber');
program_options_handling_ok('pg_createsubscriber');
my $datadir = PostgreSQL::Test::Utils::tempdir;
+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.
@@ -362,9 +363,25 @@ command_ok(
'--subscription' => 'sub2',
'--database' => $db1,
'--database' => $db2,
+ '--logdir' => $logdir,
],
'run pg_createsubscriber --dry-run on node S');
+# Check that the log files were created
+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);
+isnt($server_log_file_size, 0,
+ "pg_createsubscriber_server.log file not empty");
+
+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);
+isnt($internal_log_file_size, 0,
+ "pg_createsubscriber_internal.log file not empty");
+
# Check if node S is still a standby
$node_s->start;
is($node_s->safe_psql('postgres', 'SELECT pg_catalog.pg_is_in_recovery()'),
--
2.34.1
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
@ 2026-03-17 23:24 ` Gyan Sreejith <[email protected]>
2026-03-18 06:12 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
0 siblings, 2 replies; 55+ messages in thread
From: Gyan Sreejith @ 2026-03-17 23:24 UTC (permalink / raw)
To: Shlok Kyal <[email protected]>; +Cc: vignesh C <[email protected]>; Amit Kapila <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
Thanks a lot, Shlok, for all the changes. I have included the changes from
topup.patch to my changes.
I believe it handles all the review comments. Please take a look.
Regards,
Gyan
Attachments:
[application/octet-stream] v10-0001-Add-a-new-argument-l-logdir-to-pg_createsubscrib.patch (15.6K, 3-v10-0001-Add-a-new-argument-l-logdir-to-pg_createsubscrib.patch)
download | inline diff:
From 37ee056d8906dcf445d16ab21dd4b1f445e6d22e Mon Sep 17 00:00:00 2001
From: Gyan Sreejith <[email protected]>
Date: Tue, 17 Mar 2026 19:10:28 -0400
Subject: [PATCH v10] Add a new argument -l <logdir> to pg_createsubscriber.
Enabling the option to write messages to log files in the specified directory.
A new directory is created if required. A subdirectory is created with timestamp as its name, and it will contain two new logfiles:
1. pg_createsubscriber_server.log - captures messages related to starting and stopping the standby server.
2. pg_createsubscriber_internal.log - captures internal diagnostic output from pg_createsubscriber.
For example, if we specify -l abc as an argument, and if the timestamp on running it is 20260119T204317.204, a directory abc is created if it doesn't exist already, with 20260119T204317.204 as its subdirectory and it will contain the two log files pg_createsubscriber_server.log and pg_createsubscriber_internal.log
---
doc/src/sgml/ref/pg_createsubscriber.sgml | 28 +++
src/bin/pg_basebackup/pg_createsubscriber.c | 214 +++++++++++++++++-
.../t/040_pg_createsubscriber.pl | 41 +++-
3 files changed, 274 insertions(+), 9 deletions(-)
diff --git a/doc/src/sgml/ref/pg_createsubscriber.sgml b/doc/src/sgml/ref/pg_createsubscriber.sgml
index cf45ff3573d..2898a5ea111 100644
--- a/doc/src/sgml/ref/pg_createsubscriber.sgml
+++ b/doc/src/sgml/ref/pg_createsubscriber.sgml
@@ -136,6 +136,34 @@ PostgreSQL documentation
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>-l <replaceable class="parameter">directory</replaceable></option></term>
+ <term><option>--logdir=<replaceable class="parameter">directory</replaceable></option></term>
+ <listitem>
+ <para>
+ 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.
+ <itemizedlist>
+ <listitem>
+ <para>
+ pg_createsubscriber_server.log which captures logs related to stopping
+ and starting the standby server,
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ pg_createsubscriber_internal.log which captures internal diagnostic
+ output (validations, checks, etc.)
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>-n</option></term>
<term><option>--dry-run</option></term>
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index 2bc84505aab..4450842d2b5 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -49,10 +49,14 @@
#define INCLUDED_CONF_FILE "pg_createsubscriber.conf"
#define INCLUDED_CONF_FILE_DISABLED INCLUDED_CONF_FILE ".disabled"
+#define SERVER_LOG_FILE_NAME "pg_createsubscriber_server"
+#define INTERNAL_LOG_FILE_NAME "pg_createsubscriber_internal"
+
/* Command-line options */
struct CreateSubscriberOptions
{
char *config_file; /* configuration file */
+ char *log_dir; /* log directory name */
char *pub_conninfo_str; /* publisher connection string */
char *socket_dir; /* directory for Unix-domain socket, if any */
char *sub_port; /* subscriber port number */
@@ -146,6 +150,10 @@ 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(enum pg_log_level level,
+ const char *format,...)
+ pg_attribute_printf(2, 3);
+
#define WAIT_INTERVAL 1 /* 1 second */
@@ -167,6 +175,10 @@ static pg_prng_state prng_state;
static char *pg_ctl_path = NULL;
static char *pg_resetwal_path = NULL;
+static FILE *internal_log_file_fp = NULL; /* File ptr to log all messages to */
+static char *log_timestamp = NULL; /* Timestamp to be used in all log file
+ * names */
+
/* standby / subscriber data directory */
static char *subscriber_dir = NULL;
@@ -174,6 +186,164 @@ 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 { \
+ if (internal_log_file_fp != NULL) \
+ internal_log_file_write(PG_LOG_INFO, __VA_ARGS__); \
+ else \
+ pg_log_generic(PG_LOG_INFO, PG_LOG_PRIMARY, __VA_ARGS__); \
+} while(0)
+
+#undef pg_log_info_hint
+#define pg_log_info_hint(...) do { \
+ if (internal_log_file_fp != NULL) \
+ internal_log_file_write(PG_LOG_INFO, __VA_ARGS__); \
+ else \
+ pg_log_generic(PG_LOG_INFO, PG_LOG_HINT, __VA_ARGS__); \
+} while(0)
+
+#undef 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 { \
+ if (internal_log_file_fp != NULL) \
+ 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(enum pg_log_level level, const char *format,...)
+{
+ va_list 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);
+}
+
+/*
+ * Open a new logfile with proper permissions.
+ * From src/backend/postmaster/syslogger.c
+ */
+static FILE *
+logfile_open(const char *filename, const char *mode)
+{
+ FILE *fh;
+ mode_t oumask;
+
+ oumask = umask((mode_t) ((~(S_IRUSR | S_IWUSR)) & (S_IRWXU | S_IRWXG | S_IRWXO)));
+ fh = fopen(filename, mode);
+ umask(oumask);
+
+ if (fh)
+ {
+ setvbuf(fh, NULL, PG_IOLBF, 0);
+
+#ifdef WIN32
+ /* use CRLF line endings on Windows */
+ _setmode(_fileno(fh), _O_TEXT);
+#endif
+ }
+ else
+ pg_fatal("could not open log file \"%s\": %m",
+ filename);
+
+ return fh;
+}
+
+static void
+make_dir(const char *dir)
+{
+ struct stat statbuf;
+
+ 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(const char *log_dir)
+{
+ char timestamp[128];
+ struct timeval tval;
+ time_t now;
+ struct tm tmbuf;
+ 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));
+
+ /* append milliseconds */
+ 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);
+
+ /* Create timestamp directory */
+ make_dir(timestamp_dir);
+}
/*
* Clean up objects created by pg_createsubscriber.
@@ -269,6 +439,12 @@ cleanup_objects_atexit(void)
if (standby_running)
stop_standby_server(subscriber_dir);
+
+ if (internal_log_file_fp != NULL)
+ {
+ fclose(internal_log_file_fp);
+ internal_log_file_fp = NULL;
+ }
}
static void
@@ -283,6 +459,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"));
@@ -702,6 +879,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");
@@ -735,8 +913,14 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
else
pg_log_info("running pg_resetwal on the subscriber");
- cmd_str = psprintf("\"%s\" -D \"%s\" > \"%s\"", pg_resetwal_path,
- subscriber_dir, DEVNULL);
+
+ if (opt->log_dir != NULL)
+ out_file = psprintf("%s/%s/%s.log", opt->log_dir, log_timestamp, SERVER_LOG_FILE_NAME);
+ else
+ out_file = DEVNULL;
+
+ cmd_str = psprintf("\"%s\" -D \"%s\" >> \"%s\"", pg_resetwal_path,
+ subscriber_dir, out_file);
pg_log_debug("pg_resetwal command is: %s", cmd_str);
@@ -1650,6 +1834,9 @@ 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/%s/%s.log", opt->log_dir, log_timestamp, SERVER_LOG_FILE_NAME);
+
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);
@@ -2181,6 +2368,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'},
@@ -2239,6 +2427,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;
@@ -2267,7 +2456,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)
@@ -2288,6 +2477,10 @@ 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);
+ break;
case 'n':
dry_run = true;
break;
@@ -2356,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)
{
@@ -2621,5 +2826,8 @@ main(int argc, char **argv)
pg_log_info("Done!");
+ if (internal_log_file_fp != NULL)
+ fclose(internal_log_file_fp);
+
return 0;
}
diff --git a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
index 0c27fca7bb7..4ddfb621a5d 100644
--- a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
+++ b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
@@ -14,6 +14,7 @@ program_version_ok('pg_createsubscriber');
program_options_handling_ok('pg_createsubscriber');
my $datadir = PostgreSQL::Test::Utils::tempdir;
+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.
@@ -362,9 +363,35 @@ command_ok(
'--subscription' => 'sub2',
'--database' => $db1,
'--database' => $db2,
+ '--logdir' => $logdir,
],
'run pg_createsubscriber --dry-run on node S');
+# Check that the log files were created
+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 $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 $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;
is($node_s->safe_psql('postgres', 'SELECT pg_catalog.pg_is_in_recovery()'),
@@ -444,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);
));
@@ -540,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');
@@ -555,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(
@@ -581,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");
--
2.43.0
^ permalink raw reply [nested|flat] 55+ messages in thread
* RE: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
@ 2026-03-18 06:12 ` Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-18 10:03 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber shveta malik <[email protected]>
1 sibling, 1 reply; 55+ messages in thread
From: Kuroda, Hayato/黒田 隼人 @ 2026-03-18 06:12 UTC (permalink / raw)
To: 'Gyan Sreejith' <[email protected]>; Shlok Kyal <[email protected]>; +Cc: vignesh C <[email protected]>; Amit Kapila <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
Dear Gyan,
Thanks for updating. Not sure you have addressed my comments as well,
but I reviewed again.
01.
```
+#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__)
```
IIUC it's more common that to have do {...} while if the marco function has
several lines.
02.
```
+#undef pg_log_info
+#define pg_log_info(...) do { \
+ if (internal_log_file_fp != NULL) \
+ internal_log_file_write(PG_LOG_INFO, __VA_ARGS__); \
+ else \
+ pg_log_generic(PG_LOG_INFO, PG_LOG_PRIMARY, __VA_ARGS__); \
+} while(0)
```
Missing update?
03.
I think pg_log_error faimily should be also output on the file, but it misses.
04.
```
+static void
+make_dir(const char *dir)
```
I think this is not needed, we can follow the approach done on the pg_upgrade.
The message "directory %s created" may be removed, but I think it's ok.
05.
```
+ /* 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);
```
These checks should be done before creating the base directory.
06.
```
+static char *log_timestamp = NULL; /* Timestamp to be used in all log file
+ * names */
```
I feel it's more efficient to directly have the directory where log exists.
07.
```
+ 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);
+ }
```
I still think it can be put bit later; after validating simple messages.
How about others?
08.
```
+static void
+internal_log_file_write(enum pg_log_level level, const char *format,...)
+{
+ va_list 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);
+}
```
internal_log_file_fp has already been checked before calling, so it can be Assert().
Also, the translated string should be passed to vfprintf().
My small tests shown that changing from "format" to "_(format)" is enough, but not sure
other platforms. Some tests are needed.
09.
```
+ if (opt->log_dir != NULL)
+ out_file = psprintf("%s/%s/%s.log", opt->log_dir, log_timestamp, SERVER_LOG_FILE_NAME);
+ else
+ out_file = DEVNULL;
```
I still think comments should be atop here.
Attached patch includes above changes.
Best regards,
Hayato Kuroda
FUJITSU LIMITED
Attachments:
[application/octet-stream] kuroda_diffs_atop_v10.diffs (9.3K, 2-kuroda_diffs_atop_v10.diffs)
download
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 06:12 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
@ 2026-03-18 10:03 ` shveta malik <[email protected]>
0 siblings, 0 replies; 55+ messages in thread
From: shveta malik @ 2026-03-18 10:03 UTC (permalink / raw)
To: Kuroda, Hayato/黒田 隼人 <[email protected]>; +Cc: Gyan Sreejith <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Amit Kapila <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>; shveta malik <[email protected]>
On Wed, Mar 18, 2026 at 11:43 AM Kuroda, Hayato/黒田 隼人
<[email protected]> wrote:
>
> Dear Gyan,
>
> Thanks for updating. Not sure you have addressed my comments as well,
> but I reviewed again.
>
> 01.
> ```
> +#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__)
> ```
>
> IIUC it's more common that to have do {...} while if the marco function has
> several lines.
>
> 02.
> ```
> +#undef pg_log_info
> +#define pg_log_info(...) do { \
> + if (internal_log_file_fp != NULL) \
> + internal_log_file_write(PG_LOG_INFO, __VA_ARGS__); \
> + else \
> + pg_log_generic(PG_LOG_INFO, PG_LOG_PRIMARY, __VA_ARGS__); \
> +} while(0)
> ```
>
> Missing update?
>
> 03.
>
> I think pg_log_error faimily should be also output on the file, but it misses.
>
> 04.
> ```
> +static void
> +make_dir(const char *dir)
> ```
>
> I think this is not needed, we can follow the approach done on the pg_upgrade.
> The message "directory %s created" may be removed, but I think it's ok.
>
> 05.
> ```
> + /* 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);
> ```
>
> These checks should be done before creating the base directory.
>
> 06.
> ```
> +static char *log_timestamp = NULL; /* Timestamp to be used in all log file
> + * names */
> ```
>
> I feel it's more efficient to directly have the directory where log exists.
>
> 07.
> ```
> + 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);
> + }
> ```
>
> I still think it can be put bit later; after validating simple messages.
> How about others?
+1, otherwise it will simply create a directory and error out after
that if the command is wrong. We can avoid creating a directory.
Patch has a compilation issue, logfile_open() needs a second argument in main().
If we fix that, it gives a segmentation fault in make_output_dirs() as
logdir is not allocated, earlier it was an array, now a NULL pointer.
Kuroda-san is going to post a new patch soon.
thanks
Shveta
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
@ 2026-03-18 13:15 ` Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
1 sibling, 1 reply; 55+ messages in thread
From: Amit Kapila @ 2026-03-18 13:15 UTC (permalink / raw)
To: Gyan Sreejith <[email protected]>; +Cc: Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
On Wed, Mar 18, 2026 at 4:55 AM Gyan Sreejith <[email protected]> wrote:
>
> Thanks a lot, Shlok, for all the changes. I have included the changes from topup.patch to my changes.
>
+#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 { \
+ if (internal_log_file_fp != NULL) \
+ internal_log_file_write(PG_LOG_INFO, __VA_ARGS__); \
+ else \
+ pg_log_generic(PG_LOG_INFO, PG_LOG_PRIMARY, __VA_ARGS__); \
+} while(0)
+
+#undef pg_log_info_hint
+#define pg_log_info_hint(...) do { \
+ if (internal_log_file_fp != NULL) \
+ internal_log_file_write(PG_LOG_INFO, __VA_ARGS__); \
+ else \
+ pg_log_generic(PG_LOG_INFO, PG_LOG_HINT, __VA_ARGS__); \
+} while(0)
+
+#undef 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 { \
+ if (internal_log_file_fp != NULL) \
+ internal_log_file_write(PG_LOG_ERROR, __VA_ARGS__); \
+ pg_log_generic(PG_LOG_ERROR, PG_LOG_PRIMARY, __VA_ARGS__); \
+ exit(1); \
+} while(0)
This looks odd to me. I think it would be better to encapsulate this
in one function (something like we have in pg_log_v) and then based on
log level, do required handling.
--
With Regards,
Amit Kapila.
^ permalink raw reply [nested|flat] 55+ messages in thread
* RE: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
@ 2026-03-18 13:44 ` Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
0 siblings, 1 reply; 55+ messages in thread
From: Hayato Kuroda (Fujitsu) @ 2026-03-18 13:44 UTC (permalink / raw)
To: 'Amit Kapila' <[email protected]>; Gyan Sreejith <[email protected]>; +Cc: Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
Dear Amit,
>
> This looks odd to me. I think it would be better to encapsulate this
> in one function (something like we have in pg_log_v) and then based on
> log level, do required handling.
>
Based on the suggestion, I updated patches. Please see attached series.
v11-0001 replaces existing pg_log_xxx families to new reporting function
pg_createsub_log(). Now only pg_fatal is overwritten, which is same as pg_uprade.
v11-0002 is the rebased version of v10. Nothing new, but codes are adjusted based
on 0001.
v11-0003 contains changes from me [1]. 0002 and 0003 can be combined if it's OK.
[1]: https://www.postgresql.org/message-id/OS9PR01MB12149C7DE09F13C3BCBD9357DF54EA%40OS9PR01MB12149.jpnpr...
Best regards,
Hayato Kuroda
FUJITSU LIMITED
Attachments:
[application/octet-stream] v11-0001-pg_createsubscriber-use-own-reporting-functions.patch (55.0K, 2-v11-0001-pg_createsubscriber-use-own-reporting-functions.patch)
download | inline diff:
From 1048938cae87dd47eb486ccf01718b4be1b31eea Mon Sep 17 00:00:00 2001
From: Hayato Kuroda <[email protected]>
Date: Wed, 18 Mar 2026 20:20:50 +0900
Subject: [PATCH v11 1/3] pg_createsubscriber: use own reporting functions
This commit converts all pg_log_xxx families to call a new reporting function
pg_createsub_log(). Also, pg_fatal() is overwritten to use its own.
This commit changes nothing from the outside, but is needed for the upcoming
commit.
---
src/bin/pg_basebackup/pg_createsubscriber.c | 706 +++++++++++++-------
1 file changed, 453 insertions(+), 253 deletions(-)
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index 2bc84505aab..b02af9a4cc0 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -31,6 +31,12 @@
#include "fe_utils/version.h"
#include "getopt_long.h"
+/*
+ * For now, pg_createsubscriber does not use common/logging.c; use our own
+ * pg_fatal.
+ */
+#undef pg_fatal
+
#define DEFAULT_SUB_PORT "50432"
#define OBJECTTYPE_PUBLICATIONS 0x0001
@@ -146,6 +152,11 @@ 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 pg_createsub_log(enum pg_log_level, enum pg_log_part,
+ const char *pg_restrict fmt,...)
+ pg_attribute_printf(3, 4);
+pg_noreturn static void pg_fatal(const char *pg_restrict fmt,...)
+ pg_attribute_printf(1, 2);
#define WAIT_INTERVAL 1 /* 1 second */
@@ -174,6 +185,27 @@ static bool recovery_ended = false;
static bool standby_running = false;
static bool recovery_params_set = false;
+static void
+pg_createsub_log(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt,...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ pg_log_generic_v(level, part, fmt, args);
+ va_end(args);
+}
+
+static void
+pg_fatal(const char *pg_restrict fmt,...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, args);
+ va_end(args);
+ exit(1);
+}
/*
* Clean up objects created by pg_createsubscriber.
@@ -205,7 +237,8 @@ cleanup_objects_atexit(void)
if (durable_rename(conf_filename, conf_filename_disabled) != 0)
{
/* durable_rename() has already logged something. */
- pg_log_warning_hint("A manual removal of the recovery parameters may be required.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "A manual removal of the recovery parameters may be required.");
}
}
@@ -219,9 +252,11 @@ cleanup_objects_atexit(void)
*/
if (recovery_ended)
{
- pg_log_warning("failed after the end of recovery");
- pg_log_warning_hint("The target server cannot be used as a physical replica anymore. "
- "You must recreate the physical replica before continuing.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "failed after the end of recovery");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "The target server cannot be used as a physical replica anymore. "
+ "You must recreate the physical replica before continuing.");
}
for (int i = 0; i < num_dbs; i++)
@@ -251,17 +286,21 @@ cleanup_objects_atexit(void)
*/
if (dbinfo->made_publication)
{
- pg_log_warning("publication \"%s\" created in database \"%s\" on primary was left behind",
- dbinfo->pubname,
- dbinfo->dbname);
- pg_log_warning_hint("Drop this publication before trying again.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "publication \"%s\" created in database \"%s\" on primary was left behind",
+ dbinfo->pubname,
+ dbinfo->dbname);
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop this publication before trying again.");
}
if (dbinfo->made_replslot)
{
- pg_log_warning("replication slot \"%s\" created in database \"%s\" on primary was left behind",
- dbinfo->replslotname,
- dbinfo->dbname);
- pg_log_warning_hint("Drop this replication slot soon to avoid retention of WAL files.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "replication slot \"%s\" created in database \"%s\" on primary was left behind",
+ dbinfo->replslotname,
+ dbinfo->dbname);
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop this replication slot soon to avoid retention of WAL files.");
}
}
}
@@ -342,7 +381,8 @@ get_base_conninfo(const char *conninfo, char **dbname)
conn_opts = PQconninfoParse(conninfo, &errmsg);
if (conn_opts == NULL)
{
- pg_log_error("could not parse connection string: %s", errmsg);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not parse connection string: %s", errmsg);
PQfreemem(errmsg);
return NULL;
}
@@ -426,7 +466,8 @@ get_exec_path(const char *argv0, const char *progname)
progname, full_path, "pg_createsubscriber");
}
- pg_log_debug("%s path is: %s", progname, exec_path);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "%s path is: %s", progname, exec_path);
return exec_path;
}
@@ -443,8 +484,9 @@ check_data_directory(const char *datadir)
uint32 major_version;
char *version_str;
- pg_log_info("checking if directory \"%s\" is a cluster data directory",
- datadir);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "checking if directory \"%s\" is a cluster data directory",
+ datadir);
if (stat(datadir, &statbuf) != 0)
{
@@ -462,9 +504,11 @@ check_data_directory(const char *datadir)
major_version = GET_PG_MAJORVERSION_NUM(get_pg_version(datadir, &version_str));
if (major_version != PG_MAJORVERSION_NUM)
{
- pg_log_error("data directory is of wrong version");
- pg_log_error_detail("File \"%s\" contains \"%s\", which is not compatible with this program's version \"%s\".",
- "PG_VERSION", version_str, PG_MAJORVERSION);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "data directory is of wrong version");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "File \"%s\" contains \"%s\", which is not compatible with this program's version \"%s\".",
+ "PG_VERSION", version_str, PG_MAJORVERSION);
exit(1);
}
}
@@ -547,14 +591,16 @@ store_pub_sub_info(const struct CreateSubscriberOptions *opt,
dbinfo[i].subname = NULL;
/* Other fields will be filled later */
- pg_log_debug("publisher(%d): publication: %s ; replication slot: %s ; connection string: %s", i,
- dbinfo[i].pubname ? dbinfo[i].pubname : "(auto)",
- dbinfo[i].replslotname ? dbinfo[i].replslotname : "(auto)",
- dbinfo[i].pubconninfo);
- pg_log_debug("subscriber(%d): subscription: %s ; connection string: %s, two_phase: %s", i,
- dbinfo[i].subname ? dbinfo[i].subname : "(auto)",
- dbinfo[i].subconninfo,
- dbinfos.two_phase ? "true" : "false");
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher(%d): publication: %s ; replication slot: %s ; connection string: %s", i,
+ dbinfo[i].pubname ? dbinfo[i].pubname : "(auto)",
+ dbinfo[i].replslotname ? dbinfo[i].replslotname : "(auto)",
+ dbinfo[i].pubconninfo);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber(%d): subscription: %s ; connection string: %s, two_phase: %s", i,
+ dbinfo[i].subname ? dbinfo[i].subname : "(auto)",
+ dbinfo[i].subconninfo,
+ dbinfos.two_phase ? "true" : "false");
if (num_pubs > 0)
pubcell = pubcell->next;
@@ -582,8 +628,9 @@ connect_database(const char *conninfo, bool exit_on_error)
conn = PQconnectdb(conninfo);
if (PQstatus(conn) != CONNECTION_OK)
{
- pg_log_error("connection to database failed: %s",
- PQerrorMessage(conn));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "connection to database failed: %s",
+ PQerrorMessage(conn));
PQfinish(conn);
if (exit_on_error)
@@ -595,8 +642,9 @@ connect_database(const char *conninfo, bool exit_on_error)
res = PQexec(conn, ALWAYS_SECURE_SEARCH_PATH_SQL);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not clear \"search_path\": %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not clear \"search_path\": %s",
+ PQresultErrorMessage(res));
PQclear(res);
PQfinish(conn);
@@ -635,27 +683,31 @@ get_primary_sysid(const char *conninfo)
PGresult *res;
uint64 sysid;
- pg_log_info("getting system identifier from publisher");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "getting system identifier from publisher");
conn = connect_database(conninfo, true);
res = PQexec(conn, "SELECT system_identifier FROM pg_catalog.pg_control_system()");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not get system identifier: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not get system identifier: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
if (PQntuples(res) != 1)
{
- pg_log_error("could not get system identifier: got %d rows, expected %d row",
- PQntuples(res), 1);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not get system identifier: got %d rows, expected %d row",
+ PQntuples(res), 1);
disconnect_database(conn, true);
}
sysid = strtou64(PQgetvalue(res, 0, 0), NULL, 10);
- pg_log_info("system identifier is %" PRIu64 " on publisher", sysid);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "system identifier is %" PRIu64 " on publisher", sysid);
PQclear(res);
disconnect_database(conn, false);
@@ -675,7 +727,8 @@ get_standby_sysid(const char *datadir)
bool crc_ok;
uint64 sysid;
- pg_log_info("getting system identifier from subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "getting system identifier from subscriber");
cf = get_controlfile(datadir, &crc_ok);
if (!crc_ok)
@@ -683,7 +736,8 @@ get_standby_sysid(const char *datadir)
sysid = cf->system_identifier;
- pg_log_info("system identifier is %" PRIu64 " on subscriber", sysid);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "system identifier is %" PRIu64 " on subscriber", sysid);
pg_free(cf);
@@ -704,7 +758,8 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
char *cmd_str;
- pg_log_info("modifying system identifier of subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "modifying system identifier of subscriber");
cf = get_controlfile(subscriber_dir, &crc_ok);
if (!crc_ok)
@@ -721,31 +776,37 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
cf->system_identifier |= getpid() & 0xFFF;
if (dry_run)
- pg_log_info("dry-run: would set system identifier to %" PRIu64 " on subscriber",
- cf->system_identifier);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would set system identifier to %" PRIu64 " on subscriber",
+ cf->system_identifier);
else
{
update_controlfile(subscriber_dir, cf, true);
- pg_log_info("system identifier is %" PRIu64 " on subscriber",
- cf->system_identifier);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "system identifier is %" PRIu64 " on subscriber",
+ cf->system_identifier);
}
if (dry_run)
- pg_log_info("dry-run: would run pg_resetwal on the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would run pg_resetwal on the subscriber");
else
- pg_log_info("running pg_resetwal on the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "running pg_resetwal on the subscriber");
cmd_str = psprintf("\"%s\" -D \"%s\" > \"%s\"", pg_resetwal_path,
subscriber_dir, DEVNULL);
- pg_log_debug("pg_resetwal command is: %s", cmd_str);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "pg_resetwal command is: %s", cmd_str);
if (!dry_run)
{
int rc = system(cmd_str);
if (rc == 0)
- pg_log_info("successfully reset WAL on the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "successfully reset WAL on the subscriber");
else
pg_fatal("could not reset WAL on subscriber: %s", wait_result_to_str(rc));
}
@@ -771,15 +832,17 @@ generate_object_name(PGconn *conn)
"WHERE datname = pg_catalog.current_database()");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain database OID: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain database OID: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
if (PQntuples(res) != 1)
{
- pg_log_error("could not obtain database OID: got %d rows, expected %d row",
- PQntuples(res), 1);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain database OID: got %d rows, expected %d row",
+ PQntuples(res), 1);
disconnect_database(conn, true);
}
@@ -819,8 +882,9 @@ find_publication(PGconn *conn, const char *pubname, const char *dbname)
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not find publication \"%s\" in database \"%s\": %s",
- pubname, dbname, PQerrorMessage(conn));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not find publication \"%s\" in database \"%s\": %s",
+ pubname, dbname, PQerrorMessage(conn));
disconnect_database(conn, true);
}
@@ -873,8 +937,9 @@ setup_publisher(struct LogicalRepInfo *dbinfo)
if (find_publication(conn, dbinfo[i].pubname, dbinfo[i].dbname))
{
/* Reuse existing publication on publisher. */
- pg_log_info("use existing publication \"%s\" in database \"%s\"",
- dbinfo[i].pubname, dbinfo[i].dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "use existing publication \"%s\" in database \"%s\"",
+ dbinfo[i].pubname, dbinfo[i].dbname);
/* Don't remove pre-existing publication if an error occurs. */
dbinfo[i].made_publication = false;
}
@@ -912,8 +977,9 @@ setup_publisher(struct LogicalRepInfo *dbinfo)
res = PQexec(conn, "SELECT pg_log_standby_snapshot()");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not write an additional WAL record: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not write an additional WAL record: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
PQclear(res);
@@ -938,8 +1004,9 @@ server_is_in_recovery(PGconn *conn)
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain recovery progress: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain recovery progress: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -971,7 +1038,8 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
int max_prepared_transactions;
char *max_slot_wal_keep_size;
- pg_log_info("checking settings on publisher");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "checking settings on publisher");
conn = connect_database(dbinfo[0].pubconninfo, true);
@@ -981,7 +1049,8 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
*/
if (server_is_in_recovery(conn))
{
- pg_log_error("primary server cannot be in recovery");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "primary server cannot be in recovery");
disconnect_database(conn, true);
}
@@ -1007,8 +1076,9 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain publisher settings: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain publisher settings: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1022,48 +1092,63 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
PQclear(res);
- pg_log_debug("publisher: wal_level: %s", wal_level);
- pg_log_debug("publisher: max_replication_slots: %d", max_repslots);
- pg_log_debug("publisher: current replication slots: %d", cur_repslots);
- pg_log_debug("publisher: max_wal_senders: %d", max_walsenders);
- pg_log_debug("publisher: current wal senders: %d", cur_walsenders);
- pg_log_debug("publisher: max_prepared_transactions: %d",
- max_prepared_transactions);
- pg_log_debug("publisher: max_slot_wal_keep_size: %s",
- max_slot_wal_keep_size);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: wal_level: %s", wal_level);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: max_replication_slots: %d", max_repslots);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: current replication slots: %d", cur_repslots);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: max_wal_senders: %d", max_walsenders);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: current wal senders: %d", cur_walsenders);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: max_prepared_transactions: %d",
+ max_prepared_transactions);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: max_slot_wal_keep_size: %s",
+ max_slot_wal_keep_size);
disconnect_database(conn, false);
if (strcmp(wal_level, "minimal") == 0)
{
- pg_log_error("publisher requires \"wal_level\" >= \"replica\"");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "publisher requires \"wal_level\" >= \"replica\"");
failed = true;
}
if (max_repslots - cur_repslots < num_dbs)
{
- pg_log_error("publisher requires %d replication slots, but only %d remain",
- num_dbs, max_repslots - cur_repslots);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_replication_slots", cur_repslots + num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "publisher requires %d replication slots, but only %d remain",
+ num_dbs, max_repslots - cur_repslots);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_replication_slots", cur_repslots + num_dbs);
failed = true;
}
if (max_walsenders - cur_walsenders < num_dbs)
{
- pg_log_error("publisher requires %d WAL sender processes, but only %d remain",
- num_dbs, max_walsenders - cur_walsenders);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_wal_senders", cur_walsenders + num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "publisher requires %d WAL sender processes, but only %d remain",
+ num_dbs, max_walsenders - cur_walsenders);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_wal_senders", cur_walsenders + num_dbs);
failed = true;
}
if (max_prepared_transactions != 0 && !dbinfos.two_phase)
{
- pg_log_warning("two_phase option will not be enabled for replication slots");
- pg_log_warning_detail("Subscriptions will be created with the two_phase option disabled. "
- "Prepared transactions will be replicated at COMMIT PREPARED.");
- pg_log_warning_hint("You can use the command-line option --enable-two-phase to enable two_phase.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "two_phase option will not be enabled for replication slots");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_DETAIL,
+ "Subscriptions will be created with the two_phase option disabled. "
+ "Prepared transactions will be replicated at COMMIT PREPARED.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "You can use the command-line option --enable-two-phase to enable two_phase.");
}
/*
@@ -1073,9 +1158,11 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
*/
if (dry_run && (strcmp(max_slot_wal_keep_size, "-1") != 0))
{
- pg_log_warning("required WAL could be removed from the publisher");
- pg_log_warning_hint("Set the configuration parameter \"%s\" to -1 to ensure that required WAL files are not prematurely removed.",
- "max_slot_wal_keep_size");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "required WAL could be removed from the publisher");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Set the configuration parameter \"%s\" to -1 to ensure that required WAL files are not prematurely removed.",
+ "max_slot_wal_keep_size");
}
pg_free(wal_level);
@@ -1106,14 +1193,16 @@ check_subscriber(const struct LogicalRepInfo *dbinfo)
int max_replorigins;
int max_wprocs;
- pg_log_info("checking settings on subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "checking settings on subscriber");
conn = connect_database(dbinfo[0].subconninfo, true);
/* The target server must be a standby */
if (!server_is_in_recovery(conn))
{
- pg_log_error("target server must be a standby");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "target server must be a standby");
disconnect_database(conn, true);
}
@@ -1137,8 +1226,9 @@ check_subscriber(const struct LogicalRepInfo *dbinfo)
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain subscriber settings: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain subscriber settings: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1148,12 +1238,16 @@ check_subscriber(const struct LogicalRepInfo *dbinfo)
if (strcmp(PQgetvalue(res, 3, 0), "") != 0)
primary_slot_name = pg_strdup(PQgetvalue(res, 3, 0));
- pg_log_debug("subscriber: max_logical_replication_workers: %d",
- max_lrworkers);
- pg_log_debug("subscriber: max_active_replication_origins: %d", max_replorigins);
- pg_log_debug("subscriber: max_worker_processes: %d", max_wprocs);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber: max_logical_replication_workers: %d",
+ max_lrworkers);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber: max_active_replication_origins: %d", max_replorigins);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber: max_worker_processes: %d", max_wprocs);
if (primary_slot_name)
- pg_log_debug("subscriber: primary_slot_name: %s", primary_slot_name);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber: primary_slot_name: %s", primary_slot_name);
PQclear(res);
@@ -1161,28 +1255,34 @@ check_subscriber(const struct LogicalRepInfo *dbinfo)
if (max_replorigins < num_dbs)
{
- pg_log_error("subscriber requires %d active replication origins, but only %d remain",
- num_dbs, max_replorigins);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_active_replication_origins", num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "subscriber requires %d active replication origins, but only %d remain",
+ num_dbs, max_replorigins);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_active_replication_origins", num_dbs);
failed = true;
}
if (max_lrworkers < num_dbs)
{
- pg_log_error("subscriber requires %d logical replication workers, but only %d remain",
- num_dbs, max_lrworkers);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_logical_replication_workers", num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "subscriber requires %d logical replication workers, but only %d remain",
+ num_dbs, max_lrworkers);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_logical_replication_workers", num_dbs);
failed = true;
}
if (max_wprocs < num_dbs + 1)
{
- pg_log_error("subscriber requires %d worker processes, but only %d remain",
- num_dbs + 1, max_wprocs);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_worker_processes", num_dbs + 1);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "subscriber requires %d worker processes, but only %d remain",
+ num_dbs + 1, max_wprocs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_worker_processes", num_dbs + 1);
failed = true;
}
@@ -1215,19 +1315,22 @@ drop_existing_subscription(PGconn *conn, const char *subname, const char *dbname
appendPQExpBuffer(query, " DROP SUBSCRIPTION %s;", subname);
if (dry_run)
- pg_log_info("dry-run: would drop subscription \"%s\" in database \"%s\"",
- subname, dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would drop subscription \"%s\" in database \"%s\"",
+ subname, dbname);
else
{
- pg_log_info("dropping subscription \"%s\" in database \"%s\"",
- subname, dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dropping subscription \"%s\" in database \"%s\"",
+ subname, dbname);
res = PQexec(conn, query->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not drop subscription \"%s\": %s",
- subname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not drop subscription \"%s\": %s",
+ subname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1261,8 +1364,9 @@ check_and_drop_existing_subscriptions(PGconn *conn,
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain pre-existing subscriptions: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain pre-existing subscriptions: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1373,7 +1477,8 @@ setup_recovery(const struct LogicalRepInfo *dbinfo, const char *datadir, const c
lsn);
}
- pg_log_debug("recovery parameters:\n%s", recoveryconfcontents->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "recovery parameters:\n%s", recoveryconfcontents->data);
if (!dry_run)
{
@@ -1427,9 +1532,11 @@ drop_primary_replication_slot(struct LogicalRepInfo *dbinfo, const char *slotnam
}
else
{
- pg_log_warning("could not drop replication slot \"%s\" on primary",
- slotname);
- pg_log_warning_hint("Drop this replication slot soon to avoid retention of WAL files.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "could not drop replication slot \"%s\" on primary",
+ slotname);
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop this replication slot soon to avoid retention of WAL files.");
}
}
@@ -1461,9 +1568,11 @@ drop_failover_replication_slots(struct LogicalRepInfo *dbinfo)
}
else
{
- pg_log_warning("could not obtain failover replication slot information: %s",
- PQresultErrorMessage(res));
- pg_log_warning_hint("Drop the failover replication slots on subscriber soon to avoid retention of WAL files.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "could not obtain failover replication slot information: %s",
+ PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop the failover replication slots on subscriber soon to avoid retention of WAL files.");
}
PQclear(res);
@@ -1471,8 +1580,10 @@ drop_failover_replication_slots(struct LogicalRepInfo *dbinfo)
}
else
{
- pg_log_warning("could not drop failover replication slot");
- pg_log_warning_hint("Drop the failover replication slots on subscriber soon to avoid retention of WAL files.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "could not drop failover replication slot");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop the failover replication slots on subscriber soon to avoid retention of WAL files.");
}
}
@@ -1494,11 +1605,13 @@ create_logical_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo)
Assert(conn != NULL);
if (dry_run)
- pg_log_info("dry-run: would create the replication slot \"%s\" in database \"%s\" on publisher",
- slot_name, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would create the replication slot \"%s\" in database \"%s\" on publisher",
+ slot_name, dbinfo->dbname);
else
- pg_log_info("creating the replication slot \"%s\" in database \"%s\" on publisher",
- slot_name, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "creating the replication slot \"%s\" in database \"%s\" on publisher",
+ slot_name, dbinfo->dbname);
slot_name_esc = PQescapeLiteral(conn, slot_name, strlen(slot_name));
@@ -1509,16 +1622,18 @@ create_logical_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo)
PQfreemem(slot_name_esc);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not create replication slot \"%s\" in database \"%s\": %s",
- slot_name, dbinfo->dbname,
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not create replication slot \"%s\" in database \"%s\": %s",
+ slot_name, dbinfo->dbname,
+ PQresultErrorMessage(res));
PQclear(res);
destroyPQExpBuffer(str);
return NULL;
@@ -1547,11 +1662,13 @@ drop_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo,
Assert(conn != NULL);
if (dry_run)
- pg_log_info("dry-run: would drop the replication slot \"%s\" in database \"%s\"",
- slot_name, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would drop the replication slot \"%s\" in database \"%s\"",
+ slot_name, dbinfo->dbname);
else
- pg_log_info("dropping the replication slot \"%s\" in database \"%s\"",
- slot_name, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dropping the replication slot \"%s\" in database \"%s\"",
+ slot_name, dbinfo->dbname);
slot_name_esc = PQescapeLiteral(conn, slot_name, strlen(slot_name));
@@ -1559,15 +1676,17 @@ drop_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo,
PQfreemem(slot_name_esc);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not drop replication slot \"%s\" in database \"%s\": %s",
- slot_name, dbinfo->dbname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not drop replication slot \"%s\" in database \"%s\": %s",
+ slot_name, dbinfo->dbname, PQresultErrorMessage(res));
dbinfo->made_replslot = false; /* don't try again. */
}
@@ -1587,25 +1706,32 @@ pg_ctl_status(const char *pg_ctl_cmd, int rc)
{
if (WIFEXITED(rc))
{
- pg_log_error("pg_ctl failed with exit code %d", WEXITSTATUS(rc));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "pg_ctl failed with exit code %d",
+ WEXITSTATUS(rc));
}
else if (WIFSIGNALED(rc))
{
#if defined(WIN32)
- pg_log_error("pg_ctl was terminated by exception 0x%X",
- WTERMSIG(rc));
- pg_log_error_detail("See C include file \"ntstatus.h\" for a description of the hexadecimal value.");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "pg_ctl was terminated by exception 0x%X",
+ WTERMSIG(rc));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "See C include file \"ntstatus.h\" for a description of the hexadecimal value.");
#else
- pg_log_error("pg_ctl was terminated by signal %d: %s",
- WTERMSIG(rc), pg_strsignal(WTERMSIG(rc)));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "pg_ctl was terminated by signal %d: %s",
+ WTERMSIG(rc), pg_strsignal(WTERMSIG(rc)));
#endif
}
else
{
- pg_log_error("pg_ctl exited with unrecognized status %d", rc);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "pg_ctl exited with unrecognized status %d", rc);
}
- pg_log_error_detail("The failed command was: %s", pg_ctl_cmd);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "The failed command was: %s", pg_ctl_cmd);
exit(1);
}
}
@@ -1650,12 +1776,14 @@ start_standby_server(const struct CreateSubscriberOptions *opt, bool restricted_
if (restrict_logical_worker)
appendPQExpBufferStr(pg_ctl_cmd, " -o \"-c max_logical_replication_workers=0\"");
- pg_log_debug("pg_ctl command is: %s", pg_ctl_cmd->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "pg_ctl command is: %s", pg_ctl_cmd->data);
rc = system(pg_ctl_cmd->data);
pg_ctl_status(pg_ctl_cmd->data, rc);
standby_running = true;
destroyPQExpBuffer(pg_ctl_cmd);
- pg_log_info("server was started");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "server was started");
}
static void
@@ -1666,11 +1794,13 @@ stop_standby_server(const char *datadir)
pg_ctl_cmd = psprintf("\"%s\" stop -D \"%s\" -s", pg_ctl_path,
datadir);
- pg_log_debug("pg_ctl command is: %s", pg_ctl_cmd);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "pg_ctl command is: %s", pg_ctl_cmd);
rc = system(pg_ctl_cmd);
pg_ctl_status(pg_ctl_cmd, rc);
standby_running = false;
- pg_log_info("server was stopped");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "server was stopped");
}
/*
@@ -1689,7 +1819,8 @@ wait_for_end_recovery(const char *conninfo, const struct CreateSubscriberOptions
bool ready = false;
int timer = 0;
- pg_log_info("waiting for the target server to reach the consistent state");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "waiting for the target server to reach the consistent state");
conn = connect_database(conninfo, true);
@@ -1707,7 +1838,8 @@ wait_for_end_recovery(const char *conninfo, const struct CreateSubscriberOptions
if (opt->recovery_timeout > 0 && timer >= opt->recovery_timeout)
{
stop_standby_server(subscriber_dir);
- pg_log_error("recovery timed out");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "recovery timed out");
disconnect_database(conn, true);
}
@@ -1721,8 +1853,10 @@ wait_for_end_recovery(const char *conninfo, const struct CreateSubscriberOptions
if (!ready)
pg_fatal("server did not end recovery");
- pg_log_info("target server reached the consistent state");
- pg_log_info_hint("If pg_createsubscriber fails after this point, you must recreate the physical replica before continuing.");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "target server reached the consistent state");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_HINT,
+ "If pg_createsubscriber fails after this point, you must recreate the physical replica before continuing.");
}
/*
@@ -1749,8 +1883,9 @@ create_publication(PGconn *conn, struct LogicalRepInfo *dbinfo)
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain publication information: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain publication information: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1763,8 +1898,10 @@ create_publication(PGconn *conn, struct LogicalRepInfo *dbinfo)
* pg_createsubscriber_ prefix followed by the exact database oid and
* a random number.
*/
- pg_log_error("publication \"%s\" already exists", dbinfo->pubname);
- pg_log_error_hint("Consider renaming this publication before continuing.");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "publication \"%s\" already exists", dbinfo->pubname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Consider renaming this publication before continuing.");
disconnect_database(conn, true);
}
@@ -1772,24 +1909,28 @@ create_publication(PGconn *conn, struct LogicalRepInfo *dbinfo)
resetPQExpBuffer(str);
if (dry_run)
- pg_log_info("dry-run: would create publication \"%s\" in database \"%s\"",
- dbinfo->pubname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would create publication \"%s\" in database \"%s\"",
+ dbinfo->pubname, dbinfo->dbname);
else
- pg_log_info("creating publication \"%s\" in database \"%s\"",
- dbinfo->pubname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "creating publication \"%s\" in database \"%s\"",
+ dbinfo->pubname, dbinfo->dbname);
appendPQExpBuffer(str, "CREATE PUBLICATION %s FOR ALL TABLES",
ipubname_esc);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not create publication \"%s\" in database \"%s\": %s",
- dbinfo->pubname, dbinfo->dbname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not create publication \"%s\" in database \"%s\": %s",
+ dbinfo->pubname, dbinfo->dbname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
PQclear(res);
@@ -1819,25 +1960,29 @@ drop_publication(PGconn *conn, const char *pubname, const char *dbname,
pubname_esc = PQescapeIdentifier(conn, pubname, strlen(pubname));
if (dry_run)
- pg_log_info("dry-run: would drop publication \"%s\" in database \"%s\"",
- pubname, dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would drop publication \"%s\" in database \"%s\"",
+ pubname, dbname);
else
- pg_log_info("dropping publication \"%s\" in database \"%s\"",
- pubname, dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dropping publication \"%s\" in database \"%s\"",
+ pubname, dbname);
appendPQExpBuffer(str, "DROP PUBLICATION %s", pubname_esc);
PQfreemem(pubname_esc);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not drop publication \"%s\" in database \"%s\": %s",
- pubname, dbname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not drop publication \"%s\" in database \"%s\": %s",
+ pubname, dbname, PQresultErrorMessage(res));
*made_publication = false; /* don't try again. */
/*
@@ -1872,15 +2017,17 @@ check_and_drop_publications(PGconn *conn, struct LogicalRepInfo *dbinfo)
if (drop_all_pubs)
{
- pg_log_info("dropping all existing publications in database \"%s\"",
- dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dropping all existing publications in database \"%s\"",
+ dbinfo->dbname);
/* Fetch all publication names */
res = PQexec(conn, "SELECT pubname FROM pg_catalog.pg_publication;");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain publication information: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain publication information: %s",
+ PQresultErrorMessage(res));
PQclear(res);
disconnect_database(conn, true);
}
@@ -1903,11 +2050,13 @@ check_and_drop_publications(PGconn *conn, struct LogicalRepInfo *dbinfo)
else
{
if (dry_run)
- pg_log_info("dry-run: would preserve existing publication \"%s\" in database \"%s\"",
- dbinfo->pubname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would preserve existing publication \"%s\" in database \"%s\"",
+ dbinfo->pubname, dbinfo->dbname);
else
- pg_log_info("preserve existing publication \"%s\" in database \"%s\"",
- dbinfo->pubname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "preserve existing publication \"%s\" in database \"%s\"",
+ dbinfo->pubname, dbinfo->dbname);
}
}
}
@@ -1941,11 +2090,13 @@ create_subscription(PGconn *conn, const struct LogicalRepInfo *dbinfo)
replslotname_esc = PQescapeLiteral(conn, dbinfo->replslotname, strlen(dbinfo->replslotname));
if (dry_run)
- pg_log_info("dry-run: would create subscription \"%s\" in database \"%s\"",
- dbinfo->subname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would create subscription \"%s\" in database \"%s\"",
+ dbinfo->subname, dbinfo->dbname);
else
- pg_log_info("creating subscription \"%s\" in database \"%s\"",
- dbinfo->subname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "creating subscription \"%s\" in database \"%s\"",
+ dbinfo->subname, dbinfo->dbname);
appendPQExpBuffer(str,
"CREATE SUBSCRIPTION %s CONNECTION %s PUBLICATION %s "
@@ -1959,15 +2110,17 @@ create_subscription(PGconn *conn, const struct LogicalRepInfo *dbinfo)
PQfreemem(pubconninfo_esc);
PQfreemem(replslotname_esc);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not create subscription \"%s\" in database \"%s\": %s",
- dbinfo->subname, dbinfo->dbname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not create subscription \"%s\" in database \"%s\": %s",
+ dbinfo->subname, dbinfo->dbname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
PQclear(res);
@@ -2011,15 +2164,17 @@ set_replication_progress(PGconn *conn, const struct LogicalRepInfo *dbinfo, cons
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain subscription OID: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain subscription OID: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
if (PQntuples(res) != 1 && !dry_run)
{
- pg_log_error("could not obtain subscription OID: got %d rows, expected %d row",
- PQntuples(res), 1);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain subscription OID: got %d rows, expected %d row",
+ PQntuples(res), 1);
disconnect_database(conn, true);
}
@@ -2043,26 +2198,30 @@ set_replication_progress(PGconn *conn, const struct LogicalRepInfo *dbinfo, cons
originname = psprintf("pg_%u", suboid);
if (dry_run)
- pg_log_info("dry-run: would set the replication progress (node name \"%s\", LSN %s) in database \"%s\"",
- originname, lsnstr, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would set the replication progress (node name \"%s\", LSN %s) in database \"%s\"",
+ originname, lsnstr, dbinfo->dbname);
else
- pg_log_info("setting the replication progress (node name \"%s\", LSN %s) in database \"%s\"",
- originname, lsnstr, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "setting the replication progress (node name \"%s\", LSN %s) in database \"%s\"",
+ originname, lsnstr, dbinfo->dbname);
resetPQExpBuffer(str);
appendPQExpBuffer(str,
"SELECT pg_catalog.pg_replication_origin_advance('%s', '%s')",
originname, lsnstr);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not set replication progress for subscription \"%s\": %s",
- dbinfo->subname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not set replication progress for subscription \"%s\": %s",
+ dbinfo->subname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
PQclear(res);
@@ -2093,23 +2252,27 @@ enable_subscription(PGconn *conn, const struct LogicalRepInfo *dbinfo)
subname = PQescapeIdentifier(conn, dbinfo->subname, strlen(dbinfo->subname));
if (dry_run)
- pg_log_info("dry-run: would enable subscription \"%s\" in database \"%s\"",
- dbinfo->subname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would enable subscription \"%s\" in database \"%s\"",
+ dbinfo->subname, dbinfo->dbname);
else
- pg_log_info("enabling subscription \"%s\" in database \"%s\"",
- dbinfo->subname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "enabling subscription \"%s\" in database \"%s\"",
+ dbinfo->subname, dbinfo->dbname);
appendPQExpBuffer(str, "ALTER SUBSCRIPTION %s ENABLE", subname);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not enable subscription \"%s\": %s",
- dbinfo->subname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not enable subscription \"%s\": %s",
+ dbinfo->subname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -2154,7 +2317,9 @@ get_publisher_databases(struct CreateSubscriberOptions *opt,
res = PQexec(conn, "SELECT datname FROM pg_database WHERE datistemplate = false AND datallowconn AND datconnlimit <> -2 ORDER BY 1");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain a list of databases: %s", PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain a list of databases: %s",
+ PQresultErrorMessage(res));
PQclear(res);
disconnect_database(conn, true);
}
@@ -2258,9 +2423,11 @@ main(int argc, char **argv)
#ifndef WIN32
if (geteuid() == 0)
{
- pg_log_error("cannot be executed by \"root\"");
- pg_log_error_hint("You must run %s as the PostgreSQL superuser.",
- progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "cannot be executed by \"root\"");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "You must run %s as the PostgreSQL superuser.",
+ progname);
exit(1);
}
#endif
@@ -2351,7 +2518,9 @@ main(int argc, char **argv)
break;
default:
/* getopt_long already emitted a complaint */
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.",
+ progname);
exit(1);
}
}
@@ -2372,9 +2541,12 @@ main(int argc, char **argv)
if (bad_switch)
{
- pg_log_error("options %s and %s cannot be used together",
- bad_switch, "-a/--all");
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "options %s and %s cannot be used together",
+ bad_switch, "-a/--all");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.",
+ progname);
exit(1);
}
}
@@ -2382,17 +2554,21 @@ main(int argc, char **argv)
/* Any non-option arguments? */
if (optind < argc)
{
- pg_log_error("too many command-line arguments (first is \"%s\")",
- argv[optind]);
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "too many command-line arguments (first is \"%s\")",
+ argv[optind]);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.", progname);
exit(1);
}
/* Required arguments */
if (subscriber_dir == NULL)
{
- pg_log_error("no subscriber data directory specified");
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "no subscriber data directory specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.", progname);
exit(1);
}
@@ -2419,22 +2595,27 @@ main(int argc, char **argv)
* identical entries for physical and logical replication. If there is
* not, we would fail anyway.
*/
- pg_log_error("no publisher connection string specified");
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "no publisher connection string specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.", progname);
exit(1);
}
if (dry_run)
- pg_log_info("Executing in dry-run mode.\n"
- "The target directory will not be modified.");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "Executing in dry-run mode.\n"
+ "The target directory will not be modified.");
- pg_log_info("validating publisher connection string");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "validating publisher connection string");
pub_base_conninfo = get_base_conninfo(opt.pub_conninfo_str,
&dbname_conninfo);
if (pub_base_conninfo == NULL)
exit(1);
- pg_log_info("validating subscriber connection string");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "validating subscriber connection string");
sub_base_conninfo = get_sub_conninfo(&opt);
/*
@@ -2451,7 +2632,8 @@ main(int argc, char **argv)
if (opt.database_names.head == NULL)
{
- pg_log_info("no database was specified");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "no database was specified");
/*
* Try to obtain the dbname from the publisher conninfo. If dbname
@@ -2462,14 +2644,17 @@ main(int argc, char **argv)
simple_string_list_append(&opt.database_names, dbname_conninfo);
num_dbs++;
- pg_log_info("database name \"%s\" was extracted from the publisher connection string",
- dbname_conninfo);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "database name \"%s\" was extracted from the publisher connection string",
+ dbname_conninfo);
}
else
{
- pg_log_error("no database name specified");
- pg_log_error_hint("Try \"%s --help\" for more information.",
- progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "no database name specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.",
+ progname);
exit(1);
}
}
@@ -2477,23 +2662,29 @@ main(int argc, char **argv)
/* Number of object names must match number of databases */
if (num_pubs > 0 && num_pubs != num_dbs)
{
- pg_log_error("wrong number of publication names specified");
- pg_log_error_detail("The number of specified publication names (%d) must match the number of specified database names (%d).",
- num_pubs, num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "wrong number of publication names specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "The number of specified publication names (%d) must match the number of specified database names (%d).",
+ num_pubs, num_dbs);
exit(1);
}
if (num_subs > 0 && num_subs != num_dbs)
{
- pg_log_error("wrong number of subscription names specified");
- pg_log_error_detail("The number of specified subscription names (%d) must match the number of specified database names (%d).",
- num_subs, num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "wrong number of subscription names specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "The number of specified subscription names (%d) must match the number of specified database names (%d).",
+ num_subs, num_dbs);
exit(1);
}
if (num_replslots > 0 && num_replslots != num_dbs)
{
- pg_log_error("wrong number of replication slot names specified");
- pg_log_error_detail("The number of specified replication slot names (%d) must match the number of specified database names (%d).",
- num_replslots, num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "wrong number of replication slot names specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "The number of specified replication slot names (%d) must match the number of specified database names (%d).",
+ num_replslots, num_dbs);
exit(1);
}
@@ -2504,9 +2695,11 @@ main(int argc, char **argv)
dbinfos.objecttypes_to_clean |= OBJECTTYPE_PUBLICATIONS;
else
{
- pg_log_error("invalid object type \"%s\" specified for %s",
- cell->val, "--clean");
- pg_log_error_hint("The valid value is: \"%s\"", "publications");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "invalid object type \"%s\" specified for %s",
+ cell->val, "--clean");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "The valid value is: \"%s\"", "publications");
exit(1);
}
}
@@ -2550,8 +2743,10 @@ main(int argc, char **argv)
*/
if (stat(pidfile, &statbuf) == 0)
{
- pg_log_error("standby server is running");
- pg_log_error_hint("Stop the standby server and try again.");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "standby server is running");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Stop the standby server and try again.");
exit(1);
}
@@ -2560,7 +2755,8 @@ main(int argc, char **argv)
* by command-line options). The goal is to avoid connections during the
* transformation steps.
*/
- pg_log_info("starting the standby server with command-line options");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "starting the standby server with command-line options");
start_standby_server(&opt, true, false);
/* Check if the standby server is ready for logical replication */
@@ -2576,7 +2772,8 @@ main(int argc, char **argv)
* guarantees it) *before* creating the replication slots in
* setup_publisher().
*/
- pg_log_info("stopping the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "stopping the subscriber");
stop_standby_server(subscriber_dir);
/* Create the required objects for each database on publisher */
@@ -2590,7 +2787,8 @@ main(int argc, char **argv)
* until accepting connections. We don't want to start logical replication
* during setup.
*/
- pg_log_info("starting the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "starting the subscriber");
start_standby_server(&opt, true, true);
/* Waiting the subscriber to be promoted */
@@ -2611,7 +2809,8 @@ main(int argc, char **argv)
drop_failover_replication_slots(dbinfos.dbinfo);
/* Stop the subscriber */
- pg_log_info("stopping the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "stopping the subscriber");
stop_standby_server(subscriber_dir);
/* Change system identifier from subscriber */
@@ -2619,7 +2818,8 @@ main(int argc, char **argv)
success = true;
- pg_log_info("Done!");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "Done!");
return 0;
}
--
2.47.3
[application/octet-stream] v11-0002-Add-a-new-argument-l-logdir-to-pg_createsubscrib.patch (14.4K, 3-v11-0002-Add-a-new-argument-l-logdir-to-pg_createsubscrib.patch)
download | inline diff:
From ca05dfade11209fdcfa4223a9fbb9d43814eda78 Mon Sep 17 00:00:00 2001
From: Gyan Sreejith <[email protected]>
Date: Tue, 17 Mar 2026 19:10:28 -0400
Subject: [PATCH v11 2/3] Add a new argument -l <logdir> to
pg_createsubscriber.
Enabling the option to write messages to log files in the specified directory.
A new directory is created if required. A subdirectory is created with timestamp as its name, and it will contain two new logfiles:
1. pg_createsubscriber_server.log - captures messages related to starting and stopping the standby server.
2. pg_createsubscriber_internal.log - captures internal diagnostic output from pg_createsubscriber.
For example, if we specify -l abc as an argument, and if the timestamp on running it is 20260119T204317.204, a directory abc is created if it doesn't exist already, with 20260119T204317.204 as its subdirectory and it will contain the two log files pg_createsubscriber_server.log and pg_createsubscriber_internal.log
---
doc/src/sgml/ref/pg_createsubscriber.sgml | 28 +++
src/bin/pg_basebackup/pg_createsubscriber.c | 166 +++++++++++++++++-
.../t/040_pg_createsubscriber.pl | 41 ++++-
3 files changed, 225 insertions(+), 10 deletions(-)
diff --git a/doc/src/sgml/ref/pg_createsubscriber.sgml b/doc/src/sgml/ref/pg_createsubscriber.sgml
index cf45ff3573d..2898a5ea111 100644
--- a/doc/src/sgml/ref/pg_createsubscriber.sgml
+++ b/doc/src/sgml/ref/pg_createsubscriber.sgml
@@ -136,6 +136,34 @@ PostgreSQL documentation
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>-l <replaceable class="parameter">directory</replaceable></option></term>
+ <term><option>--logdir=<replaceable class="parameter">directory</replaceable></option></term>
+ <listitem>
+ <para>
+ 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.
+ <itemizedlist>
+ <listitem>
+ <para>
+ pg_createsubscriber_server.log which captures logs related to stopping
+ and starting the standby server,
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ pg_createsubscriber_internal.log which captures internal diagnostic
+ output (validations, checks, etc.)
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>-n</option></term>
<term><option>--dry-run</option></term>
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index b02af9a4cc0..87e4f95b22e 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -55,10 +55,14 @@
#define INCLUDED_CONF_FILE "pg_createsubscriber.conf"
#define INCLUDED_CONF_FILE_DISABLED INCLUDED_CONF_FILE ".disabled"
+#define SERVER_LOG_FILE_NAME "pg_createsubscriber_server"
+#define INTERNAL_LOG_FILE_NAME "pg_createsubscriber_internal"
+
/* Command-line options */
struct CreateSubscriberOptions
{
char *config_file; /* configuration file */
+ char *log_dir; /* log directory name */
char *pub_conninfo_str; /* publisher connection string */
char *socket_dir; /* directory for Unix-domain socket, if any */
char *sub_port; /* subscriber port number */
@@ -157,6 +161,9 @@ static void pg_createsub_log(enum pg_log_level, enum pg_log_part,
pg_attribute_printf(3, 4);
pg_noreturn static void pg_fatal(const char *pg_restrict fmt,...)
pg_attribute_printf(1, 2);
+static void internal_log_file_write(enum pg_log_level level,
+ const char *pg_restrict fmt, va_list args)
+ pg_attribute_printf(2, 0);
#define WAIT_INTERVAL 1 /* 1 second */
@@ -178,6 +185,10 @@ static pg_prng_state prng_state;
static char *pg_ctl_path = NULL;
static char *pg_resetwal_path = NULL;
+static FILE *internal_log_file_fp = NULL; /* File ptr to log all messages to */
+static char *log_timestamp = NULL; /* Timestamp to be used in all log file
+ * names */
+
/* standby / subscriber data directory */
static char *subscriber_dir = NULL;
@@ -192,7 +203,13 @@ pg_createsub_log(enum pg_log_level level, enum pg_log_part part,
va_list args;
va_start(args, fmt);
- pg_log_generic_v(level, part, fmt, args);
+
+ if (internal_log_file_fp != NULL)
+ internal_log_file_write(level, fmt, args);
+
+ if (internal_log_file_fp == NULL || level > PG_LOG_INFO)
+ pg_log_generic_v(level, part, fmt, args);
+
va_end(args);
}
@@ -308,6 +325,12 @@ cleanup_objects_atexit(void)
if (standby_running)
stop_standby_server(subscriber_dir);
+
+ if (internal_log_file_fp != NULL)
+ {
+ fclose(internal_log_file_fp);
+ internal_log_file_fp = NULL;
+ }
}
static void
@@ -322,6 +345,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"));
@@ -756,6 +780,7 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
bool crc_ok;
struct timeval tv;
+ char *out_file;
char *cmd_str;
pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
@@ -794,8 +819,13 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
"running pg_resetwal on the subscriber");
- cmd_str = psprintf("\"%s\" -D \"%s\" > \"%s\"", pg_resetwal_path,
- subscriber_dir, DEVNULL);
+ if (opt->log_dir != NULL)
+ out_file = psprintf("%s/%s/%s.log", opt->log_dir, log_timestamp, SERVER_LOG_FILE_NAME);
+ else
+ out_file = DEVNULL;
+
+ cmd_str = psprintf("\"%s\" -D \"%s\" >> \"%s\"", pg_resetwal_path,
+ subscriber_dir, out_file);
pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
"pg_resetwal command is: %s", cmd_str);
@@ -1018,6 +1048,110 @@ server_is_in_recovery(PGconn *conn)
return ret == 0;
}
+static void
+internal_log_file_write(enum pg_log_level level, const char *pg_restrict fmt,
+ va_list args)
+{
+ if (level < __pg_log_level)
+ return;
+
+ if (internal_log_file_fp == NULL)
+ return;
+
+ vfprintf(internal_log_file_fp, fmt, args);
+
+ fprintf(internal_log_file_fp, "\n");
+ fflush(internal_log_file_fp);
+}
+
+/*
+ * Open a new logfile with proper permissions.
+ * From src/backend/postmaster/syslogger.c
+ */
+static FILE *
+logfile_open(const char *filename, const char *mode)
+{
+ FILE *fh;
+ mode_t oumask;
+
+ oumask = umask((mode_t) ((~(S_IRUSR | S_IWUSR)) & (S_IRWXU | S_IRWXG | S_IRWXO)));
+ fh = fopen(filename, mode);
+ umask(oumask);
+
+ if (fh)
+ {
+ setvbuf(fh, NULL, PG_IOLBF, 0);
+
+#ifdef WIN32
+ /* use CRLF line endings on Windows */
+ _setmode(_fileno(fh), _O_TEXT);
+#endif
+ }
+ else
+ pg_fatal("could not open log file \"%s\": %m",
+ filename);
+
+ return fh;
+}
+
+static void
+make_dir(const char *dir)
+{
+ struct stat statbuf;
+
+ 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(const char *log_dir)
+{
+ char timestamp[128];
+ struct timeval tval;
+ time_t now;
+ struct tm tmbuf;
+ 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));
+
+ /* append milliseconds */
+ 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);
+
+ /* Create timestamp directory */
+ make_dir(timestamp_dir);
+}
+
/*
* Is the primary server ready for logical replication?
*
@@ -1776,6 +1910,9 @@ 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/%s/%s.log", opt->log_dir, log_timestamp, SERVER_LOG_FILE_NAME);
+
pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
"pg_ctl command is: %s", pg_ctl_cmd->data);
rc = system(pg_ctl_cmd->data);
@@ -2346,6 +2483,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'},
@@ -2404,6 +2542,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;
@@ -2434,7 +2573,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)
@@ -2455,6 +2594,10 @@ 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);
+ break;
case 'n':
dry_run = true;
break;
@@ -2525,6 +2668,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)
{
@@ -2821,5 +2976,8 @@ main(int argc, char **argv)
pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
"Done!");
+ if (internal_log_file_fp != NULL)
+ fclose(internal_log_file_fp);
+
return 0;
}
diff --git a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
index 0c27fca7bb7..4ddfb621a5d 100644
--- a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
+++ b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
@@ -14,6 +14,7 @@ program_version_ok('pg_createsubscriber');
program_options_handling_ok('pg_createsubscriber');
my $datadir = PostgreSQL::Test::Utils::tempdir;
+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.
@@ -362,9 +363,35 @@ command_ok(
'--subscription' => 'sub2',
'--database' => $db1,
'--database' => $db2,
+ '--logdir' => $logdir,
],
'run pg_createsubscriber --dry-run on node S');
+# Check that the log files were created
+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 $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 $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;
is($node_s->safe_psql('postgres', 'SELECT pg_catalog.pg_is_in_recovery()'),
@@ -444,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);
));
@@ -540,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');
@@ -555,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(
@@ -581,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");
--
2.47.3
[application/octet-stream] v11-0003-Address-comments-from-Hayato-Kuroda.patch (5.3K, 4-v11-0003-Address-comments-from-Hayato-Kuroda.patch)
download | inline diff:
From 1f72429ebf20958973edb84822c2fbbf603165f5 Mon Sep 17 00:00:00 2001
From: Hayato Kuroda <[email protected]>
Date: Wed, 18 Mar 2026 21:43:08 +0900
Subject: [PATCH v11 3/3] Address comments from Hayato Kuroda
---
src/bin/pg_basebackup/pg_createsubscriber.c | 89 +++++++++------------
1 file changed, 36 insertions(+), 53 deletions(-)
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index 87e4f95b22e..83b64960da8 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -186,8 +186,7 @@ static char *pg_ctl_path = NULL;
static char *pg_resetwal_path = NULL;
static FILE *internal_log_file_fp = NULL; /* File ptr to log all messages to */
-static char *log_timestamp = NULL; /* Timestamp to be used in all log file
- * names */
+static char logdir[MAXPGPATH]; /* Directory log files are put (if specified) */
/* standby / subscriber data directory */
static char *subscriber_dir = NULL;
@@ -819,8 +818,13 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
"running pg_resetwal on the subscriber");
- if (opt->log_dir != NULL)
- out_file = psprintf("%s/%s/%s.log", opt->log_dir, log_timestamp, SERVER_LOG_FILE_NAME);
+ /*
+ * Redirecting the output to the logfile if specified. Since the output
+ * would be very short, around one line, we do not provide a separate file
+ * for it; it's done as a part of the server log.
+ */
+ if (opt->log_dir)
+ out_file = psprintf("%s/%s.log", logdir, SERVER_LOG_FILE_NAME);
else
out_file = DEVNULL;
@@ -1052,13 +1056,13 @@ static void
internal_log_file_write(enum pg_log_level level, const char *pg_restrict fmt,
va_list args)
{
- if (level < __pg_log_level)
- return;
+ Assert(internal_log_file_fp);
- if (internal_log_file_fp == NULL)
+ /* Do nothing if log level is too low. */
+ if (level < __pg_log_level)
return;
- vfprintf(internal_log_file_fp, fmt, args);
+ vfprintf(internal_log_file_fp, _(fmt), args);
fprintf(internal_log_file_fp, "\n");
fflush(internal_log_file_fp);
@@ -1095,33 +1099,12 @@ logfile_open(const char *filename, const char *mode)
}
static void
-make_dir(const char *dir)
-{
- struct stat statbuf;
-
- 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(const char *log_dir)
+make_output_dirs(const char *log_basedir)
{
char timestamp[128];
struct timeval tval;
time_t now;
struct tm tmbuf;
- char timestamp_dir[MAXPGPATH];
int len;
/* Generate timestamp */
@@ -1136,20 +1119,20 @@ make_output_dirs(const char *log_dir)
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);
+ len = snprintf(logdir, MAXPGPATH, "%s/%s", log_basedir, timestamp);
if (len >= MAXPGPATH)
pg_fatal("directory path for log files, %s/%s, is too long",
- log_dir, timestamp);
+ logdir, timestamp);
+
+ /* Create base directory (ignore if exists) */
+ if (mkdir(log_basedir, S_IRWXU) < 0 && errno != EEXIST)
+ pg_fatal("could not create directory \"%s\": %m", log_basedir);
- /* Create timestamp directory */
- make_dir(timestamp_dir);
+ /* Create BASE_DIR/$timestamp */
+ if (mkdir(logdir, S_IRWXU) < 0)
+ pg_fatal("could not create directory \"%s\": %m", logdir);
}
/*
@@ -1910,8 +1893,8 @@ 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/%s/%s.log", opt->log_dir, log_timestamp, SERVER_LOG_FILE_NAME);
+ if (opt->log_dir)
+ appendPQExpBuffer(pg_ctl_cmd, " -l %s/%s.log", logdir, SERVER_LOG_FILE_NAME);
pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
"pg_ctl command is: %s", pg_ctl_cmd->data);
@@ -2668,18 +2651,6 @@ 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)
{
@@ -2757,6 +2728,18 @@ main(int argc, char **argv)
exit(1);
}
+ if (opt.log_dir != NULL)
+ {
+ char *internal_log_file;
+
+ make_output_dirs(opt.log_dir);
+ internal_log_file = psprintf("%s/%s.log", logdir,
+ 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);
+ }
+
if (dry_run)
pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
"Executing in dry-run mode.\n"
--
2.47.3
^ permalink raw reply [nested|flat] 55+ messages in thread
* RE: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
@ 2026-03-19 11:55 ` Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
0 siblings, 1 reply; 55+ messages in thread
From: Kuroda, Hayato/黒田 隼人 @ 2026-03-19 11:55 UTC (permalink / raw)
To: Kuroda, Hayato/黒田 隼人 <[email protected]>; 'Amit Kapila' <[email protected]>; Gyan Sreejith <[email protected]>; +Cc: Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
Hi,
While checking again, I found an issue in the new logging function - same va_list
is used twice, but it was the undefined behavior. va_copy() is used to fix the issue.
Also, below points were found and fixed in 0003. Thanks Peter Smith to work on.
```
+ printf(_(" -l, --logdir=LOGDIR location for the new log directory\n"));
```
But there is a possibility that existing directory is specified, right?
```
+my $logdir = PostgreSQL::Test::Utils::tempdir + "/logdir";
```
Isn't it enough to just use tempdir here? At least, I think using "+" is not
appropriate, "." is used to connect strings.
```
+ if (opt->log_dir != NULL)
+ appendPQExpBuffer(pg_ctl_cmd, " -l %s/%s/%s.log", opt->log_dir, log_timestamp, SERVER_LOG_FILE_NAME);
```
I do not have 100% confident, but do we have to quote the logfile here?
Best regards,
Hayato Kuroda
FUJITSU LIMITED
Attachments:
[application/octet-stream] v12-0001-pg_createsubscriber-use-own-reporting-functions.patch (55.1K, 2-v12-0001-pg_createsubscriber-use-own-reporting-functions.patch)
download | inline diff:
From 5af62b9a19878916662a9aa2281d450d18785c42 Mon Sep 17 00:00:00 2001
From: Hayato Kuroda <[email protected]>
Date: Wed, 18 Mar 2026 20:20:50 +0900
Subject: [PATCH v12 1/3] pg_createsubscriber: use own reporting functions
This commit converts all pg_log_xxx families to call a new reporting function
pg_createsub_log(). Also, pg_fatal() is overwritten to use its own.
This commit changes nothing from the outside, but is needed for the upcoming
commit.
---
src/bin/pg_basebackup/pg_createsubscriber.c | 711 +++++++++++++-------
1 file changed, 458 insertions(+), 253 deletions(-)
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index 2bc84505aab..0770e163041 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -31,6 +31,12 @@
#include "fe_utils/version.h"
#include "getopt_long.h"
+/*
+ * For now, pg_createsubscriber does not use common/logging.c; use our own
+ * pg_fatal.
+ */
+#undef pg_fatal
+
#define DEFAULT_SUB_PORT "50432"
#define OBJECTTYPE_PUBLICATIONS 0x0001
@@ -146,6 +152,11 @@ 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 pg_createsub_log(enum pg_log_level, enum pg_log_part,
+ const char *pg_restrict fmt,...)
+ pg_attribute_printf(3, 4);
+pg_noreturn static void pg_fatal(const char *pg_restrict fmt,...)
+ pg_attribute_printf(1, 2);
#define WAIT_INTERVAL 1 /* 1 second */
@@ -174,6 +185,32 @@ static bool recovery_ended = false;
static bool standby_running = false;
static bool recovery_params_set = false;
+static void
+pg_createsub_log(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt,...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+
+ pg_log_generic_v(level, part, fmt, args);
+
+ va_end(args);
+}
+
+static void
+pg_fatal(const char *pg_restrict fmt,...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+
+ pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, args);
+
+ va_end(args);
+
+ exit(1);
+}
/*
* Clean up objects created by pg_createsubscriber.
@@ -205,7 +242,8 @@ cleanup_objects_atexit(void)
if (durable_rename(conf_filename, conf_filename_disabled) != 0)
{
/* durable_rename() has already logged something. */
- pg_log_warning_hint("A manual removal of the recovery parameters may be required.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "A manual removal of the recovery parameters may be required.");
}
}
@@ -219,9 +257,11 @@ cleanup_objects_atexit(void)
*/
if (recovery_ended)
{
- pg_log_warning("failed after the end of recovery");
- pg_log_warning_hint("The target server cannot be used as a physical replica anymore. "
- "You must recreate the physical replica before continuing.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "failed after the end of recovery");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "The target server cannot be used as a physical replica anymore. "
+ "You must recreate the physical replica before continuing.");
}
for (int i = 0; i < num_dbs; i++)
@@ -251,17 +291,21 @@ cleanup_objects_atexit(void)
*/
if (dbinfo->made_publication)
{
- pg_log_warning("publication \"%s\" created in database \"%s\" on primary was left behind",
- dbinfo->pubname,
- dbinfo->dbname);
- pg_log_warning_hint("Drop this publication before trying again.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "publication \"%s\" created in database \"%s\" on primary was left behind",
+ dbinfo->pubname,
+ dbinfo->dbname);
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop this publication before trying again.");
}
if (dbinfo->made_replslot)
{
- pg_log_warning("replication slot \"%s\" created in database \"%s\" on primary was left behind",
- dbinfo->replslotname,
- dbinfo->dbname);
- pg_log_warning_hint("Drop this replication slot soon to avoid retention of WAL files.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "replication slot \"%s\" created in database \"%s\" on primary was left behind",
+ dbinfo->replslotname,
+ dbinfo->dbname);
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop this replication slot soon to avoid retention of WAL files.");
}
}
}
@@ -342,7 +386,8 @@ get_base_conninfo(const char *conninfo, char **dbname)
conn_opts = PQconninfoParse(conninfo, &errmsg);
if (conn_opts == NULL)
{
- pg_log_error("could not parse connection string: %s", errmsg);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not parse connection string: %s", errmsg);
PQfreemem(errmsg);
return NULL;
}
@@ -426,7 +471,8 @@ get_exec_path(const char *argv0, const char *progname)
progname, full_path, "pg_createsubscriber");
}
- pg_log_debug("%s path is: %s", progname, exec_path);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "%s path is: %s", progname, exec_path);
return exec_path;
}
@@ -443,8 +489,9 @@ check_data_directory(const char *datadir)
uint32 major_version;
char *version_str;
- pg_log_info("checking if directory \"%s\" is a cluster data directory",
- datadir);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "checking if directory \"%s\" is a cluster data directory",
+ datadir);
if (stat(datadir, &statbuf) != 0)
{
@@ -462,9 +509,11 @@ check_data_directory(const char *datadir)
major_version = GET_PG_MAJORVERSION_NUM(get_pg_version(datadir, &version_str));
if (major_version != PG_MAJORVERSION_NUM)
{
- pg_log_error("data directory is of wrong version");
- pg_log_error_detail("File \"%s\" contains \"%s\", which is not compatible with this program's version \"%s\".",
- "PG_VERSION", version_str, PG_MAJORVERSION);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "data directory is of wrong version");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "File \"%s\" contains \"%s\", which is not compatible with this program's version \"%s\".",
+ "PG_VERSION", version_str, PG_MAJORVERSION);
exit(1);
}
}
@@ -547,14 +596,16 @@ store_pub_sub_info(const struct CreateSubscriberOptions *opt,
dbinfo[i].subname = NULL;
/* Other fields will be filled later */
- pg_log_debug("publisher(%d): publication: %s ; replication slot: %s ; connection string: %s", i,
- dbinfo[i].pubname ? dbinfo[i].pubname : "(auto)",
- dbinfo[i].replslotname ? dbinfo[i].replslotname : "(auto)",
- dbinfo[i].pubconninfo);
- pg_log_debug("subscriber(%d): subscription: %s ; connection string: %s, two_phase: %s", i,
- dbinfo[i].subname ? dbinfo[i].subname : "(auto)",
- dbinfo[i].subconninfo,
- dbinfos.two_phase ? "true" : "false");
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher(%d): publication: %s ; replication slot: %s ; connection string: %s", i,
+ dbinfo[i].pubname ? dbinfo[i].pubname : "(auto)",
+ dbinfo[i].replslotname ? dbinfo[i].replslotname : "(auto)",
+ dbinfo[i].pubconninfo);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber(%d): subscription: %s ; connection string: %s, two_phase: %s", i,
+ dbinfo[i].subname ? dbinfo[i].subname : "(auto)",
+ dbinfo[i].subconninfo,
+ dbinfos.two_phase ? "true" : "false");
if (num_pubs > 0)
pubcell = pubcell->next;
@@ -582,8 +633,9 @@ connect_database(const char *conninfo, bool exit_on_error)
conn = PQconnectdb(conninfo);
if (PQstatus(conn) != CONNECTION_OK)
{
- pg_log_error("connection to database failed: %s",
- PQerrorMessage(conn));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "connection to database failed: %s",
+ PQerrorMessage(conn));
PQfinish(conn);
if (exit_on_error)
@@ -595,8 +647,9 @@ connect_database(const char *conninfo, bool exit_on_error)
res = PQexec(conn, ALWAYS_SECURE_SEARCH_PATH_SQL);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not clear \"search_path\": %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not clear \"search_path\": %s",
+ PQresultErrorMessage(res));
PQclear(res);
PQfinish(conn);
@@ -635,27 +688,31 @@ get_primary_sysid(const char *conninfo)
PGresult *res;
uint64 sysid;
- pg_log_info("getting system identifier from publisher");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "getting system identifier from publisher");
conn = connect_database(conninfo, true);
res = PQexec(conn, "SELECT system_identifier FROM pg_catalog.pg_control_system()");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not get system identifier: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not get system identifier: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
if (PQntuples(res) != 1)
{
- pg_log_error("could not get system identifier: got %d rows, expected %d row",
- PQntuples(res), 1);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not get system identifier: got %d rows, expected %d row",
+ PQntuples(res), 1);
disconnect_database(conn, true);
}
sysid = strtou64(PQgetvalue(res, 0, 0), NULL, 10);
- pg_log_info("system identifier is %" PRIu64 " on publisher", sysid);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "system identifier is %" PRIu64 " on publisher", sysid);
PQclear(res);
disconnect_database(conn, false);
@@ -675,7 +732,8 @@ get_standby_sysid(const char *datadir)
bool crc_ok;
uint64 sysid;
- pg_log_info("getting system identifier from subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "getting system identifier from subscriber");
cf = get_controlfile(datadir, &crc_ok);
if (!crc_ok)
@@ -683,7 +741,8 @@ get_standby_sysid(const char *datadir)
sysid = cf->system_identifier;
- pg_log_info("system identifier is %" PRIu64 " on subscriber", sysid);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "system identifier is %" PRIu64 " on subscriber", sysid);
pg_free(cf);
@@ -704,7 +763,8 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
char *cmd_str;
- pg_log_info("modifying system identifier of subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "modifying system identifier of subscriber");
cf = get_controlfile(subscriber_dir, &crc_ok);
if (!crc_ok)
@@ -721,31 +781,37 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
cf->system_identifier |= getpid() & 0xFFF;
if (dry_run)
- pg_log_info("dry-run: would set system identifier to %" PRIu64 " on subscriber",
- cf->system_identifier);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would set system identifier to %" PRIu64 " on subscriber",
+ cf->system_identifier);
else
{
update_controlfile(subscriber_dir, cf, true);
- pg_log_info("system identifier is %" PRIu64 " on subscriber",
- cf->system_identifier);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "system identifier is %" PRIu64 " on subscriber",
+ cf->system_identifier);
}
if (dry_run)
- pg_log_info("dry-run: would run pg_resetwal on the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would run pg_resetwal on the subscriber");
else
- pg_log_info("running pg_resetwal on the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "running pg_resetwal on the subscriber");
cmd_str = psprintf("\"%s\" -D \"%s\" > \"%s\"", pg_resetwal_path,
subscriber_dir, DEVNULL);
- pg_log_debug("pg_resetwal command is: %s", cmd_str);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "pg_resetwal command is: %s", cmd_str);
if (!dry_run)
{
int rc = system(cmd_str);
if (rc == 0)
- pg_log_info("successfully reset WAL on the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "successfully reset WAL on the subscriber");
else
pg_fatal("could not reset WAL on subscriber: %s", wait_result_to_str(rc));
}
@@ -771,15 +837,17 @@ generate_object_name(PGconn *conn)
"WHERE datname = pg_catalog.current_database()");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain database OID: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain database OID: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
if (PQntuples(res) != 1)
{
- pg_log_error("could not obtain database OID: got %d rows, expected %d row",
- PQntuples(res), 1);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain database OID: got %d rows, expected %d row",
+ PQntuples(res), 1);
disconnect_database(conn, true);
}
@@ -819,8 +887,9 @@ find_publication(PGconn *conn, const char *pubname, const char *dbname)
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not find publication \"%s\" in database \"%s\": %s",
- pubname, dbname, PQerrorMessage(conn));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not find publication \"%s\" in database \"%s\": %s",
+ pubname, dbname, PQerrorMessage(conn));
disconnect_database(conn, true);
}
@@ -873,8 +942,9 @@ setup_publisher(struct LogicalRepInfo *dbinfo)
if (find_publication(conn, dbinfo[i].pubname, dbinfo[i].dbname))
{
/* Reuse existing publication on publisher. */
- pg_log_info("use existing publication \"%s\" in database \"%s\"",
- dbinfo[i].pubname, dbinfo[i].dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "use existing publication \"%s\" in database \"%s\"",
+ dbinfo[i].pubname, dbinfo[i].dbname);
/* Don't remove pre-existing publication if an error occurs. */
dbinfo[i].made_publication = false;
}
@@ -912,8 +982,9 @@ setup_publisher(struct LogicalRepInfo *dbinfo)
res = PQexec(conn, "SELECT pg_log_standby_snapshot()");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not write an additional WAL record: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not write an additional WAL record: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
PQclear(res);
@@ -938,8 +1009,9 @@ server_is_in_recovery(PGconn *conn)
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain recovery progress: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain recovery progress: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -971,7 +1043,8 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
int max_prepared_transactions;
char *max_slot_wal_keep_size;
- pg_log_info("checking settings on publisher");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "checking settings on publisher");
conn = connect_database(dbinfo[0].pubconninfo, true);
@@ -981,7 +1054,8 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
*/
if (server_is_in_recovery(conn))
{
- pg_log_error("primary server cannot be in recovery");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "primary server cannot be in recovery");
disconnect_database(conn, true);
}
@@ -1007,8 +1081,9 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain publisher settings: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain publisher settings: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1022,48 +1097,63 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
PQclear(res);
- pg_log_debug("publisher: wal_level: %s", wal_level);
- pg_log_debug("publisher: max_replication_slots: %d", max_repslots);
- pg_log_debug("publisher: current replication slots: %d", cur_repslots);
- pg_log_debug("publisher: max_wal_senders: %d", max_walsenders);
- pg_log_debug("publisher: current wal senders: %d", cur_walsenders);
- pg_log_debug("publisher: max_prepared_transactions: %d",
- max_prepared_transactions);
- pg_log_debug("publisher: max_slot_wal_keep_size: %s",
- max_slot_wal_keep_size);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: wal_level: %s", wal_level);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: max_replication_slots: %d", max_repslots);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: current replication slots: %d", cur_repslots);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: max_wal_senders: %d", max_walsenders);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: current wal senders: %d", cur_walsenders);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: max_prepared_transactions: %d",
+ max_prepared_transactions);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: max_slot_wal_keep_size: %s",
+ max_slot_wal_keep_size);
disconnect_database(conn, false);
if (strcmp(wal_level, "minimal") == 0)
{
- pg_log_error("publisher requires \"wal_level\" >= \"replica\"");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "publisher requires \"wal_level\" >= \"replica\"");
failed = true;
}
if (max_repslots - cur_repslots < num_dbs)
{
- pg_log_error("publisher requires %d replication slots, but only %d remain",
- num_dbs, max_repslots - cur_repslots);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_replication_slots", cur_repslots + num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "publisher requires %d replication slots, but only %d remain",
+ num_dbs, max_repslots - cur_repslots);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_replication_slots", cur_repslots + num_dbs);
failed = true;
}
if (max_walsenders - cur_walsenders < num_dbs)
{
- pg_log_error("publisher requires %d WAL sender processes, but only %d remain",
- num_dbs, max_walsenders - cur_walsenders);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_wal_senders", cur_walsenders + num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "publisher requires %d WAL sender processes, but only %d remain",
+ num_dbs, max_walsenders - cur_walsenders);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_wal_senders", cur_walsenders + num_dbs);
failed = true;
}
if (max_prepared_transactions != 0 && !dbinfos.two_phase)
{
- pg_log_warning("two_phase option will not be enabled for replication slots");
- pg_log_warning_detail("Subscriptions will be created with the two_phase option disabled. "
- "Prepared transactions will be replicated at COMMIT PREPARED.");
- pg_log_warning_hint("You can use the command-line option --enable-two-phase to enable two_phase.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "two_phase option will not be enabled for replication slots");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_DETAIL,
+ "Subscriptions will be created with the two_phase option disabled. "
+ "Prepared transactions will be replicated at COMMIT PREPARED.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "You can use the command-line option --enable-two-phase to enable two_phase.");
}
/*
@@ -1073,9 +1163,11 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
*/
if (dry_run && (strcmp(max_slot_wal_keep_size, "-1") != 0))
{
- pg_log_warning("required WAL could be removed from the publisher");
- pg_log_warning_hint("Set the configuration parameter \"%s\" to -1 to ensure that required WAL files are not prematurely removed.",
- "max_slot_wal_keep_size");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "required WAL could be removed from the publisher");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Set the configuration parameter \"%s\" to -1 to ensure that required WAL files are not prematurely removed.",
+ "max_slot_wal_keep_size");
}
pg_free(wal_level);
@@ -1106,14 +1198,16 @@ check_subscriber(const struct LogicalRepInfo *dbinfo)
int max_replorigins;
int max_wprocs;
- pg_log_info("checking settings on subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "checking settings on subscriber");
conn = connect_database(dbinfo[0].subconninfo, true);
/* The target server must be a standby */
if (!server_is_in_recovery(conn))
{
- pg_log_error("target server must be a standby");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "target server must be a standby");
disconnect_database(conn, true);
}
@@ -1137,8 +1231,9 @@ check_subscriber(const struct LogicalRepInfo *dbinfo)
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain subscriber settings: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain subscriber settings: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1148,12 +1243,16 @@ check_subscriber(const struct LogicalRepInfo *dbinfo)
if (strcmp(PQgetvalue(res, 3, 0), "") != 0)
primary_slot_name = pg_strdup(PQgetvalue(res, 3, 0));
- pg_log_debug("subscriber: max_logical_replication_workers: %d",
- max_lrworkers);
- pg_log_debug("subscriber: max_active_replication_origins: %d", max_replorigins);
- pg_log_debug("subscriber: max_worker_processes: %d", max_wprocs);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber: max_logical_replication_workers: %d",
+ max_lrworkers);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber: max_active_replication_origins: %d", max_replorigins);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber: max_worker_processes: %d", max_wprocs);
if (primary_slot_name)
- pg_log_debug("subscriber: primary_slot_name: %s", primary_slot_name);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber: primary_slot_name: %s", primary_slot_name);
PQclear(res);
@@ -1161,28 +1260,34 @@ check_subscriber(const struct LogicalRepInfo *dbinfo)
if (max_replorigins < num_dbs)
{
- pg_log_error("subscriber requires %d active replication origins, but only %d remain",
- num_dbs, max_replorigins);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_active_replication_origins", num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "subscriber requires %d active replication origins, but only %d remain",
+ num_dbs, max_replorigins);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_active_replication_origins", num_dbs);
failed = true;
}
if (max_lrworkers < num_dbs)
{
- pg_log_error("subscriber requires %d logical replication workers, but only %d remain",
- num_dbs, max_lrworkers);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_logical_replication_workers", num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "subscriber requires %d logical replication workers, but only %d remain",
+ num_dbs, max_lrworkers);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_logical_replication_workers", num_dbs);
failed = true;
}
if (max_wprocs < num_dbs + 1)
{
- pg_log_error("subscriber requires %d worker processes, but only %d remain",
- num_dbs + 1, max_wprocs);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_worker_processes", num_dbs + 1);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "subscriber requires %d worker processes, but only %d remain",
+ num_dbs + 1, max_wprocs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_worker_processes", num_dbs + 1);
failed = true;
}
@@ -1215,19 +1320,22 @@ drop_existing_subscription(PGconn *conn, const char *subname, const char *dbname
appendPQExpBuffer(query, " DROP SUBSCRIPTION %s;", subname);
if (dry_run)
- pg_log_info("dry-run: would drop subscription \"%s\" in database \"%s\"",
- subname, dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would drop subscription \"%s\" in database \"%s\"",
+ subname, dbname);
else
{
- pg_log_info("dropping subscription \"%s\" in database \"%s\"",
- subname, dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dropping subscription \"%s\" in database \"%s\"",
+ subname, dbname);
res = PQexec(conn, query->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not drop subscription \"%s\": %s",
- subname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not drop subscription \"%s\": %s",
+ subname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1261,8 +1369,9 @@ check_and_drop_existing_subscriptions(PGconn *conn,
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain pre-existing subscriptions: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain pre-existing subscriptions: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1373,7 +1482,8 @@ setup_recovery(const struct LogicalRepInfo *dbinfo, const char *datadir, const c
lsn);
}
- pg_log_debug("recovery parameters:\n%s", recoveryconfcontents->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "recovery parameters:\n%s", recoveryconfcontents->data);
if (!dry_run)
{
@@ -1427,9 +1537,11 @@ drop_primary_replication_slot(struct LogicalRepInfo *dbinfo, const char *slotnam
}
else
{
- pg_log_warning("could not drop replication slot \"%s\" on primary",
- slotname);
- pg_log_warning_hint("Drop this replication slot soon to avoid retention of WAL files.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "could not drop replication slot \"%s\" on primary",
+ slotname);
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop this replication slot soon to avoid retention of WAL files.");
}
}
@@ -1461,9 +1573,11 @@ drop_failover_replication_slots(struct LogicalRepInfo *dbinfo)
}
else
{
- pg_log_warning("could not obtain failover replication slot information: %s",
- PQresultErrorMessage(res));
- pg_log_warning_hint("Drop the failover replication slots on subscriber soon to avoid retention of WAL files.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "could not obtain failover replication slot information: %s",
+ PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop the failover replication slots on subscriber soon to avoid retention of WAL files.");
}
PQclear(res);
@@ -1471,8 +1585,10 @@ drop_failover_replication_slots(struct LogicalRepInfo *dbinfo)
}
else
{
- pg_log_warning("could not drop failover replication slot");
- pg_log_warning_hint("Drop the failover replication slots on subscriber soon to avoid retention of WAL files.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "could not drop failover replication slot");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop the failover replication slots on subscriber soon to avoid retention of WAL files.");
}
}
@@ -1494,11 +1610,13 @@ create_logical_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo)
Assert(conn != NULL);
if (dry_run)
- pg_log_info("dry-run: would create the replication slot \"%s\" in database \"%s\" on publisher",
- slot_name, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would create the replication slot \"%s\" in database \"%s\" on publisher",
+ slot_name, dbinfo->dbname);
else
- pg_log_info("creating the replication slot \"%s\" in database \"%s\" on publisher",
- slot_name, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "creating the replication slot \"%s\" in database \"%s\" on publisher",
+ slot_name, dbinfo->dbname);
slot_name_esc = PQescapeLiteral(conn, slot_name, strlen(slot_name));
@@ -1509,16 +1627,18 @@ create_logical_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo)
PQfreemem(slot_name_esc);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not create replication slot \"%s\" in database \"%s\": %s",
- slot_name, dbinfo->dbname,
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not create replication slot \"%s\" in database \"%s\": %s",
+ slot_name, dbinfo->dbname,
+ PQresultErrorMessage(res));
PQclear(res);
destroyPQExpBuffer(str);
return NULL;
@@ -1547,11 +1667,13 @@ drop_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo,
Assert(conn != NULL);
if (dry_run)
- pg_log_info("dry-run: would drop the replication slot \"%s\" in database \"%s\"",
- slot_name, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would drop the replication slot \"%s\" in database \"%s\"",
+ slot_name, dbinfo->dbname);
else
- pg_log_info("dropping the replication slot \"%s\" in database \"%s\"",
- slot_name, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dropping the replication slot \"%s\" in database \"%s\"",
+ slot_name, dbinfo->dbname);
slot_name_esc = PQescapeLiteral(conn, slot_name, strlen(slot_name));
@@ -1559,15 +1681,17 @@ drop_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo,
PQfreemem(slot_name_esc);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not drop replication slot \"%s\" in database \"%s\": %s",
- slot_name, dbinfo->dbname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not drop replication slot \"%s\" in database \"%s\": %s",
+ slot_name, dbinfo->dbname, PQresultErrorMessage(res));
dbinfo->made_replslot = false; /* don't try again. */
}
@@ -1587,25 +1711,32 @@ pg_ctl_status(const char *pg_ctl_cmd, int rc)
{
if (WIFEXITED(rc))
{
- pg_log_error("pg_ctl failed with exit code %d", WEXITSTATUS(rc));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "pg_ctl failed with exit code %d",
+ WEXITSTATUS(rc));
}
else if (WIFSIGNALED(rc))
{
#if defined(WIN32)
- pg_log_error("pg_ctl was terminated by exception 0x%X",
- WTERMSIG(rc));
- pg_log_error_detail("See C include file \"ntstatus.h\" for a description of the hexadecimal value.");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "pg_ctl was terminated by exception 0x%X",
+ WTERMSIG(rc));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "See C include file \"ntstatus.h\" for a description of the hexadecimal value.");
#else
- pg_log_error("pg_ctl was terminated by signal %d: %s",
- WTERMSIG(rc), pg_strsignal(WTERMSIG(rc)));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "pg_ctl was terminated by signal %d: %s",
+ WTERMSIG(rc), pg_strsignal(WTERMSIG(rc)));
#endif
}
else
{
- pg_log_error("pg_ctl exited with unrecognized status %d", rc);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "pg_ctl exited with unrecognized status %d", rc);
}
- pg_log_error_detail("The failed command was: %s", pg_ctl_cmd);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "The failed command was: %s", pg_ctl_cmd);
exit(1);
}
}
@@ -1650,12 +1781,14 @@ start_standby_server(const struct CreateSubscriberOptions *opt, bool restricted_
if (restrict_logical_worker)
appendPQExpBufferStr(pg_ctl_cmd, " -o \"-c max_logical_replication_workers=0\"");
- pg_log_debug("pg_ctl command is: %s", pg_ctl_cmd->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "pg_ctl command is: %s", pg_ctl_cmd->data);
rc = system(pg_ctl_cmd->data);
pg_ctl_status(pg_ctl_cmd->data, rc);
standby_running = true;
destroyPQExpBuffer(pg_ctl_cmd);
- pg_log_info("server was started");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "server was started");
}
static void
@@ -1666,11 +1799,13 @@ stop_standby_server(const char *datadir)
pg_ctl_cmd = psprintf("\"%s\" stop -D \"%s\" -s", pg_ctl_path,
datadir);
- pg_log_debug("pg_ctl command is: %s", pg_ctl_cmd);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "pg_ctl command is: %s", pg_ctl_cmd);
rc = system(pg_ctl_cmd);
pg_ctl_status(pg_ctl_cmd, rc);
standby_running = false;
- pg_log_info("server was stopped");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "server was stopped");
}
/*
@@ -1689,7 +1824,8 @@ wait_for_end_recovery(const char *conninfo, const struct CreateSubscriberOptions
bool ready = false;
int timer = 0;
- pg_log_info("waiting for the target server to reach the consistent state");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "waiting for the target server to reach the consistent state");
conn = connect_database(conninfo, true);
@@ -1707,7 +1843,8 @@ wait_for_end_recovery(const char *conninfo, const struct CreateSubscriberOptions
if (opt->recovery_timeout > 0 && timer >= opt->recovery_timeout)
{
stop_standby_server(subscriber_dir);
- pg_log_error("recovery timed out");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "recovery timed out");
disconnect_database(conn, true);
}
@@ -1721,8 +1858,10 @@ wait_for_end_recovery(const char *conninfo, const struct CreateSubscriberOptions
if (!ready)
pg_fatal("server did not end recovery");
- pg_log_info("target server reached the consistent state");
- pg_log_info_hint("If pg_createsubscriber fails after this point, you must recreate the physical replica before continuing.");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "target server reached the consistent state");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_HINT,
+ "If pg_createsubscriber fails after this point, you must recreate the physical replica before continuing.");
}
/*
@@ -1749,8 +1888,9 @@ create_publication(PGconn *conn, struct LogicalRepInfo *dbinfo)
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain publication information: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain publication information: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1763,8 +1903,10 @@ create_publication(PGconn *conn, struct LogicalRepInfo *dbinfo)
* pg_createsubscriber_ prefix followed by the exact database oid and
* a random number.
*/
- pg_log_error("publication \"%s\" already exists", dbinfo->pubname);
- pg_log_error_hint("Consider renaming this publication before continuing.");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "publication \"%s\" already exists", dbinfo->pubname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Consider renaming this publication before continuing.");
disconnect_database(conn, true);
}
@@ -1772,24 +1914,28 @@ create_publication(PGconn *conn, struct LogicalRepInfo *dbinfo)
resetPQExpBuffer(str);
if (dry_run)
- pg_log_info("dry-run: would create publication \"%s\" in database \"%s\"",
- dbinfo->pubname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would create publication \"%s\" in database \"%s\"",
+ dbinfo->pubname, dbinfo->dbname);
else
- pg_log_info("creating publication \"%s\" in database \"%s\"",
- dbinfo->pubname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "creating publication \"%s\" in database \"%s\"",
+ dbinfo->pubname, dbinfo->dbname);
appendPQExpBuffer(str, "CREATE PUBLICATION %s FOR ALL TABLES",
ipubname_esc);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not create publication \"%s\" in database \"%s\": %s",
- dbinfo->pubname, dbinfo->dbname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not create publication \"%s\" in database \"%s\": %s",
+ dbinfo->pubname, dbinfo->dbname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
PQclear(res);
@@ -1819,25 +1965,29 @@ drop_publication(PGconn *conn, const char *pubname, const char *dbname,
pubname_esc = PQescapeIdentifier(conn, pubname, strlen(pubname));
if (dry_run)
- pg_log_info("dry-run: would drop publication \"%s\" in database \"%s\"",
- pubname, dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would drop publication \"%s\" in database \"%s\"",
+ pubname, dbname);
else
- pg_log_info("dropping publication \"%s\" in database \"%s\"",
- pubname, dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dropping publication \"%s\" in database \"%s\"",
+ pubname, dbname);
appendPQExpBuffer(str, "DROP PUBLICATION %s", pubname_esc);
PQfreemem(pubname_esc);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not drop publication \"%s\" in database \"%s\": %s",
- pubname, dbname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not drop publication \"%s\" in database \"%s\": %s",
+ pubname, dbname, PQresultErrorMessage(res));
*made_publication = false; /* don't try again. */
/*
@@ -1872,15 +2022,17 @@ check_and_drop_publications(PGconn *conn, struct LogicalRepInfo *dbinfo)
if (drop_all_pubs)
{
- pg_log_info("dropping all existing publications in database \"%s\"",
- dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dropping all existing publications in database \"%s\"",
+ dbinfo->dbname);
/* Fetch all publication names */
res = PQexec(conn, "SELECT pubname FROM pg_catalog.pg_publication;");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain publication information: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain publication information: %s",
+ PQresultErrorMessage(res));
PQclear(res);
disconnect_database(conn, true);
}
@@ -1903,11 +2055,13 @@ check_and_drop_publications(PGconn *conn, struct LogicalRepInfo *dbinfo)
else
{
if (dry_run)
- pg_log_info("dry-run: would preserve existing publication \"%s\" in database \"%s\"",
- dbinfo->pubname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would preserve existing publication \"%s\" in database \"%s\"",
+ dbinfo->pubname, dbinfo->dbname);
else
- pg_log_info("preserve existing publication \"%s\" in database \"%s\"",
- dbinfo->pubname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "preserve existing publication \"%s\" in database \"%s\"",
+ dbinfo->pubname, dbinfo->dbname);
}
}
}
@@ -1941,11 +2095,13 @@ create_subscription(PGconn *conn, const struct LogicalRepInfo *dbinfo)
replslotname_esc = PQescapeLiteral(conn, dbinfo->replslotname, strlen(dbinfo->replslotname));
if (dry_run)
- pg_log_info("dry-run: would create subscription \"%s\" in database \"%s\"",
- dbinfo->subname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would create subscription \"%s\" in database \"%s\"",
+ dbinfo->subname, dbinfo->dbname);
else
- pg_log_info("creating subscription \"%s\" in database \"%s\"",
- dbinfo->subname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "creating subscription \"%s\" in database \"%s\"",
+ dbinfo->subname, dbinfo->dbname);
appendPQExpBuffer(str,
"CREATE SUBSCRIPTION %s CONNECTION %s PUBLICATION %s "
@@ -1959,15 +2115,17 @@ create_subscription(PGconn *conn, const struct LogicalRepInfo *dbinfo)
PQfreemem(pubconninfo_esc);
PQfreemem(replslotname_esc);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not create subscription \"%s\" in database \"%s\": %s",
- dbinfo->subname, dbinfo->dbname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not create subscription \"%s\" in database \"%s\": %s",
+ dbinfo->subname, dbinfo->dbname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
PQclear(res);
@@ -2011,15 +2169,17 @@ set_replication_progress(PGconn *conn, const struct LogicalRepInfo *dbinfo, cons
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain subscription OID: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain subscription OID: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
if (PQntuples(res) != 1 && !dry_run)
{
- pg_log_error("could not obtain subscription OID: got %d rows, expected %d row",
- PQntuples(res), 1);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain subscription OID: got %d rows, expected %d row",
+ PQntuples(res), 1);
disconnect_database(conn, true);
}
@@ -2043,26 +2203,30 @@ set_replication_progress(PGconn *conn, const struct LogicalRepInfo *dbinfo, cons
originname = psprintf("pg_%u", suboid);
if (dry_run)
- pg_log_info("dry-run: would set the replication progress (node name \"%s\", LSN %s) in database \"%s\"",
- originname, lsnstr, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would set the replication progress (node name \"%s\", LSN %s) in database \"%s\"",
+ originname, lsnstr, dbinfo->dbname);
else
- pg_log_info("setting the replication progress (node name \"%s\", LSN %s) in database \"%s\"",
- originname, lsnstr, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "setting the replication progress (node name \"%s\", LSN %s) in database \"%s\"",
+ originname, lsnstr, dbinfo->dbname);
resetPQExpBuffer(str);
appendPQExpBuffer(str,
"SELECT pg_catalog.pg_replication_origin_advance('%s', '%s')",
originname, lsnstr);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not set replication progress for subscription \"%s\": %s",
- dbinfo->subname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not set replication progress for subscription \"%s\": %s",
+ dbinfo->subname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
PQclear(res);
@@ -2093,23 +2257,27 @@ enable_subscription(PGconn *conn, const struct LogicalRepInfo *dbinfo)
subname = PQescapeIdentifier(conn, dbinfo->subname, strlen(dbinfo->subname));
if (dry_run)
- pg_log_info("dry-run: would enable subscription \"%s\" in database \"%s\"",
- dbinfo->subname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would enable subscription \"%s\" in database \"%s\"",
+ dbinfo->subname, dbinfo->dbname);
else
- pg_log_info("enabling subscription \"%s\" in database \"%s\"",
- dbinfo->subname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "enabling subscription \"%s\" in database \"%s\"",
+ dbinfo->subname, dbinfo->dbname);
appendPQExpBuffer(str, "ALTER SUBSCRIPTION %s ENABLE", subname);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not enable subscription \"%s\": %s",
- dbinfo->subname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not enable subscription \"%s\": %s",
+ dbinfo->subname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -2154,7 +2322,9 @@ get_publisher_databases(struct CreateSubscriberOptions *opt,
res = PQexec(conn, "SELECT datname FROM pg_database WHERE datistemplate = false AND datallowconn AND datconnlimit <> -2 ORDER BY 1");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain a list of databases: %s", PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain a list of databases: %s",
+ PQresultErrorMessage(res));
PQclear(res);
disconnect_database(conn, true);
}
@@ -2258,9 +2428,11 @@ main(int argc, char **argv)
#ifndef WIN32
if (geteuid() == 0)
{
- pg_log_error("cannot be executed by \"root\"");
- pg_log_error_hint("You must run %s as the PostgreSQL superuser.",
- progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "cannot be executed by \"root\"");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "You must run %s as the PostgreSQL superuser.",
+ progname);
exit(1);
}
#endif
@@ -2351,7 +2523,9 @@ main(int argc, char **argv)
break;
default:
/* getopt_long already emitted a complaint */
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.",
+ progname);
exit(1);
}
}
@@ -2372,9 +2546,12 @@ main(int argc, char **argv)
if (bad_switch)
{
- pg_log_error("options %s and %s cannot be used together",
- bad_switch, "-a/--all");
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "options %s and %s cannot be used together",
+ bad_switch, "-a/--all");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.",
+ progname);
exit(1);
}
}
@@ -2382,17 +2559,21 @@ main(int argc, char **argv)
/* Any non-option arguments? */
if (optind < argc)
{
- pg_log_error("too many command-line arguments (first is \"%s\")",
- argv[optind]);
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "too many command-line arguments (first is \"%s\")",
+ argv[optind]);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.", progname);
exit(1);
}
/* Required arguments */
if (subscriber_dir == NULL)
{
- pg_log_error("no subscriber data directory specified");
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "no subscriber data directory specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.", progname);
exit(1);
}
@@ -2419,22 +2600,27 @@ main(int argc, char **argv)
* identical entries for physical and logical replication. If there is
* not, we would fail anyway.
*/
- pg_log_error("no publisher connection string specified");
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "no publisher connection string specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.", progname);
exit(1);
}
if (dry_run)
- pg_log_info("Executing in dry-run mode.\n"
- "The target directory will not be modified.");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "Executing in dry-run mode.\n"
+ "The target directory will not be modified.");
- pg_log_info("validating publisher connection string");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "validating publisher connection string");
pub_base_conninfo = get_base_conninfo(opt.pub_conninfo_str,
&dbname_conninfo);
if (pub_base_conninfo == NULL)
exit(1);
- pg_log_info("validating subscriber connection string");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "validating subscriber connection string");
sub_base_conninfo = get_sub_conninfo(&opt);
/*
@@ -2451,7 +2637,8 @@ main(int argc, char **argv)
if (opt.database_names.head == NULL)
{
- pg_log_info("no database was specified");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "no database was specified");
/*
* Try to obtain the dbname from the publisher conninfo. If dbname
@@ -2462,14 +2649,17 @@ main(int argc, char **argv)
simple_string_list_append(&opt.database_names, dbname_conninfo);
num_dbs++;
- pg_log_info("database name \"%s\" was extracted from the publisher connection string",
- dbname_conninfo);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "database name \"%s\" was extracted from the publisher connection string",
+ dbname_conninfo);
}
else
{
- pg_log_error("no database name specified");
- pg_log_error_hint("Try \"%s --help\" for more information.",
- progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "no database name specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.",
+ progname);
exit(1);
}
}
@@ -2477,23 +2667,29 @@ main(int argc, char **argv)
/* Number of object names must match number of databases */
if (num_pubs > 0 && num_pubs != num_dbs)
{
- pg_log_error("wrong number of publication names specified");
- pg_log_error_detail("The number of specified publication names (%d) must match the number of specified database names (%d).",
- num_pubs, num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "wrong number of publication names specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "The number of specified publication names (%d) must match the number of specified database names (%d).",
+ num_pubs, num_dbs);
exit(1);
}
if (num_subs > 0 && num_subs != num_dbs)
{
- pg_log_error("wrong number of subscription names specified");
- pg_log_error_detail("The number of specified subscription names (%d) must match the number of specified database names (%d).",
- num_subs, num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "wrong number of subscription names specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "The number of specified subscription names (%d) must match the number of specified database names (%d).",
+ num_subs, num_dbs);
exit(1);
}
if (num_replslots > 0 && num_replslots != num_dbs)
{
- pg_log_error("wrong number of replication slot names specified");
- pg_log_error_detail("The number of specified replication slot names (%d) must match the number of specified database names (%d).",
- num_replslots, num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "wrong number of replication slot names specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "The number of specified replication slot names (%d) must match the number of specified database names (%d).",
+ num_replslots, num_dbs);
exit(1);
}
@@ -2504,9 +2700,11 @@ main(int argc, char **argv)
dbinfos.objecttypes_to_clean |= OBJECTTYPE_PUBLICATIONS;
else
{
- pg_log_error("invalid object type \"%s\" specified for %s",
- cell->val, "--clean");
- pg_log_error_hint("The valid value is: \"%s\"", "publications");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "invalid object type \"%s\" specified for %s",
+ cell->val, "--clean");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "The valid value is: \"%s\"", "publications");
exit(1);
}
}
@@ -2550,8 +2748,10 @@ main(int argc, char **argv)
*/
if (stat(pidfile, &statbuf) == 0)
{
- pg_log_error("standby server is running");
- pg_log_error_hint("Stop the standby server and try again.");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "standby server is running");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Stop the standby server and try again.");
exit(1);
}
@@ -2560,7 +2760,8 @@ main(int argc, char **argv)
* by command-line options). The goal is to avoid connections during the
* transformation steps.
*/
- pg_log_info("starting the standby server with command-line options");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "starting the standby server with command-line options");
start_standby_server(&opt, true, false);
/* Check if the standby server is ready for logical replication */
@@ -2576,7 +2777,8 @@ main(int argc, char **argv)
* guarantees it) *before* creating the replication slots in
* setup_publisher().
*/
- pg_log_info("stopping the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "stopping the subscriber");
stop_standby_server(subscriber_dir);
/* Create the required objects for each database on publisher */
@@ -2590,7 +2792,8 @@ main(int argc, char **argv)
* until accepting connections. We don't want to start logical replication
* during setup.
*/
- pg_log_info("starting the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "starting the subscriber");
start_standby_server(&opt, true, true);
/* Waiting the subscriber to be promoted */
@@ -2611,7 +2814,8 @@ main(int argc, char **argv)
drop_failover_replication_slots(dbinfos.dbinfo);
/* Stop the subscriber */
- pg_log_info("stopping the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "stopping the subscriber");
stop_standby_server(subscriber_dir);
/* Change system identifier from subscriber */
@@ -2619,7 +2823,8 @@ main(int argc, char **argv)
success = true;
- pg_log_info("Done!");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "Done!");
return 0;
}
--
2.47.3
[application/octet-stream] v12-0002-Add-a-new-argument-l-logdir-to-pg_createsubscrib.patch (15.6K, 3-v12-0002-Add-a-new-argument-l-logdir-to-pg_createsubscrib.patch)
download | inline diff:
From 12e518a786f9532e3264976e3fe8abf638a5ffdf Mon Sep 17 00:00:00 2001
From: Gyan Sreejith <[email protected]>
Date: Tue, 17 Mar 2026 19:10:28 -0400
Subject: [PATCH v12 2/3] Add a new argument -l <logdir> to
pg_createsubscriber.
Enabling the option to write messages to log files in the specified directory.
A new directory is created if required. A subdirectory is created with timestamp as its name, and it will contain two new logfiles:
1. pg_createsubscriber_server.log - captures messages related to starting and stopping the standby server.
2. pg_createsubscriber_internal.log - captures internal diagnostic output from pg_createsubscriber.
For example, if we specify -l abc as an argument, and if the timestamp on running it is 20260119T204317.204, a directory abc is created if it doesn't exist already, with 20260119T204317.204 as its subdirectory and it will contain the two log files pg_createsubscriber_server.log and pg_createsubscriber_internal.log
---
doc/src/sgml/ref/pg_createsubscriber.sgml | 28 +++
src/bin/pg_basebackup/pg_createsubscriber.c | 190 +++++++++++++++++-
.../t/040_pg_createsubscriber.pl | 41 +++-
3 files changed, 248 insertions(+), 11 deletions(-)
diff --git a/doc/src/sgml/ref/pg_createsubscriber.sgml b/doc/src/sgml/ref/pg_createsubscriber.sgml
index cf45ff3573d..2898a5ea111 100644
--- a/doc/src/sgml/ref/pg_createsubscriber.sgml
+++ b/doc/src/sgml/ref/pg_createsubscriber.sgml
@@ -136,6 +136,34 @@ PostgreSQL documentation
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>-l <replaceable class="parameter">directory</replaceable></option></term>
+ <term><option>--logdir=<replaceable class="parameter">directory</replaceable></option></term>
+ <listitem>
+ <para>
+ 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.
+ <itemizedlist>
+ <listitem>
+ <para>
+ pg_createsubscriber_server.log which captures logs related to stopping
+ and starting the standby server,
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ pg_createsubscriber_internal.log which captures internal diagnostic
+ output (validations, checks, etc.)
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>-n</option></term>
<term><option>--dry-run</option></term>
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index 0770e163041..bd15b9c1015 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -55,10 +55,14 @@
#define INCLUDED_CONF_FILE "pg_createsubscriber.conf"
#define INCLUDED_CONF_FILE_DISABLED INCLUDED_CONF_FILE ".disabled"
+#define SERVER_LOG_FILE_NAME "pg_createsubscriber_server"
+#define INTERNAL_LOG_FILE_NAME "pg_createsubscriber_internal"
+
/* Command-line options */
struct CreateSubscriberOptions
{
char *config_file; /* configuration file */
+ char *log_dir; /* log directory name */
char *pub_conninfo_str; /* publisher connection string */
char *socket_dir; /* directory for Unix-domain socket, if any */
char *sub_port; /* subscriber port number */
@@ -155,8 +159,14 @@ static void get_publisher_databases(struct CreateSubscriberOptions *opt,
static void pg_createsub_log(enum pg_log_level, enum pg_log_part,
const char *pg_restrict fmt,...)
pg_attribute_printf(3, 4);
+static void pg_createsub_log_v(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt, va_list args)
+ pg_attribute_printf(3, 0);
pg_noreturn static void pg_fatal(const char *pg_restrict fmt,...)
pg_attribute_printf(1, 2);
+static void internal_log_file_write(enum pg_log_level level,
+ const char *pg_restrict fmt, va_list args)
+ pg_attribute_printf(2, 0);
#define WAIT_INTERVAL 1 /* 1 second */
@@ -178,6 +188,10 @@ static pg_prng_state prng_state;
static char *pg_ctl_path = NULL;
static char *pg_resetwal_path = NULL;
+static FILE *internal_log_file_fp = NULL; /* File ptr to log all messages to */
+static char *log_timestamp = NULL; /* Timestamp to be used in all log file
+ * names */
+
/* standby / subscriber data directory */
static char *subscriber_dir = NULL;
@@ -185,6 +199,30 @@ static bool recovery_ended = false;
static bool standby_running = false;
static bool recovery_params_set = false;
+static void
+pg_createsub_log_v(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt, va_list args)
+{
+ if (internal_log_file_fp != NULL)
+ {
+ /*
+ * Output to both stderr and specified log files if the log level is
+ * warning or higher.
+ */
+ if (level > PG_LOG_INFO)
+ {
+ va_list arg_cpy;
+
+ va_copy(arg_cpy, args);
+ pg_log_generic_v(level, part, fmt, arg_cpy);
+ va_end(arg_cpy);
+ }
+ internal_log_file_write(level, fmt, args);
+ }
+ else
+ pg_log_generic_v(level, part, fmt, args);
+}
+
static void
pg_createsub_log(enum pg_log_level level, enum pg_log_part part,
const char *pg_restrict fmt,...)
@@ -193,7 +231,7 @@ pg_createsub_log(enum pg_log_level level, enum pg_log_part part,
va_start(args, fmt);
- pg_log_generic_v(level, part, fmt, args);
+ pg_createsub_log_v(level, part, fmt, args);
va_end(args);
}
@@ -205,10 +243,11 @@ pg_fatal(const char *pg_restrict fmt,...)
va_start(args, fmt);
- pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, args);
+ pg_createsub_log_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, args);
va_end(args);
+
exit(1);
}
@@ -313,6 +352,12 @@ cleanup_objects_atexit(void)
if (standby_running)
stop_standby_server(subscriber_dir);
+
+ if (internal_log_file_fp != NULL)
+ {
+ fclose(internal_log_file_fp);
+ internal_log_file_fp = NULL;
+ }
}
static void
@@ -327,6 +372,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"));
@@ -761,6 +807,7 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
bool crc_ok;
struct timeval tv;
+ char *out_file;
char *cmd_str;
pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
@@ -799,8 +846,13 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
"running pg_resetwal on the subscriber");
- cmd_str = psprintf("\"%s\" -D \"%s\" > \"%s\"", pg_resetwal_path,
- subscriber_dir, DEVNULL);
+ if (opt->log_dir != NULL)
+ out_file = psprintf("%s/%s/%s.log", opt->log_dir, log_timestamp, SERVER_LOG_FILE_NAME);
+ else
+ out_file = DEVNULL;
+
+ cmd_str = psprintf("\"%s\" -D \"%s\" >> \"%s\"", pg_resetwal_path,
+ subscriber_dir, out_file);
pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
"pg_resetwal command is: %s", cmd_str);
@@ -1023,6 +1075,110 @@ server_is_in_recovery(PGconn *conn)
return ret == 0;
}
+static void
+internal_log_file_write(enum pg_log_level level, const char *pg_restrict fmt,
+ va_list args)
+{
+ if (level < __pg_log_level)
+ return;
+
+ if (internal_log_file_fp == NULL)
+ return;
+
+ vfprintf(internal_log_file_fp, fmt, args);
+
+ fprintf(internal_log_file_fp, "\n");
+ fflush(internal_log_file_fp);
+}
+
+/*
+ * Open a new logfile with proper permissions.
+ * From src/backend/postmaster/syslogger.c
+ */
+static FILE *
+logfile_open(const char *filename, const char *mode)
+{
+ FILE *fh;
+ mode_t oumask;
+
+ oumask = umask((mode_t) ((~(S_IRUSR | S_IWUSR)) & (S_IRWXU | S_IRWXG | S_IRWXO)));
+ fh = fopen(filename, mode);
+ umask(oumask);
+
+ if (fh)
+ {
+ setvbuf(fh, NULL, PG_IOLBF, 0);
+
+#ifdef WIN32
+ /* use CRLF line endings on Windows */
+ _setmode(_fileno(fh), _O_TEXT);
+#endif
+ }
+ else
+ pg_fatal("could not open log file \"%s\": %m",
+ filename);
+
+ return fh;
+}
+
+static void
+make_dir(const char *dir)
+{
+ struct stat statbuf;
+
+ 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(const char *log_dir)
+{
+ char timestamp[128];
+ struct timeval tval;
+ time_t now;
+ struct tm tmbuf;
+ 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));
+
+ /* append milliseconds */
+ 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);
+
+ /* Create timestamp directory */
+ make_dir(timestamp_dir);
+}
+
/*
* Is the primary server ready for logical replication?
*
@@ -1781,6 +1937,9 @@ 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/%s/%s.log", opt->log_dir, log_timestamp, SERVER_LOG_FILE_NAME);
+
pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
"pg_ctl command is: %s", pg_ctl_cmd->data);
rc = system(pg_ctl_cmd->data);
@@ -2351,6 +2510,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'},
@@ -2409,6 +2569,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;
@@ -2439,7 +2600,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)
@@ -2460,6 +2621,10 @@ 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);
+ break;
case 'n':
dry_run = true;
break;
@@ -2530,6 +2695,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)
{
@@ -2826,5 +3003,8 @@ main(int argc, char **argv)
pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
"Done!");
+ if (internal_log_file_fp != NULL)
+ fclose(internal_log_file_fp);
+
return 0;
}
diff --git a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
index 0c27fca7bb7..4ddfb621a5d 100644
--- a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
+++ b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
@@ -14,6 +14,7 @@ program_version_ok('pg_createsubscriber');
program_options_handling_ok('pg_createsubscriber');
my $datadir = PostgreSQL::Test::Utils::tempdir;
+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.
@@ -362,9 +363,35 @@ command_ok(
'--subscription' => 'sub2',
'--database' => $db1,
'--database' => $db2,
+ '--logdir' => $logdir,
],
'run pg_createsubscriber --dry-run on node S');
+# Check that the log files were created
+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 $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 $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;
is($node_s->safe_psql('postgres', 'SELECT pg_catalog.pg_is_in_recovery()'),
@@ -444,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);
));
@@ -540,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');
@@ -555,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(
@@ -581,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");
--
2.47.3
[application/octet-stream] v12-0003-Address-comments-from-Hayato-Kuroda.patch (8.5K, 4-v12-0003-Address-comments-from-Hayato-Kuroda.patch)
download | inline diff:
From fcd036e0098d74d661b6dbd4f98bf216d51a3565 Mon Sep 17 00:00:00 2001
From: Hayato Kuroda <[email protected]>
Date: Wed, 18 Mar 2026 21:43:08 +0900
Subject: [PATCH v12 3/3] Address comments from Hayato Kuroda
---
doc/src/sgml/ref/pg_createsubscriber.sgml | 15 +--
src/bin/pg_basebackup/pg_createsubscriber.c | 91 ++++++++-----------
.../t/040_pg_createsubscriber.pl | 2 +-
3 files changed, 46 insertions(+), 62 deletions(-)
diff --git a/doc/src/sgml/ref/pg_createsubscriber.sgml b/doc/src/sgml/ref/pg_createsubscriber.sgml
index 2898a5ea111..ff635ba26cb 100644
--- a/doc/src/sgml/ref/pg_createsubscriber.sgml
+++ b/doc/src/sgml/ref/pg_createsubscriber.sgml
@@ -143,20 +143,21 @@ PostgreSQL documentation
<para>
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.
+ indicating the time at which <application>pg_createsubscriber</application>
+ 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.
<itemizedlist>
<listitem>
<para>
- pg_createsubscriber_server.log which captures logs related to stopping
- and starting the standby server,
+ <literal>pg_createsubscriber_server.log</literal> which captures logs
+ related to stopping and starting the standby server,
</para>
</listitem>
<listitem>
<para>
- pg_createsubscriber_internal.log which captures internal diagnostic
- output (validations, checks, etc.)
+ <literal>pg_createsubscriber_internal.log</literal> which captures
+ internal diagnostic output (validations, checks, etc.)
</para>
</listitem>
</itemizedlist>
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index bd15b9c1015..5a2bc43ce63 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -189,8 +189,7 @@ static char *pg_ctl_path = NULL;
static char *pg_resetwal_path = NULL;
static FILE *internal_log_file_fp = NULL; /* File ptr to log all messages to */
-static char *log_timestamp = NULL; /* Timestamp to be used in all log file
- * names */
+static char logdir[MAXPGPATH]; /* Directory log files are put (if specified) */
/* standby / subscriber data directory */
static char *subscriber_dir = NULL;
@@ -372,7 +371,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(_(" -l, --logdir=LOGDIR location for the 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"));
@@ -846,8 +845,13 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
"running pg_resetwal on the subscriber");
- if (opt->log_dir != NULL)
- out_file = psprintf("%s/%s/%s.log", opt->log_dir, log_timestamp, SERVER_LOG_FILE_NAME);
+ /*
+ * Redirecting the output to the logfile if specified. Since the output
+ * would be very short, around one line, we do not provide a separate file
+ * for it; it's done as a part of the server log.
+ */
+ if (opt->log_dir)
+ out_file = psprintf("%s/%s.log", logdir, SERVER_LOG_FILE_NAME);
else
out_file = DEVNULL;
@@ -1079,13 +1083,13 @@ static void
internal_log_file_write(enum pg_log_level level, const char *pg_restrict fmt,
va_list args)
{
- if (level < __pg_log_level)
- return;
+ Assert(internal_log_file_fp);
- if (internal_log_file_fp == NULL)
+ /* Do nothing if log level is too low. */
+ if (level < __pg_log_level)
return;
- vfprintf(internal_log_file_fp, fmt, args);
+ vfprintf(internal_log_file_fp, _(fmt), args);
fprintf(internal_log_file_fp, "\n");
fflush(internal_log_file_fp);
@@ -1122,33 +1126,12 @@ logfile_open(const char *filename, const char *mode)
}
static void
-make_dir(const char *dir)
-{
- struct stat statbuf;
-
- 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(const char *log_dir)
+make_output_dirs(const char *log_basedir)
{
char timestamp[128];
struct timeval tval;
time_t now;
struct tm tmbuf;
- char timestamp_dir[MAXPGPATH];
int len;
/* Generate timestamp */
@@ -1163,20 +1146,20 @@ make_output_dirs(const char *log_dir)
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);
+ len = snprintf(logdir, MAXPGPATH, "%s/%s", log_basedir, timestamp);
if (len >= MAXPGPATH)
pg_fatal("directory path for log files, %s/%s, is too long",
- log_dir, timestamp);
+ logdir, timestamp);
+
+ /* Create base directory (ignore if exists) */
+ if (mkdir(log_basedir, S_IRWXU) < 0 && errno != EEXIST)
+ pg_fatal("could not create directory \"%s\": %m", log_basedir);
- /* Create timestamp directory */
- make_dir(timestamp_dir);
+ /* Create BASE_DIR/$timestamp */
+ if (mkdir(logdir, S_IRWXU) < 0)
+ pg_fatal("could not create directory \"%s\": %m", logdir);
}
/*
@@ -1937,8 +1920,8 @@ 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/%s/%s.log", opt->log_dir, log_timestamp, SERVER_LOG_FILE_NAME);
+ if (opt->log_dir)
+ appendPQExpBuffer(pg_ctl_cmd, " -l \"%s/%s.log\"", logdir, SERVER_LOG_FILE_NAME);
pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
"pg_ctl command is: %s", pg_ctl_cmd->data);
@@ -2695,18 +2678,6 @@ 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)
{
@@ -2784,6 +2755,18 @@ main(int argc, char **argv)
exit(1);
}
+ if (opt.log_dir != NULL)
+ {
+ char *internal_log_file;
+
+ make_output_dirs(opt.log_dir);
+ internal_log_file = psprintf("%s/%s.log", logdir,
+ 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);
+ }
+
if (dry_run)
pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
"Executing in dry-run mode.\n"
diff --git a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
index 4ddfb621a5d..d3af1561551 100644
--- a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
+++ b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
@@ -14,7 +14,7 @@ program_version_ok('pg_createsubscriber');
program_options_handling_ok('pg_createsubscriber');
my $datadir = PostgreSQL::Test::Utils::tempdir;
-my $logdir = PostgreSQL::Test::Utils::tempdir + "/logdir";
+my $logdir = PostgreSQL::Test::Utils::tempdir;
# Generate a database with a name made of a range of ASCII characters.
# Extracted from 002_pg_upgrade.pl.
--
2.47.3
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
@ 2026-03-19 18:29 ` Gyan Sreejith <[email protected]>
2026-03-20 10:30 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Nisha Moond <[email protected]>
2026-03-20 11:37 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
0 siblings, 2 replies; 55+ messages in thread
From: Gyan Sreejith @ 2026-03-19 18:29 UTC (permalink / raw)
To: Kuroda, Hayato/黒田 隼人 <[email protected]>; +Cc: Amit Kapila <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
Thank you, Kuroda-san, for all the work.
I have made a small change - I added a pg_free() to free internal_log_file.
Regards,
Gyan
On Thu, Mar 19, 2026 at 7:55 AM Kuroda, Hayato/黒田 隼人 <
[email protected]> wrote:
> Hi,
>
> While checking again, I found an issue in the new logging function - same
> va_list
> is used twice, but it was the undefined behavior. va_copy() is used to fix
> the issue.
>
> Also, below points were found and fixed in 0003. Thanks Peter Smith to
> work on.
>
> ```
> + printf(_(" -l, --logdir=LOGDIR location for the new
> log directory\n"));
> ```
>
> But there is a possibility that existing directory is specified, right?
>
> ```
> +my $logdir = PostgreSQL::Test::Utils::tempdir + "/logdir";
> ```
>
> Isn't it enough to just use tempdir here? At least, I think using "+" is
> not
> appropriate, "." is used to connect strings.
>
> ```
> + if (opt->log_dir != NULL)
> + appendPQExpBuffer(pg_ctl_cmd, " -l %s/%s/%s.log",
> opt->log_dir, log_timestamp, SERVER_LOG_FILE_NAME);
> ```
>
> I do not have 100% confident, but do we have to quote the logfile here?
>
> Best regards,
> Hayato Kuroda
> FUJITSU LIMITED
>
>
Attachments:
[application/octet-stream] v13-0003-Address-comments-from-Hayato-Kuroda.patch (8.5K, 3-v13-0003-Address-comments-from-Hayato-Kuroda.patch)
download | inline diff:
From 48ef73fcc4b3e32486e6df21a2038f7edf2e44a7 Mon Sep 17 00:00:00 2001
From: Hayato Kuroda <[email protected]>
Date: Wed, 18 Mar 2026 21:43:08 +0900
Subject: [PATCH v13 3/3] Address comments from Hayato Kuroda
---
doc/src/sgml/ref/pg_createsubscriber.sgml | 15 +--
src/bin/pg_basebackup/pg_createsubscriber.c | 95 ++++++++-----------
.../t/040_pg_createsubscriber.pl | 2 +-
3 files changed, 48 insertions(+), 64 deletions(-)
diff --git a/doc/src/sgml/ref/pg_createsubscriber.sgml b/doc/src/sgml/ref/pg_createsubscriber.sgml
index 2898a5ea111..ff635ba26cb 100644
--- a/doc/src/sgml/ref/pg_createsubscriber.sgml
+++ b/doc/src/sgml/ref/pg_createsubscriber.sgml
@@ -143,20 +143,21 @@ PostgreSQL documentation
<para>
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.
+ indicating the time at which <application>pg_createsubscriber</application>
+ 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.
<itemizedlist>
<listitem>
<para>
- pg_createsubscriber_server.log which captures logs related to stopping
- and starting the standby server,
+ <literal>pg_createsubscriber_server.log</literal> which captures logs
+ related to stopping and starting the standby server,
</para>
</listitem>
<listitem>
<para>
- pg_createsubscriber_internal.log which captures internal diagnostic
- output (validations, checks, etc.)
+ <literal>pg_createsubscriber_internal.log</literal> which captures
+ internal diagnostic output (validations, checks, etc.)
</para>
</listitem>
</itemizedlist>
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index bb537938a7e..690623201f4 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -189,8 +189,7 @@ static char *pg_ctl_path = NULL;
static char *pg_resetwal_path = NULL;
static FILE *internal_log_file_fp = NULL; /* File ptr to log all messages to */
-static char *log_timestamp = NULL; /* Timestamp to be used in all log file
- * names */
+static char logdir[MAXPGPATH]; /* Directory log files are put (if specified) */
/* standby / subscriber data directory */
static char *subscriber_dir = NULL;
@@ -372,7 +371,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(_(" -l, --logdir=LOGDIR location for the 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"));
@@ -846,8 +845,13 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
"running pg_resetwal on the subscriber");
- if (opt->log_dir != NULL)
- out_file = psprintf("%s/%s/%s.log", opt->log_dir, log_timestamp, SERVER_LOG_FILE_NAME);
+ /*
+ * Redirecting the output to the logfile if specified. Since the output
+ * would be very short, around one line, we do not provide a separate file
+ * for it; it's done as a part of the server log.
+ */
+ if (opt->log_dir)
+ out_file = psprintf("%s/%s.log", logdir, SERVER_LOG_FILE_NAME);
else
out_file = DEVNULL;
@@ -1079,13 +1083,13 @@ static void
internal_log_file_write(enum pg_log_level level, const char *pg_restrict fmt,
va_list args)
{
- if (level < __pg_log_level)
- return;
+ Assert(internal_log_file_fp);
- if (internal_log_file_fp == NULL)
+ /* Do nothing if log level is too low. */
+ if (level < __pg_log_level)
return;
- vfprintf(internal_log_file_fp, fmt, args);
+ vfprintf(internal_log_file_fp, _(fmt), args);
fprintf(internal_log_file_fp, "\n");
fflush(internal_log_file_fp);
@@ -1122,33 +1126,12 @@ logfile_open(const char *filename, const char *mode)
}
static void
-make_dir(const char *dir)
-{
- struct stat statbuf;
-
- 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(const char *log_dir)
+make_output_dirs(const char *log_basedir)
{
char timestamp[128];
struct timeval tval;
time_t now;
struct tm tmbuf;
- char timestamp_dir[MAXPGPATH];
int len;
/* Generate timestamp */
@@ -1163,20 +1146,20 @@ make_output_dirs(const char *log_dir)
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);
+ len = snprintf(logdir, MAXPGPATH, "%s/%s", log_basedir, timestamp);
if (len >= MAXPGPATH)
pg_fatal("directory path for log files, %s/%s, is too long",
- log_dir, timestamp);
+ logdir, timestamp);
+
+ /* Create base directory (ignore if exists) */
+ if (mkdir(log_basedir, S_IRWXU) < 0 && errno != EEXIST)
+ pg_fatal("could not create directory \"%s\": %m", log_basedir);
- /* Create timestamp directory */
- make_dir(timestamp_dir);
+ /* Create BASE_DIR/$timestamp */
+ if (mkdir(logdir, S_IRWXU) < 0)
+ pg_fatal("could not create directory \"%s\": %m", logdir);
}
/*
@@ -1937,8 +1920,8 @@ 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/%s/%s.log", opt->log_dir, log_timestamp, SERVER_LOG_FILE_NAME);
+ if (opt->log_dir)
+ appendPQExpBuffer(pg_ctl_cmd, " -l \"%s/%s.log\"", logdir, SERVER_LOG_FILE_NAME);
pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
"pg_ctl command is: %s", pg_ctl_cmd->data);
@@ -2695,20 +2678,6 @@ 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);
-
- pg_free(internal_log_file);
- }
-
/* Validate that --all is not used with incompatible options */
if (opt.all_dbs)
{
@@ -2786,6 +2755,20 @@ main(int argc, char **argv)
exit(1);
}
+ if (opt.log_dir != NULL)
+ {
+ char *internal_log_file;
+
+ make_output_dirs(opt.log_dir);
+ internal_log_file = psprintf("%s/%s.log", logdir,
+ 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);
+
+ pg_free(internal_log_file);
+ }
+
if (dry_run)
pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
"Executing in dry-run mode.\n"
diff --git a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
index 4ddfb621a5d..d3af1561551 100644
--- a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
+++ b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
@@ -14,7 +14,7 @@ program_version_ok('pg_createsubscriber');
program_options_handling_ok('pg_createsubscriber');
my $datadir = PostgreSQL::Test::Utils::tempdir;
-my $logdir = PostgreSQL::Test::Utils::tempdir + "/logdir";
+my $logdir = PostgreSQL::Test::Utils::tempdir;
# Generate a database with a name made of a range of ASCII characters.
# Extracted from 002_pg_upgrade.pl.
--
2.43.0
[application/octet-stream] v13-0002-Add-a-new-argument-l-logdir-to-pg_createsubscrib.patch (15.6K, 4-v13-0002-Add-a-new-argument-l-logdir-to-pg_createsubscrib.patch)
download | inline diff:
From ed16dbe760ce10cd27252ad574e9cedb2a014f9b Mon Sep 17 00:00:00 2001
From: Gyan Sreejith <[email protected]>
Date: Thu, 19 Mar 2026 14:08:07 -0400
Subject: [PATCH v13 2/3] Add a new argument -l <logdir> to
pg_createsubscriber.
Enabling the option to write messages to log files in the specified directory.
A new directory is created if required. A subdirectory is created with timestamp as its name, and it will contain two new logfiles:
1. pg_createsubscriber_server.log - captures messages related to starting and stopping the standby server.
2. pg_createsubscriber_internal.log - captures internal diagnostic output from pg_createsubscriber.
For example, if we specify -l abc as an argument, and if the timestamp on running it is 20260119T204317.204, a directory abc is created if it doesn't exist already, with 20260119T204317.204 as its subdirectory and it will contain the two log files pg_createsubscriber_server.log and pg_createsubscriber_internal.log
---
doc/src/sgml/ref/pg_createsubscriber.sgml | 28 +++
src/bin/pg_basebackup/pg_createsubscriber.c | 192 +++++++++++++++++-
.../t/040_pg_createsubscriber.pl | 41 +++-
3 files changed, 250 insertions(+), 11 deletions(-)
diff --git a/doc/src/sgml/ref/pg_createsubscriber.sgml b/doc/src/sgml/ref/pg_createsubscriber.sgml
index cf45ff3573d..2898a5ea111 100644
--- a/doc/src/sgml/ref/pg_createsubscriber.sgml
+++ b/doc/src/sgml/ref/pg_createsubscriber.sgml
@@ -136,6 +136,34 @@ PostgreSQL documentation
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>-l <replaceable class="parameter">directory</replaceable></option></term>
+ <term><option>--logdir=<replaceable class="parameter">directory</replaceable></option></term>
+ <listitem>
+ <para>
+ 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.
+ <itemizedlist>
+ <listitem>
+ <para>
+ pg_createsubscriber_server.log which captures logs related to stopping
+ and starting the standby server,
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ pg_createsubscriber_internal.log which captures internal diagnostic
+ output (validations, checks, etc.)
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>-n</option></term>
<term><option>--dry-run</option></term>
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index 0770e163041..bb537938a7e 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -55,10 +55,14 @@
#define INCLUDED_CONF_FILE "pg_createsubscriber.conf"
#define INCLUDED_CONF_FILE_DISABLED INCLUDED_CONF_FILE ".disabled"
+#define SERVER_LOG_FILE_NAME "pg_createsubscriber_server"
+#define INTERNAL_LOG_FILE_NAME "pg_createsubscriber_internal"
+
/* Command-line options */
struct CreateSubscriberOptions
{
char *config_file; /* configuration file */
+ char *log_dir; /* log directory name */
char *pub_conninfo_str; /* publisher connection string */
char *socket_dir; /* directory for Unix-domain socket, if any */
char *sub_port; /* subscriber port number */
@@ -155,8 +159,14 @@ static void get_publisher_databases(struct CreateSubscriberOptions *opt,
static void pg_createsub_log(enum pg_log_level, enum pg_log_part,
const char *pg_restrict fmt,...)
pg_attribute_printf(3, 4);
+static void pg_createsub_log_v(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt, va_list args)
+ pg_attribute_printf(3, 0);
pg_noreturn static void pg_fatal(const char *pg_restrict fmt,...)
pg_attribute_printf(1, 2);
+static void internal_log_file_write(enum pg_log_level level,
+ const char *pg_restrict fmt, va_list args)
+ pg_attribute_printf(2, 0);
#define WAIT_INTERVAL 1 /* 1 second */
@@ -178,6 +188,10 @@ static pg_prng_state prng_state;
static char *pg_ctl_path = NULL;
static char *pg_resetwal_path = NULL;
+static FILE *internal_log_file_fp = NULL; /* File ptr to log all messages to */
+static char *log_timestamp = NULL; /* Timestamp to be used in all log file
+ * names */
+
/* standby / subscriber data directory */
static char *subscriber_dir = NULL;
@@ -185,6 +199,30 @@ static bool recovery_ended = false;
static bool standby_running = false;
static bool recovery_params_set = false;
+static void
+pg_createsub_log_v(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt, va_list args)
+{
+ if (internal_log_file_fp != NULL)
+ {
+ /*
+ * Output to both stderr and specified log files if the log level is
+ * warning or higher.
+ */
+ if (level > PG_LOG_INFO)
+ {
+ va_list arg_cpy;
+
+ va_copy(arg_cpy, args);
+ pg_log_generic_v(level, part, fmt, arg_cpy);
+ va_end(arg_cpy);
+ }
+ internal_log_file_write(level, fmt, args);
+ }
+ else
+ pg_log_generic_v(level, part, fmt, args);
+}
+
static void
pg_createsub_log(enum pg_log_level level, enum pg_log_part part,
const char *pg_restrict fmt,...)
@@ -193,7 +231,7 @@ pg_createsub_log(enum pg_log_level level, enum pg_log_part part,
va_start(args, fmt);
- pg_log_generic_v(level, part, fmt, args);
+ pg_createsub_log_v(level, part, fmt, args);
va_end(args);
}
@@ -205,10 +243,11 @@ pg_fatal(const char *pg_restrict fmt,...)
va_start(args, fmt);
- pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, args);
+ pg_createsub_log_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, args);
va_end(args);
+
exit(1);
}
@@ -313,6 +352,12 @@ cleanup_objects_atexit(void)
if (standby_running)
stop_standby_server(subscriber_dir);
+
+ if (internal_log_file_fp != NULL)
+ {
+ fclose(internal_log_file_fp);
+ internal_log_file_fp = NULL;
+ }
}
static void
@@ -327,6 +372,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"));
@@ -761,6 +807,7 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
bool crc_ok;
struct timeval tv;
+ char *out_file;
char *cmd_str;
pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
@@ -799,8 +846,13 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
"running pg_resetwal on the subscriber");
- cmd_str = psprintf("\"%s\" -D \"%s\" > \"%s\"", pg_resetwal_path,
- subscriber_dir, DEVNULL);
+ if (opt->log_dir != NULL)
+ out_file = psprintf("%s/%s/%s.log", opt->log_dir, log_timestamp, SERVER_LOG_FILE_NAME);
+ else
+ out_file = DEVNULL;
+
+ cmd_str = psprintf("\"%s\" -D \"%s\" >> \"%s\"", pg_resetwal_path,
+ subscriber_dir, out_file);
pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
"pg_resetwal command is: %s", cmd_str);
@@ -1023,6 +1075,110 @@ server_is_in_recovery(PGconn *conn)
return ret == 0;
}
+static void
+internal_log_file_write(enum pg_log_level level, const char *pg_restrict fmt,
+ va_list args)
+{
+ if (level < __pg_log_level)
+ return;
+
+ if (internal_log_file_fp == NULL)
+ return;
+
+ vfprintf(internal_log_file_fp, fmt, args);
+
+ fprintf(internal_log_file_fp, "\n");
+ fflush(internal_log_file_fp);
+}
+
+/*
+ * Open a new logfile with proper permissions.
+ * From src/backend/postmaster/syslogger.c
+ */
+static FILE *
+logfile_open(const char *filename, const char *mode)
+{
+ FILE *fh;
+ mode_t oumask;
+
+ oumask = umask((mode_t) ((~(S_IRUSR | S_IWUSR)) & (S_IRWXU | S_IRWXG | S_IRWXO)));
+ fh = fopen(filename, mode);
+ umask(oumask);
+
+ if (fh)
+ {
+ setvbuf(fh, NULL, PG_IOLBF, 0);
+
+#ifdef WIN32
+ /* use CRLF line endings on Windows */
+ _setmode(_fileno(fh), _O_TEXT);
+#endif
+ }
+ else
+ pg_fatal("could not open log file \"%s\": %m",
+ filename);
+
+ return fh;
+}
+
+static void
+make_dir(const char *dir)
+{
+ struct stat statbuf;
+
+ 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(const char *log_dir)
+{
+ char timestamp[128];
+ struct timeval tval;
+ time_t now;
+ struct tm tmbuf;
+ 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));
+
+ /* append milliseconds */
+ 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);
+
+ /* Create timestamp directory */
+ make_dir(timestamp_dir);
+}
+
/*
* Is the primary server ready for logical replication?
*
@@ -1781,6 +1937,9 @@ 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/%s/%s.log", opt->log_dir, log_timestamp, SERVER_LOG_FILE_NAME);
+
pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
"pg_ctl command is: %s", pg_ctl_cmd->data);
rc = system(pg_ctl_cmd->data);
@@ -2351,6 +2510,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'},
@@ -2409,6 +2569,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;
@@ -2439,7 +2600,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)
@@ -2460,6 +2621,10 @@ 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);
+ break;
case 'n':
dry_run = true;
break;
@@ -2530,6 +2695,20 @@ 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);
+
+ pg_free(internal_log_file);
+ }
+
/* Validate that --all is not used with incompatible options */
if (opt.all_dbs)
{
@@ -2826,5 +3005,8 @@ main(int argc, char **argv)
pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
"Done!");
+ if (internal_log_file_fp != NULL)
+ fclose(internal_log_file_fp);
+
return 0;
}
diff --git a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
index 0c27fca7bb7..4ddfb621a5d 100644
--- a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
+++ b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
@@ -14,6 +14,7 @@ program_version_ok('pg_createsubscriber');
program_options_handling_ok('pg_createsubscriber');
my $datadir = PostgreSQL::Test::Utils::tempdir;
+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.
@@ -362,9 +363,35 @@ command_ok(
'--subscription' => 'sub2',
'--database' => $db1,
'--database' => $db2,
+ '--logdir' => $logdir,
],
'run pg_createsubscriber --dry-run on node S');
+# Check that the log files were created
+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 $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 $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;
is($node_s->safe_psql('postgres', 'SELECT pg_catalog.pg_is_in_recovery()'),
@@ -444,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);
));
@@ -540,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');
@@ -555,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(
@@ -581,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");
--
2.43.0
[application/octet-stream] v13-0001-pg_createsubscriber-use-own-reporting-functions.patch (55.1K, 5-v13-0001-pg_createsubscriber-use-own-reporting-functions.patch)
download | inline diff:
From de04c5a1d54e15395d74ae0bcd32eb57a5f4cc88 Mon Sep 17 00:00:00 2001
From: Hayato Kuroda <[email protected]>
Date: Wed, 18 Mar 2026 20:20:50 +0900
Subject: [PATCH v13 1/3] pg_createsubscriber: use own reporting functions
This commit converts all pg_log_xxx families to call a new reporting function
pg_createsub_log(). Also, pg_fatal() is overwritten to use its own.
This commit changes nothing from the outside, but is needed for the upcoming
commit.
---
src/bin/pg_basebackup/pg_createsubscriber.c | 711 +++++++++++++-------
1 file changed, 458 insertions(+), 253 deletions(-)
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index 2bc84505aab..0770e163041 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -31,6 +31,12 @@
#include "fe_utils/version.h"
#include "getopt_long.h"
+/*
+ * For now, pg_createsubscriber does not use common/logging.c; use our own
+ * pg_fatal.
+ */
+#undef pg_fatal
+
#define DEFAULT_SUB_PORT "50432"
#define OBJECTTYPE_PUBLICATIONS 0x0001
@@ -146,6 +152,11 @@ 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 pg_createsub_log(enum pg_log_level, enum pg_log_part,
+ const char *pg_restrict fmt,...)
+ pg_attribute_printf(3, 4);
+pg_noreturn static void pg_fatal(const char *pg_restrict fmt,...)
+ pg_attribute_printf(1, 2);
#define WAIT_INTERVAL 1 /* 1 second */
@@ -174,6 +185,32 @@ static bool recovery_ended = false;
static bool standby_running = false;
static bool recovery_params_set = false;
+static void
+pg_createsub_log(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt,...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+
+ pg_log_generic_v(level, part, fmt, args);
+
+ va_end(args);
+}
+
+static void
+pg_fatal(const char *pg_restrict fmt,...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+
+ pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, args);
+
+ va_end(args);
+
+ exit(1);
+}
/*
* Clean up objects created by pg_createsubscriber.
@@ -205,7 +242,8 @@ cleanup_objects_atexit(void)
if (durable_rename(conf_filename, conf_filename_disabled) != 0)
{
/* durable_rename() has already logged something. */
- pg_log_warning_hint("A manual removal of the recovery parameters may be required.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "A manual removal of the recovery parameters may be required.");
}
}
@@ -219,9 +257,11 @@ cleanup_objects_atexit(void)
*/
if (recovery_ended)
{
- pg_log_warning("failed after the end of recovery");
- pg_log_warning_hint("The target server cannot be used as a physical replica anymore. "
- "You must recreate the physical replica before continuing.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "failed after the end of recovery");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "The target server cannot be used as a physical replica anymore. "
+ "You must recreate the physical replica before continuing.");
}
for (int i = 0; i < num_dbs; i++)
@@ -251,17 +291,21 @@ cleanup_objects_atexit(void)
*/
if (dbinfo->made_publication)
{
- pg_log_warning("publication \"%s\" created in database \"%s\" on primary was left behind",
- dbinfo->pubname,
- dbinfo->dbname);
- pg_log_warning_hint("Drop this publication before trying again.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "publication \"%s\" created in database \"%s\" on primary was left behind",
+ dbinfo->pubname,
+ dbinfo->dbname);
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop this publication before trying again.");
}
if (dbinfo->made_replslot)
{
- pg_log_warning("replication slot \"%s\" created in database \"%s\" on primary was left behind",
- dbinfo->replslotname,
- dbinfo->dbname);
- pg_log_warning_hint("Drop this replication slot soon to avoid retention of WAL files.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "replication slot \"%s\" created in database \"%s\" on primary was left behind",
+ dbinfo->replslotname,
+ dbinfo->dbname);
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop this replication slot soon to avoid retention of WAL files.");
}
}
}
@@ -342,7 +386,8 @@ get_base_conninfo(const char *conninfo, char **dbname)
conn_opts = PQconninfoParse(conninfo, &errmsg);
if (conn_opts == NULL)
{
- pg_log_error("could not parse connection string: %s", errmsg);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not parse connection string: %s", errmsg);
PQfreemem(errmsg);
return NULL;
}
@@ -426,7 +471,8 @@ get_exec_path(const char *argv0, const char *progname)
progname, full_path, "pg_createsubscriber");
}
- pg_log_debug("%s path is: %s", progname, exec_path);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "%s path is: %s", progname, exec_path);
return exec_path;
}
@@ -443,8 +489,9 @@ check_data_directory(const char *datadir)
uint32 major_version;
char *version_str;
- pg_log_info("checking if directory \"%s\" is a cluster data directory",
- datadir);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "checking if directory \"%s\" is a cluster data directory",
+ datadir);
if (stat(datadir, &statbuf) != 0)
{
@@ -462,9 +509,11 @@ check_data_directory(const char *datadir)
major_version = GET_PG_MAJORVERSION_NUM(get_pg_version(datadir, &version_str));
if (major_version != PG_MAJORVERSION_NUM)
{
- pg_log_error("data directory is of wrong version");
- pg_log_error_detail("File \"%s\" contains \"%s\", which is not compatible with this program's version \"%s\".",
- "PG_VERSION", version_str, PG_MAJORVERSION);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "data directory is of wrong version");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "File \"%s\" contains \"%s\", which is not compatible with this program's version \"%s\".",
+ "PG_VERSION", version_str, PG_MAJORVERSION);
exit(1);
}
}
@@ -547,14 +596,16 @@ store_pub_sub_info(const struct CreateSubscriberOptions *opt,
dbinfo[i].subname = NULL;
/* Other fields will be filled later */
- pg_log_debug("publisher(%d): publication: %s ; replication slot: %s ; connection string: %s", i,
- dbinfo[i].pubname ? dbinfo[i].pubname : "(auto)",
- dbinfo[i].replslotname ? dbinfo[i].replslotname : "(auto)",
- dbinfo[i].pubconninfo);
- pg_log_debug("subscriber(%d): subscription: %s ; connection string: %s, two_phase: %s", i,
- dbinfo[i].subname ? dbinfo[i].subname : "(auto)",
- dbinfo[i].subconninfo,
- dbinfos.two_phase ? "true" : "false");
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher(%d): publication: %s ; replication slot: %s ; connection string: %s", i,
+ dbinfo[i].pubname ? dbinfo[i].pubname : "(auto)",
+ dbinfo[i].replslotname ? dbinfo[i].replslotname : "(auto)",
+ dbinfo[i].pubconninfo);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber(%d): subscription: %s ; connection string: %s, two_phase: %s", i,
+ dbinfo[i].subname ? dbinfo[i].subname : "(auto)",
+ dbinfo[i].subconninfo,
+ dbinfos.two_phase ? "true" : "false");
if (num_pubs > 0)
pubcell = pubcell->next;
@@ -582,8 +633,9 @@ connect_database(const char *conninfo, bool exit_on_error)
conn = PQconnectdb(conninfo);
if (PQstatus(conn) != CONNECTION_OK)
{
- pg_log_error("connection to database failed: %s",
- PQerrorMessage(conn));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "connection to database failed: %s",
+ PQerrorMessage(conn));
PQfinish(conn);
if (exit_on_error)
@@ -595,8 +647,9 @@ connect_database(const char *conninfo, bool exit_on_error)
res = PQexec(conn, ALWAYS_SECURE_SEARCH_PATH_SQL);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not clear \"search_path\": %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not clear \"search_path\": %s",
+ PQresultErrorMessage(res));
PQclear(res);
PQfinish(conn);
@@ -635,27 +688,31 @@ get_primary_sysid(const char *conninfo)
PGresult *res;
uint64 sysid;
- pg_log_info("getting system identifier from publisher");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "getting system identifier from publisher");
conn = connect_database(conninfo, true);
res = PQexec(conn, "SELECT system_identifier FROM pg_catalog.pg_control_system()");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not get system identifier: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not get system identifier: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
if (PQntuples(res) != 1)
{
- pg_log_error("could not get system identifier: got %d rows, expected %d row",
- PQntuples(res), 1);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not get system identifier: got %d rows, expected %d row",
+ PQntuples(res), 1);
disconnect_database(conn, true);
}
sysid = strtou64(PQgetvalue(res, 0, 0), NULL, 10);
- pg_log_info("system identifier is %" PRIu64 " on publisher", sysid);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "system identifier is %" PRIu64 " on publisher", sysid);
PQclear(res);
disconnect_database(conn, false);
@@ -675,7 +732,8 @@ get_standby_sysid(const char *datadir)
bool crc_ok;
uint64 sysid;
- pg_log_info("getting system identifier from subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "getting system identifier from subscriber");
cf = get_controlfile(datadir, &crc_ok);
if (!crc_ok)
@@ -683,7 +741,8 @@ get_standby_sysid(const char *datadir)
sysid = cf->system_identifier;
- pg_log_info("system identifier is %" PRIu64 " on subscriber", sysid);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "system identifier is %" PRIu64 " on subscriber", sysid);
pg_free(cf);
@@ -704,7 +763,8 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
char *cmd_str;
- pg_log_info("modifying system identifier of subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "modifying system identifier of subscriber");
cf = get_controlfile(subscriber_dir, &crc_ok);
if (!crc_ok)
@@ -721,31 +781,37 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
cf->system_identifier |= getpid() & 0xFFF;
if (dry_run)
- pg_log_info("dry-run: would set system identifier to %" PRIu64 " on subscriber",
- cf->system_identifier);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would set system identifier to %" PRIu64 " on subscriber",
+ cf->system_identifier);
else
{
update_controlfile(subscriber_dir, cf, true);
- pg_log_info("system identifier is %" PRIu64 " on subscriber",
- cf->system_identifier);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "system identifier is %" PRIu64 " on subscriber",
+ cf->system_identifier);
}
if (dry_run)
- pg_log_info("dry-run: would run pg_resetwal on the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would run pg_resetwal on the subscriber");
else
- pg_log_info("running pg_resetwal on the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "running pg_resetwal on the subscriber");
cmd_str = psprintf("\"%s\" -D \"%s\" > \"%s\"", pg_resetwal_path,
subscriber_dir, DEVNULL);
- pg_log_debug("pg_resetwal command is: %s", cmd_str);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "pg_resetwal command is: %s", cmd_str);
if (!dry_run)
{
int rc = system(cmd_str);
if (rc == 0)
- pg_log_info("successfully reset WAL on the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "successfully reset WAL on the subscriber");
else
pg_fatal("could not reset WAL on subscriber: %s", wait_result_to_str(rc));
}
@@ -771,15 +837,17 @@ generate_object_name(PGconn *conn)
"WHERE datname = pg_catalog.current_database()");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain database OID: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain database OID: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
if (PQntuples(res) != 1)
{
- pg_log_error("could not obtain database OID: got %d rows, expected %d row",
- PQntuples(res), 1);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain database OID: got %d rows, expected %d row",
+ PQntuples(res), 1);
disconnect_database(conn, true);
}
@@ -819,8 +887,9 @@ find_publication(PGconn *conn, const char *pubname, const char *dbname)
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not find publication \"%s\" in database \"%s\": %s",
- pubname, dbname, PQerrorMessage(conn));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not find publication \"%s\" in database \"%s\": %s",
+ pubname, dbname, PQerrorMessage(conn));
disconnect_database(conn, true);
}
@@ -873,8 +942,9 @@ setup_publisher(struct LogicalRepInfo *dbinfo)
if (find_publication(conn, dbinfo[i].pubname, dbinfo[i].dbname))
{
/* Reuse existing publication on publisher. */
- pg_log_info("use existing publication \"%s\" in database \"%s\"",
- dbinfo[i].pubname, dbinfo[i].dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "use existing publication \"%s\" in database \"%s\"",
+ dbinfo[i].pubname, dbinfo[i].dbname);
/* Don't remove pre-existing publication if an error occurs. */
dbinfo[i].made_publication = false;
}
@@ -912,8 +982,9 @@ setup_publisher(struct LogicalRepInfo *dbinfo)
res = PQexec(conn, "SELECT pg_log_standby_snapshot()");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not write an additional WAL record: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not write an additional WAL record: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
PQclear(res);
@@ -938,8 +1009,9 @@ server_is_in_recovery(PGconn *conn)
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain recovery progress: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain recovery progress: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -971,7 +1043,8 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
int max_prepared_transactions;
char *max_slot_wal_keep_size;
- pg_log_info("checking settings on publisher");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "checking settings on publisher");
conn = connect_database(dbinfo[0].pubconninfo, true);
@@ -981,7 +1054,8 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
*/
if (server_is_in_recovery(conn))
{
- pg_log_error("primary server cannot be in recovery");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "primary server cannot be in recovery");
disconnect_database(conn, true);
}
@@ -1007,8 +1081,9 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain publisher settings: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain publisher settings: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1022,48 +1097,63 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
PQclear(res);
- pg_log_debug("publisher: wal_level: %s", wal_level);
- pg_log_debug("publisher: max_replication_slots: %d", max_repslots);
- pg_log_debug("publisher: current replication slots: %d", cur_repslots);
- pg_log_debug("publisher: max_wal_senders: %d", max_walsenders);
- pg_log_debug("publisher: current wal senders: %d", cur_walsenders);
- pg_log_debug("publisher: max_prepared_transactions: %d",
- max_prepared_transactions);
- pg_log_debug("publisher: max_slot_wal_keep_size: %s",
- max_slot_wal_keep_size);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: wal_level: %s", wal_level);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: max_replication_slots: %d", max_repslots);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: current replication slots: %d", cur_repslots);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: max_wal_senders: %d", max_walsenders);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: current wal senders: %d", cur_walsenders);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: max_prepared_transactions: %d",
+ max_prepared_transactions);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: max_slot_wal_keep_size: %s",
+ max_slot_wal_keep_size);
disconnect_database(conn, false);
if (strcmp(wal_level, "minimal") == 0)
{
- pg_log_error("publisher requires \"wal_level\" >= \"replica\"");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "publisher requires \"wal_level\" >= \"replica\"");
failed = true;
}
if (max_repslots - cur_repslots < num_dbs)
{
- pg_log_error("publisher requires %d replication slots, but only %d remain",
- num_dbs, max_repslots - cur_repslots);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_replication_slots", cur_repslots + num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "publisher requires %d replication slots, but only %d remain",
+ num_dbs, max_repslots - cur_repslots);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_replication_slots", cur_repslots + num_dbs);
failed = true;
}
if (max_walsenders - cur_walsenders < num_dbs)
{
- pg_log_error("publisher requires %d WAL sender processes, but only %d remain",
- num_dbs, max_walsenders - cur_walsenders);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_wal_senders", cur_walsenders + num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "publisher requires %d WAL sender processes, but only %d remain",
+ num_dbs, max_walsenders - cur_walsenders);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_wal_senders", cur_walsenders + num_dbs);
failed = true;
}
if (max_prepared_transactions != 0 && !dbinfos.two_phase)
{
- pg_log_warning("two_phase option will not be enabled for replication slots");
- pg_log_warning_detail("Subscriptions will be created with the two_phase option disabled. "
- "Prepared transactions will be replicated at COMMIT PREPARED.");
- pg_log_warning_hint("You can use the command-line option --enable-two-phase to enable two_phase.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "two_phase option will not be enabled for replication slots");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_DETAIL,
+ "Subscriptions will be created with the two_phase option disabled. "
+ "Prepared transactions will be replicated at COMMIT PREPARED.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "You can use the command-line option --enable-two-phase to enable two_phase.");
}
/*
@@ -1073,9 +1163,11 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
*/
if (dry_run && (strcmp(max_slot_wal_keep_size, "-1") != 0))
{
- pg_log_warning("required WAL could be removed from the publisher");
- pg_log_warning_hint("Set the configuration parameter \"%s\" to -1 to ensure that required WAL files are not prematurely removed.",
- "max_slot_wal_keep_size");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "required WAL could be removed from the publisher");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Set the configuration parameter \"%s\" to -1 to ensure that required WAL files are not prematurely removed.",
+ "max_slot_wal_keep_size");
}
pg_free(wal_level);
@@ -1106,14 +1198,16 @@ check_subscriber(const struct LogicalRepInfo *dbinfo)
int max_replorigins;
int max_wprocs;
- pg_log_info("checking settings on subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "checking settings on subscriber");
conn = connect_database(dbinfo[0].subconninfo, true);
/* The target server must be a standby */
if (!server_is_in_recovery(conn))
{
- pg_log_error("target server must be a standby");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "target server must be a standby");
disconnect_database(conn, true);
}
@@ -1137,8 +1231,9 @@ check_subscriber(const struct LogicalRepInfo *dbinfo)
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain subscriber settings: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain subscriber settings: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1148,12 +1243,16 @@ check_subscriber(const struct LogicalRepInfo *dbinfo)
if (strcmp(PQgetvalue(res, 3, 0), "") != 0)
primary_slot_name = pg_strdup(PQgetvalue(res, 3, 0));
- pg_log_debug("subscriber: max_logical_replication_workers: %d",
- max_lrworkers);
- pg_log_debug("subscriber: max_active_replication_origins: %d", max_replorigins);
- pg_log_debug("subscriber: max_worker_processes: %d", max_wprocs);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber: max_logical_replication_workers: %d",
+ max_lrworkers);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber: max_active_replication_origins: %d", max_replorigins);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber: max_worker_processes: %d", max_wprocs);
if (primary_slot_name)
- pg_log_debug("subscriber: primary_slot_name: %s", primary_slot_name);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber: primary_slot_name: %s", primary_slot_name);
PQclear(res);
@@ -1161,28 +1260,34 @@ check_subscriber(const struct LogicalRepInfo *dbinfo)
if (max_replorigins < num_dbs)
{
- pg_log_error("subscriber requires %d active replication origins, but only %d remain",
- num_dbs, max_replorigins);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_active_replication_origins", num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "subscriber requires %d active replication origins, but only %d remain",
+ num_dbs, max_replorigins);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_active_replication_origins", num_dbs);
failed = true;
}
if (max_lrworkers < num_dbs)
{
- pg_log_error("subscriber requires %d logical replication workers, but only %d remain",
- num_dbs, max_lrworkers);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_logical_replication_workers", num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "subscriber requires %d logical replication workers, but only %d remain",
+ num_dbs, max_lrworkers);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_logical_replication_workers", num_dbs);
failed = true;
}
if (max_wprocs < num_dbs + 1)
{
- pg_log_error("subscriber requires %d worker processes, but only %d remain",
- num_dbs + 1, max_wprocs);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_worker_processes", num_dbs + 1);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "subscriber requires %d worker processes, but only %d remain",
+ num_dbs + 1, max_wprocs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_worker_processes", num_dbs + 1);
failed = true;
}
@@ -1215,19 +1320,22 @@ drop_existing_subscription(PGconn *conn, const char *subname, const char *dbname
appendPQExpBuffer(query, " DROP SUBSCRIPTION %s;", subname);
if (dry_run)
- pg_log_info("dry-run: would drop subscription \"%s\" in database \"%s\"",
- subname, dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would drop subscription \"%s\" in database \"%s\"",
+ subname, dbname);
else
{
- pg_log_info("dropping subscription \"%s\" in database \"%s\"",
- subname, dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dropping subscription \"%s\" in database \"%s\"",
+ subname, dbname);
res = PQexec(conn, query->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not drop subscription \"%s\": %s",
- subname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not drop subscription \"%s\": %s",
+ subname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1261,8 +1369,9 @@ check_and_drop_existing_subscriptions(PGconn *conn,
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain pre-existing subscriptions: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain pre-existing subscriptions: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1373,7 +1482,8 @@ setup_recovery(const struct LogicalRepInfo *dbinfo, const char *datadir, const c
lsn);
}
- pg_log_debug("recovery parameters:\n%s", recoveryconfcontents->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "recovery parameters:\n%s", recoveryconfcontents->data);
if (!dry_run)
{
@@ -1427,9 +1537,11 @@ drop_primary_replication_slot(struct LogicalRepInfo *dbinfo, const char *slotnam
}
else
{
- pg_log_warning("could not drop replication slot \"%s\" on primary",
- slotname);
- pg_log_warning_hint("Drop this replication slot soon to avoid retention of WAL files.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "could not drop replication slot \"%s\" on primary",
+ slotname);
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop this replication slot soon to avoid retention of WAL files.");
}
}
@@ -1461,9 +1573,11 @@ drop_failover_replication_slots(struct LogicalRepInfo *dbinfo)
}
else
{
- pg_log_warning("could not obtain failover replication slot information: %s",
- PQresultErrorMessage(res));
- pg_log_warning_hint("Drop the failover replication slots on subscriber soon to avoid retention of WAL files.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "could not obtain failover replication slot information: %s",
+ PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop the failover replication slots on subscriber soon to avoid retention of WAL files.");
}
PQclear(res);
@@ -1471,8 +1585,10 @@ drop_failover_replication_slots(struct LogicalRepInfo *dbinfo)
}
else
{
- pg_log_warning("could not drop failover replication slot");
- pg_log_warning_hint("Drop the failover replication slots on subscriber soon to avoid retention of WAL files.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "could not drop failover replication slot");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop the failover replication slots on subscriber soon to avoid retention of WAL files.");
}
}
@@ -1494,11 +1610,13 @@ create_logical_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo)
Assert(conn != NULL);
if (dry_run)
- pg_log_info("dry-run: would create the replication slot \"%s\" in database \"%s\" on publisher",
- slot_name, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would create the replication slot \"%s\" in database \"%s\" on publisher",
+ slot_name, dbinfo->dbname);
else
- pg_log_info("creating the replication slot \"%s\" in database \"%s\" on publisher",
- slot_name, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "creating the replication slot \"%s\" in database \"%s\" on publisher",
+ slot_name, dbinfo->dbname);
slot_name_esc = PQescapeLiteral(conn, slot_name, strlen(slot_name));
@@ -1509,16 +1627,18 @@ create_logical_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo)
PQfreemem(slot_name_esc);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not create replication slot \"%s\" in database \"%s\": %s",
- slot_name, dbinfo->dbname,
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not create replication slot \"%s\" in database \"%s\": %s",
+ slot_name, dbinfo->dbname,
+ PQresultErrorMessage(res));
PQclear(res);
destroyPQExpBuffer(str);
return NULL;
@@ -1547,11 +1667,13 @@ drop_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo,
Assert(conn != NULL);
if (dry_run)
- pg_log_info("dry-run: would drop the replication slot \"%s\" in database \"%s\"",
- slot_name, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would drop the replication slot \"%s\" in database \"%s\"",
+ slot_name, dbinfo->dbname);
else
- pg_log_info("dropping the replication slot \"%s\" in database \"%s\"",
- slot_name, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dropping the replication slot \"%s\" in database \"%s\"",
+ slot_name, dbinfo->dbname);
slot_name_esc = PQescapeLiteral(conn, slot_name, strlen(slot_name));
@@ -1559,15 +1681,17 @@ drop_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo,
PQfreemem(slot_name_esc);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not drop replication slot \"%s\" in database \"%s\": %s",
- slot_name, dbinfo->dbname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not drop replication slot \"%s\" in database \"%s\": %s",
+ slot_name, dbinfo->dbname, PQresultErrorMessage(res));
dbinfo->made_replslot = false; /* don't try again. */
}
@@ -1587,25 +1711,32 @@ pg_ctl_status(const char *pg_ctl_cmd, int rc)
{
if (WIFEXITED(rc))
{
- pg_log_error("pg_ctl failed with exit code %d", WEXITSTATUS(rc));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "pg_ctl failed with exit code %d",
+ WEXITSTATUS(rc));
}
else if (WIFSIGNALED(rc))
{
#if defined(WIN32)
- pg_log_error("pg_ctl was terminated by exception 0x%X",
- WTERMSIG(rc));
- pg_log_error_detail("See C include file \"ntstatus.h\" for a description of the hexadecimal value.");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "pg_ctl was terminated by exception 0x%X",
+ WTERMSIG(rc));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "See C include file \"ntstatus.h\" for a description of the hexadecimal value.");
#else
- pg_log_error("pg_ctl was terminated by signal %d: %s",
- WTERMSIG(rc), pg_strsignal(WTERMSIG(rc)));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "pg_ctl was terminated by signal %d: %s",
+ WTERMSIG(rc), pg_strsignal(WTERMSIG(rc)));
#endif
}
else
{
- pg_log_error("pg_ctl exited with unrecognized status %d", rc);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "pg_ctl exited with unrecognized status %d", rc);
}
- pg_log_error_detail("The failed command was: %s", pg_ctl_cmd);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "The failed command was: %s", pg_ctl_cmd);
exit(1);
}
}
@@ -1650,12 +1781,14 @@ start_standby_server(const struct CreateSubscriberOptions *opt, bool restricted_
if (restrict_logical_worker)
appendPQExpBufferStr(pg_ctl_cmd, " -o \"-c max_logical_replication_workers=0\"");
- pg_log_debug("pg_ctl command is: %s", pg_ctl_cmd->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "pg_ctl command is: %s", pg_ctl_cmd->data);
rc = system(pg_ctl_cmd->data);
pg_ctl_status(pg_ctl_cmd->data, rc);
standby_running = true;
destroyPQExpBuffer(pg_ctl_cmd);
- pg_log_info("server was started");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "server was started");
}
static void
@@ -1666,11 +1799,13 @@ stop_standby_server(const char *datadir)
pg_ctl_cmd = psprintf("\"%s\" stop -D \"%s\" -s", pg_ctl_path,
datadir);
- pg_log_debug("pg_ctl command is: %s", pg_ctl_cmd);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "pg_ctl command is: %s", pg_ctl_cmd);
rc = system(pg_ctl_cmd);
pg_ctl_status(pg_ctl_cmd, rc);
standby_running = false;
- pg_log_info("server was stopped");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "server was stopped");
}
/*
@@ -1689,7 +1824,8 @@ wait_for_end_recovery(const char *conninfo, const struct CreateSubscriberOptions
bool ready = false;
int timer = 0;
- pg_log_info("waiting for the target server to reach the consistent state");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "waiting for the target server to reach the consistent state");
conn = connect_database(conninfo, true);
@@ -1707,7 +1843,8 @@ wait_for_end_recovery(const char *conninfo, const struct CreateSubscriberOptions
if (opt->recovery_timeout > 0 && timer >= opt->recovery_timeout)
{
stop_standby_server(subscriber_dir);
- pg_log_error("recovery timed out");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "recovery timed out");
disconnect_database(conn, true);
}
@@ -1721,8 +1858,10 @@ wait_for_end_recovery(const char *conninfo, const struct CreateSubscriberOptions
if (!ready)
pg_fatal("server did not end recovery");
- pg_log_info("target server reached the consistent state");
- pg_log_info_hint("If pg_createsubscriber fails after this point, you must recreate the physical replica before continuing.");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "target server reached the consistent state");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_HINT,
+ "If pg_createsubscriber fails after this point, you must recreate the physical replica before continuing.");
}
/*
@@ -1749,8 +1888,9 @@ create_publication(PGconn *conn, struct LogicalRepInfo *dbinfo)
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain publication information: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain publication information: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1763,8 +1903,10 @@ create_publication(PGconn *conn, struct LogicalRepInfo *dbinfo)
* pg_createsubscriber_ prefix followed by the exact database oid and
* a random number.
*/
- pg_log_error("publication \"%s\" already exists", dbinfo->pubname);
- pg_log_error_hint("Consider renaming this publication before continuing.");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "publication \"%s\" already exists", dbinfo->pubname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Consider renaming this publication before continuing.");
disconnect_database(conn, true);
}
@@ -1772,24 +1914,28 @@ create_publication(PGconn *conn, struct LogicalRepInfo *dbinfo)
resetPQExpBuffer(str);
if (dry_run)
- pg_log_info("dry-run: would create publication \"%s\" in database \"%s\"",
- dbinfo->pubname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would create publication \"%s\" in database \"%s\"",
+ dbinfo->pubname, dbinfo->dbname);
else
- pg_log_info("creating publication \"%s\" in database \"%s\"",
- dbinfo->pubname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "creating publication \"%s\" in database \"%s\"",
+ dbinfo->pubname, dbinfo->dbname);
appendPQExpBuffer(str, "CREATE PUBLICATION %s FOR ALL TABLES",
ipubname_esc);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not create publication \"%s\" in database \"%s\": %s",
- dbinfo->pubname, dbinfo->dbname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not create publication \"%s\" in database \"%s\": %s",
+ dbinfo->pubname, dbinfo->dbname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
PQclear(res);
@@ -1819,25 +1965,29 @@ drop_publication(PGconn *conn, const char *pubname, const char *dbname,
pubname_esc = PQescapeIdentifier(conn, pubname, strlen(pubname));
if (dry_run)
- pg_log_info("dry-run: would drop publication \"%s\" in database \"%s\"",
- pubname, dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would drop publication \"%s\" in database \"%s\"",
+ pubname, dbname);
else
- pg_log_info("dropping publication \"%s\" in database \"%s\"",
- pubname, dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dropping publication \"%s\" in database \"%s\"",
+ pubname, dbname);
appendPQExpBuffer(str, "DROP PUBLICATION %s", pubname_esc);
PQfreemem(pubname_esc);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not drop publication \"%s\" in database \"%s\": %s",
- pubname, dbname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not drop publication \"%s\" in database \"%s\": %s",
+ pubname, dbname, PQresultErrorMessage(res));
*made_publication = false; /* don't try again. */
/*
@@ -1872,15 +2022,17 @@ check_and_drop_publications(PGconn *conn, struct LogicalRepInfo *dbinfo)
if (drop_all_pubs)
{
- pg_log_info("dropping all existing publications in database \"%s\"",
- dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dropping all existing publications in database \"%s\"",
+ dbinfo->dbname);
/* Fetch all publication names */
res = PQexec(conn, "SELECT pubname FROM pg_catalog.pg_publication;");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain publication information: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain publication information: %s",
+ PQresultErrorMessage(res));
PQclear(res);
disconnect_database(conn, true);
}
@@ -1903,11 +2055,13 @@ check_and_drop_publications(PGconn *conn, struct LogicalRepInfo *dbinfo)
else
{
if (dry_run)
- pg_log_info("dry-run: would preserve existing publication \"%s\" in database \"%s\"",
- dbinfo->pubname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would preserve existing publication \"%s\" in database \"%s\"",
+ dbinfo->pubname, dbinfo->dbname);
else
- pg_log_info("preserve existing publication \"%s\" in database \"%s\"",
- dbinfo->pubname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "preserve existing publication \"%s\" in database \"%s\"",
+ dbinfo->pubname, dbinfo->dbname);
}
}
}
@@ -1941,11 +2095,13 @@ create_subscription(PGconn *conn, const struct LogicalRepInfo *dbinfo)
replslotname_esc = PQescapeLiteral(conn, dbinfo->replslotname, strlen(dbinfo->replslotname));
if (dry_run)
- pg_log_info("dry-run: would create subscription \"%s\" in database \"%s\"",
- dbinfo->subname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would create subscription \"%s\" in database \"%s\"",
+ dbinfo->subname, dbinfo->dbname);
else
- pg_log_info("creating subscription \"%s\" in database \"%s\"",
- dbinfo->subname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "creating subscription \"%s\" in database \"%s\"",
+ dbinfo->subname, dbinfo->dbname);
appendPQExpBuffer(str,
"CREATE SUBSCRIPTION %s CONNECTION %s PUBLICATION %s "
@@ -1959,15 +2115,17 @@ create_subscription(PGconn *conn, const struct LogicalRepInfo *dbinfo)
PQfreemem(pubconninfo_esc);
PQfreemem(replslotname_esc);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not create subscription \"%s\" in database \"%s\": %s",
- dbinfo->subname, dbinfo->dbname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not create subscription \"%s\" in database \"%s\": %s",
+ dbinfo->subname, dbinfo->dbname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
PQclear(res);
@@ -2011,15 +2169,17 @@ set_replication_progress(PGconn *conn, const struct LogicalRepInfo *dbinfo, cons
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain subscription OID: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain subscription OID: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
if (PQntuples(res) != 1 && !dry_run)
{
- pg_log_error("could not obtain subscription OID: got %d rows, expected %d row",
- PQntuples(res), 1);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain subscription OID: got %d rows, expected %d row",
+ PQntuples(res), 1);
disconnect_database(conn, true);
}
@@ -2043,26 +2203,30 @@ set_replication_progress(PGconn *conn, const struct LogicalRepInfo *dbinfo, cons
originname = psprintf("pg_%u", suboid);
if (dry_run)
- pg_log_info("dry-run: would set the replication progress (node name \"%s\", LSN %s) in database \"%s\"",
- originname, lsnstr, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would set the replication progress (node name \"%s\", LSN %s) in database \"%s\"",
+ originname, lsnstr, dbinfo->dbname);
else
- pg_log_info("setting the replication progress (node name \"%s\", LSN %s) in database \"%s\"",
- originname, lsnstr, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "setting the replication progress (node name \"%s\", LSN %s) in database \"%s\"",
+ originname, lsnstr, dbinfo->dbname);
resetPQExpBuffer(str);
appendPQExpBuffer(str,
"SELECT pg_catalog.pg_replication_origin_advance('%s', '%s')",
originname, lsnstr);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not set replication progress for subscription \"%s\": %s",
- dbinfo->subname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not set replication progress for subscription \"%s\": %s",
+ dbinfo->subname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
PQclear(res);
@@ -2093,23 +2257,27 @@ enable_subscription(PGconn *conn, const struct LogicalRepInfo *dbinfo)
subname = PQescapeIdentifier(conn, dbinfo->subname, strlen(dbinfo->subname));
if (dry_run)
- pg_log_info("dry-run: would enable subscription \"%s\" in database \"%s\"",
- dbinfo->subname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would enable subscription \"%s\" in database \"%s\"",
+ dbinfo->subname, dbinfo->dbname);
else
- pg_log_info("enabling subscription \"%s\" in database \"%s\"",
- dbinfo->subname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "enabling subscription \"%s\" in database \"%s\"",
+ dbinfo->subname, dbinfo->dbname);
appendPQExpBuffer(str, "ALTER SUBSCRIPTION %s ENABLE", subname);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not enable subscription \"%s\": %s",
- dbinfo->subname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not enable subscription \"%s\": %s",
+ dbinfo->subname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -2154,7 +2322,9 @@ get_publisher_databases(struct CreateSubscriberOptions *opt,
res = PQexec(conn, "SELECT datname FROM pg_database WHERE datistemplate = false AND datallowconn AND datconnlimit <> -2 ORDER BY 1");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain a list of databases: %s", PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain a list of databases: %s",
+ PQresultErrorMessage(res));
PQclear(res);
disconnect_database(conn, true);
}
@@ -2258,9 +2428,11 @@ main(int argc, char **argv)
#ifndef WIN32
if (geteuid() == 0)
{
- pg_log_error("cannot be executed by \"root\"");
- pg_log_error_hint("You must run %s as the PostgreSQL superuser.",
- progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "cannot be executed by \"root\"");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "You must run %s as the PostgreSQL superuser.",
+ progname);
exit(1);
}
#endif
@@ -2351,7 +2523,9 @@ main(int argc, char **argv)
break;
default:
/* getopt_long already emitted a complaint */
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.",
+ progname);
exit(1);
}
}
@@ -2372,9 +2546,12 @@ main(int argc, char **argv)
if (bad_switch)
{
- pg_log_error("options %s and %s cannot be used together",
- bad_switch, "-a/--all");
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "options %s and %s cannot be used together",
+ bad_switch, "-a/--all");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.",
+ progname);
exit(1);
}
}
@@ -2382,17 +2559,21 @@ main(int argc, char **argv)
/* Any non-option arguments? */
if (optind < argc)
{
- pg_log_error("too many command-line arguments (first is \"%s\")",
- argv[optind]);
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "too many command-line arguments (first is \"%s\")",
+ argv[optind]);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.", progname);
exit(1);
}
/* Required arguments */
if (subscriber_dir == NULL)
{
- pg_log_error("no subscriber data directory specified");
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "no subscriber data directory specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.", progname);
exit(1);
}
@@ -2419,22 +2600,27 @@ main(int argc, char **argv)
* identical entries for physical and logical replication. If there is
* not, we would fail anyway.
*/
- pg_log_error("no publisher connection string specified");
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "no publisher connection string specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.", progname);
exit(1);
}
if (dry_run)
- pg_log_info("Executing in dry-run mode.\n"
- "The target directory will not be modified.");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "Executing in dry-run mode.\n"
+ "The target directory will not be modified.");
- pg_log_info("validating publisher connection string");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "validating publisher connection string");
pub_base_conninfo = get_base_conninfo(opt.pub_conninfo_str,
&dbname_conninfo);
if (pub_base_conninfo == NULL)
exit(1);
- pg_log_info("validating subscriber connection string");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "validating subscriber connection string");
sub_base_conninfo = get_sub_conninfo(&opt);
/*
@@ -2451,7 +2637,8 @@ main(int argc, char **argv)
if (opt.database_names.head == NULL)
{
- pg_log_info("no database was specified");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "no database was specified");
/*
* Try to obtain the dbname from the publisher conninfo. If dbname
@@ -2462,14 +2649,17 @@ main(int argc, char **argv)
simple_string_list_append(&opt.database_names, dbname_conninfo);
num_dbs++;
- pg_log_info("database name \"%s\" was extracted from the publisher connection string",
- dbname_conninfo);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "database name \"%s\" was extracted from the publisher connection string",
+ dbname_conninfo);
}
else
{
- pg_log_error("no database name specified");
- pg_log_error_hint("Try \"%s --help\" for more information.",
- progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "no database name specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.",
+ progname);
exit(1);
}
}
@@ -2477,23 +2667,29 @@ main(int argc, char **argv)
/* Number of object names must match number of databases */
if (num_pubs > 0 && num_pubs != num_dbs)
{
- pg_log_error("wrong number of publication names specified");
- pg_log_error_detail("The number of specified publication names (%d) must match the number of specified database names (%d).",
- num_pubs, num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "wrong number of publication names specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "The number of specified publication names (%d) must match the number of specified database names (%d).",
+ num_pubs, num_dbs);
exit(1);
}
if (num_subs > 0 && num_subs != num_dbs)
{
- pg_log_error("wrong number of subscription names specified");
- pg_log_error_detail("The number of specified subscription names (%d) must match the number of specified database names (%d).",
- num_subs, num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "wrong number of subscription names specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "The number of specified subscription names (%d) must match the number of specified database names (%d).",
+ num_subs, num_dbs);
exit(1);
}
if (num_replslots > 0 && num_replslots != num_dbs)
{
- pg_log_error("wrong number of replication slot names specified");
- pg_log_error_detail("The number of specified replication slot names (%d) must match the number of specified database names (%d).",
- num_replslots, num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "wrong number of replication slot names specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "The number of specified replication slot names (%d) must match the number of specified database names (%d).",
+ num_replslots, num_dbs);
exit(1);
}
@@ -2504,9 +2700,11 @@ main(int argc, char **argv)
dbinfos.objecttypes_to_clean |= OBJECTTYPE_PUBLICATIONS;
else
{
- pg_log_error("invalid object type \"%s\" specified for %s",
- cell->val, "--clean");
- pg_log_error_hint("The valid value is: \"%s\"", "publications");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "invalid object type \"%s\" specified for %s",
+ cell->val, "--clean");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "The valid value is: \"%s\"", "publications");
exit(1);
}
}
@@ -2550,8 +2748,10 @@ main(int argc, char **argv)
*/
if (stat(pidfile, &statbuf) == 0)
{
- pg_log_error("standby server is running");
- pg_log_error_hint("Stop the standby server and try again.");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "standby server is running");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Stop the standby server and try again.");
exit(1);
}
@@ -2560,7 +2760,8 @@ main(int argc, char **argv)
* by command-line options). The goal is to avoid connections during the
* transformation steps.
*/
- pg_log_info("starting the standby server with command-line options");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "starting the standby server with command-line options");
start_standby_server(&opt, true, false);
/* Check if the standby server is ready for logical replication */
@@ -2576,7 +2777,8 @@ main(int argc, char **argv)
* guarantees it) *before* creating the replication slots in
* setup_publisher().
*/
- pg_log_info("stopping the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "stopping the subscriber");
stop_standby_server(subscriber_dir);
/* Create the required objects for each database on publisher */
@@ -2590,7 +2792,8 @@ main(int argc, char **argv)
* until accepting connections. We don't want to start logical replication
* during setup.
*/
- pg_log_info("starting the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "starting the subscriber");
start_standby_server(&opt, true, true);
/* Waiting the subscriber to be promoted */
@@ -2611,7 +2814,8 @@ main(int argc, char **argv)
drop_failover_replication_slots(dbinfos.dbinfo);
/* Stop the subscriber */
- pg_log_info("stopping the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "stopping the subscriber");
stop_standby_server(subscriber_dir);
/* Change system identifier from subscriber */
@@ -2619,7 +2823,8 @@ main(int argc, char **argv)
success = true;
- pg_log_info("Done!");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "Done!");
return 0;
}
--
2.43.0
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
@ 2026-03-20 10:30 ` Nisha Moond <[email protected]>
1 sibling, 0 replies; 55+ messages in thread
From: Nisha Moond @ 2026-03-20 10:30 UTC (permalink / raw)
To: Gyan Sreejith <[email protected]>; +Cc: Kuroda, Hayato/黒田 隼人 <[email protected]>; Amit Kapila <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
On Thu, Mar 19, 2026 at 11:59 PM Gyan Sreejith <[email protected]> wrote:
>
> Thank you, Kuroda-san, for all the work.
> I have made a small change - I added a pg_free() to free internal_log_file.
>>
>>
Hi Gyan,
I reviewed/tested the patches, please find below comments for v13-002 patch -
File: pg_createsubscriber.c
1)
+
+ if ((internal_log_file_fp = logfile_open(internal_log_file, "a")) == NULL)
+ pg_fatal("could not open log file \"%s\": %m", internal_log_file);
+
IIUC, the pg_fatal() call here seems unreachable as the function
logfile_open() itself calls pg_fatal() and exits if it fails to open
the file.
I think it should just be -
internal_log_file_fp = logfile_open(internal_log_file, "a");
Please correct me if I'm missing something.
~~~
2)
+ if (opt->log_dir != NULL)
+ out_file = psprintf("%s/%s/%s.log", opt->log_dir, log_timestamp,
SERVER_LOG_FILE_NAME);
+ else
+ out_file = DEVNULL;
+
+ cmd_str = psprintf("\"%s\" -D \"%s\" >> \"%s\"", pg_resetwal_path,
+ subscriber_dir, out_file);
Similar to internal_log_file, why are 'out_file' and 'cmd_str' above not freed?
~~~
3) Typo - extra blank line after va_end(args);
@@ -205,10 +243,11 @@ pg_fatal(const char *pg_restrict fmt,...)
va_start(args, fmt);
- pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, args);
+ pg_createsub_log_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, args);
va_end(args);
+
exit(1);
~~~
4) File: t/040_pg_createsubscriber.pl
+is( scalar(@server_log_files), 1, "
+ pg_createsubscriber_server.log file was created");
...
...
+is( scalar(@internal_log_files), 1, "
+ pg_createsubscriber_internal.log file was created");
Above introduces newlines in result log, test 29 and 32 looks like -
[14:46:59.071](0.639s) ok 28 - run pg_createsubscriber --dry-run on node S
[14:46:59.071](0.000s) ok 29 -
[14:46:59.071](0.000s) # pg_createsubscriber_server.log file was created
[14:46:59.071](0.000s) ok 30 - pg_createsubscriber_server.log file not empty
[14:46:59.071](0.000s) ok 31 - server reached consistent recovery state
[14:46:59.072](0.000s) ok 32 -
[14:46:59.072](0.000s) # pg_createsubscriber_internal.log file was created
[14:46:59.072](0.000s) ok 33 - pg_createsubscriber_internal.log file not empty
These should be single-line results similar to others.
--
Thanks,
Nisha
^ permalink raw reply [nested|flat] 55+ messages in thread
* RE: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
@ 2026-03-20 11:37 ` Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-20 17:00 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
1 sibling, 1 reply; 55+ messages in thread
From: Kuroda, Hayato/黒田 隼人 @ 2026-03-20 11:37 UTC (permalink / raw)
To: 'Gyan Sreejith' <[email protected]>; +Cc: Amit Kapila <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
Dear Gyan,
Thanks for updating the patch!
While considering it further, I started to think the combination with -l and -v
is problematic.
For now, if -l and -v are set, info/debug-level messages are written only to the
file, whereas warning/error messages are written to both.
In the case of pg_upgrade, however, all additional messages are printed to both
the terminal and the file. They are similar programs, so we should follow the
spec. Also, there is another advantage that users can check the progress of the
command from INFO/DEBUG messages.
Since all comments were OK for you, I merged 0003 into 0002, and now all
messages would be printed on both stderr/log file. How do you think?
Can you also check other comments like posted on [1] and revise if needed?
[1]: https://www.postgresql.org/message-id/[email protected]...
Best regards,
Hayato Kuroda
FUJITSU LIMITED
Attachments:
[application/octet-stream] v14-0001-pg_createsubscriber-use-own-reporting-functions.patch (55.1K, 2-v14-0001-pg_createsubscriber-use-own-reporting-functions.patch)
download | inline diff:
From 2cf32bdfed2183509b9136f637d77e7893bd19a7 Mon Sep 17 00:00:00 2001
From: Hayato Kuroda <[email protected]>
Date: Wed, 18 Mar 2026 20:20:50 +0900
Subject: [PATCH v14 1/2] pg_createsubscriber: use own reporting functions
This commit converts all pg_log_xxx families to call a new reporting function
pg_createsub_log(). Also, pg_fatal() is overwritten to use its own.
This commit changes nothing from the outside, but is needed for the upcoming
commit.
---
src/bin/pg_basebackup/pg_createsubscriber.c | 717 +++++++++++++-------
1 file changed, 464 insertions(+), 253 deletions(-)
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index 2bc84505aab..c3df83025d7 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -31,6 +31,12 @@
#include "fe_utils/version.h"
#include "getopt_long.h"
+/*
+ * For now, pg_createsubscriber does not use common/logging.c; use our own
+ * pg_fatal.
+ */
+#undef pg_fatal
+
#define DEFAULT_SUB_PORT "50432"
#define OBJECTTYPE_PUBLICATIONS 0x0001
@@ -146,6 +152,11 @@ 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 pg_createsub_log(enum pg_log_level, enum pg_log_part,
+ const char *pg_restrict fmt,...)
+ pg_attribute_printf(3, 4);
+pg_noreturn static void pg_fatal(const char *pg_restrict fmt,...)
+ pg_attribute_printf(1, 2);
#define WAIT_INTERVAL 1 /* 1 second */
@@ -174,6 +185,38 @@ static bool recovery_ended = false;
static bool standby_running = false;
static bool recovery_params_set = false;
+/*
+ * Report a message with a given log level
+ */
+static void
+pg_createsub_log(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt,...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+
+ pg_log_generic_v(level, part, fmt, args);
+
+ va_end(args);
+}
+
+/*
+ * Report a fatal error and exit
+ */
+static void
+pg_fatal(const char *pg_restrict fmt,...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+
+ pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, args);
+
+ va_end(args);
+
+ exit(1);
+}
/*
* Clean up objects created by pg_createsubscriber.
@@ -205,7 +248,8 @@ cleanup_objects_atexit(void)
if (durable_rename(conf_filename, conf_filename_disabled) != 0)
{
/* durable_rename() has already logged something. */
- pg_log_warning_hint("A manual removal of the recovery parameters may be required.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "A manual removal of the recovery parameters may be required.");
}
}
@@ -219,9 +263,11 @@ cleanup_objects_atexit(void)
*/
if (recovery_ended)
{
- pg_log_warning("failed after the end of recovery");
- pg_log_warning_hint("The target server cannot be used as a physical replica anymore. "
- "You must recreate the physical replica before continuing.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "failed after the end of recovery");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "The target server cannot be used as a physical replica anymore. "
+ "You must recreate the physical replica before continuing.");
}
for (int i = 0; i < num_dbs; i++)
@@ -251,17 +297,21 @@ cleanup_objects_atexit(void)
*/
if (dbinfo->made_publication)
{
- pg_log_warning("publication \"%s\" created in database \"%s\" on primary was left behind",
- dbinfo->pubname,
- dbinfo->dbname);
- pg_log_warning_hint("Drop this publication before trying again.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "publication \"%s\" created in database \"%s\" on primary was left behind",
+ dbinfo->pubname,
+ dbinfo->dbname);
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop this publication before trying again.");
}
if (dbinfo->made_replslot)
{
- pg_log_warning("replication slot \"%s\" created in database \"%s\" on primary was left behind",
- dbinfo->replslotname,
- dbinfo->dbname);
- pg_log_warning_hint("Drop this replication slot soon to avoid retention of WAL files.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "replication slot \"%s\" created in database \"%s\" on primary was left behind",
+ dbinfo->replslotname,
+ dbinfo->dbname);
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop this replication slot soon to avoid retention of WAL files.");
}
}
}
@@ -342,7 +392,8 @@ get_base_conninfo(const char *conninfo, char **dbname)
conn_opts = PQconninfoParse(conninfo, &errmsg);
if (conn_opts == NULL)
{
- pg_log_error("could not parse connection string: %s", errmsg);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not parse connection string: %s", errmsg);
PQfreemem(errmsg);
return NULL;
}
@@ -426,7 +477,8 @@ get_exec_path(const char *argv0, const char *progname)
progname, full_path, "pg_createsubscriber");
}
- pg_log_debug("%s path is: %s", progname, exec_path);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "%s path is: %s", progname, exec_path);
return exec_path;
}
@@ -443,8 +495,9 @@ check_data_directory(const char *datadir)
uint32 major_version;
char *version_str;
- pg_log_info("checking if directory \"%s\" is a cluster data directory",
- datadir);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "checking if directory \"%s\" is a cluster data directory",
+ datadir);
if (stat(datadir, &statbuf) != 0)
{
@@ -462,9 +515,11 @@ check_data_directory(const char *datadir)
major_version = GET_PG_MAJORVERSION_NUM(get_pg_version(datadir, &version_str));
if (major_version != PG_MAJORVERSION_NUM)
{
- pg_log_error("data directory is of wrong version");
- pg_log_error_detail("File \"%s\" contains \"%s\", which is not compatible with this program's version \"%s\".",
- "PG_VERSION", version_str, PG_MAJORVERSION);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "data directory is of wrong version");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "File \"%s\" contains \"%s\", which is not compatible with this program's version \"%s\".",
+ "PG_VERSION", version_str, PG_MAJORVERSION);
exit(1);
}
}
@@ -547,14 +602,16 @@ store_pub_sub_info(const struct CreateSubscriberOptions *opt,
dbinfo[i].subname = NULL;
/* Other fields will be filled later */
- pg_log_debug("publisher(%d): publication: %s ; replication slot: %s ; connection string: %s", i,
- dbinfo[i].pubname ? dbinfo[i].pubname : "(auto)",
- dbinfo[i].replslotname ? dbinfo[i].replslotname : "(auto)",
- dbinfo[i].pubconninfo);
- pg_log_debug("subscriber(%d): subscription: %s ; connection string: %s, two_phase: %s", i,
- dbinfo[i].subname ? dbinfo[i].subname : "(auto)",
- dbinfo[i].subconninfo,
- dbinfos.two_phase ? "true" : "false");
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher(%d): publication: %s ; replication slot: %s ; connection string: %s", i,
+ dbinfo[i].pubname ? dbinfo[i].pubname : "(auto)",
+ dbinfo[i].replslotname ? dbinfo[i].replslotname : "(auto)",
+ dbinfo[i].pubconninfo);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber(%d): subscription: %s ; connection string: %s, two_phase: %s", i,
+ dbinfo[i].subname ? dbinfo[i].subname : "(auto)",
+ dbinfo[i].subconninfo,
+ dbinfos.two_phase ? "true" : "false");
if (num_pubs > 0)
pubcell = pubcell->next;
@@ -582,8 +639,9 @@ connect_database(const char *conninfo, bool exit_on_error)
conn = PQconnectdb(conninfo);
if (PQstatus(conn) != CONNECTION_OK)
{
- pg_log_error("connection to database failed: %s",
- PQerrorMessage(conn));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "connection to database failed: %s",
+ PQerrorMessage(conn));
PQfinish(conn);
if (exit_on_error)
@@ -595,8 +653,9 @@ connect_database(const char *conninfo, bool exit_on_error)
res = PQexec(conn, ALWAYS_SECURE_SEARCH_PATH_SQL);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not clear \"search_path\": %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not clear \"search_path\": %s",
+ PQresultErrorMessage(res));
PQclear(res);
PQfinish(conn);
@@ -635,27 +694,31 @@ get_primary_sysid(const char *conninfo)
PGresult *res;
uint64 sysid;
- pg_log_info("getting system identifier from publisher");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "getting system identifier from publisher");
conn = connect_database(conninfo, true);
res = PQexec(conn, "SELECT system_identifier FROM pg_catalog.pg_control_system()");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not get system identifier: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not get system identifier: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
if (PQntuples(res) != 1)
{
- pg_log_error("could not get system identifier: got %d rows, expected %d row",
- PQntuples(res), 1);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not get system identifier: got %d rows, expected %d row",
+ PQntuples(res), 1);
disconnect_database(conn, true);
}
sysid = strtou64(PQgetvalue(res, 0, 0), NULL, 10);
- pg_log_info("system identifier is %" PRIu64 " on publisher", sysid);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "system identifier is %" PRIu64 " on publisher", sysid);
PQclear(res);
disconnect_database(conn, false);
@@ -675,7 +738,8 @@ get_standby_sysid(const char *datadir)
bool crc_ok;
uint64 sysid;
- pg_log_info("getting system identifier from subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "getting system identifier from subscriber");
cf = get_controlfile(datadir, &crc_ok);
if (!crc_ok)
@@ -683,7 +747,8 @@ get_standby_sysid(const char *datadir)
sysid = cf->system_identifier;
- pg_log_info("system identifier is %" PRIu64 " on subscriber", sysid);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "system identifier is %" PRIu64 " on subscriber", sysid);
pg_free(cf);
@@ -704,7 +769,8 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
char *cmd_str;
- pg_log_info("modifying system identifier of subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "modifying system identifier of subscriber");
cf = get_controlfile(subscriber_dir, &crc_ok);
if (!crc_ok)
@@ -721,31 +787,37 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
cf->system_identifier |= getpid() & 0xFFF;
if (dry_run)
- pg_log_info("dry-run: would set system identifier to %" PRIu64 " on subscriber",
- cf->system_identifier);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would set system identifier to %" PRIu64 " on subscriber",
+ cf->system_identifier);
else
{
update_controlfile(subscriber_dir, cf, true);
- pg_log_info("system identifier is %" PRIu64 " on subscriber",
- cf->system_identifier);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "system identifier is %" PRIu64 " on subscriber",
+ cf->system_identifier);
}
if (dry_run)
- pg_log_info("dry-run: would run pg_resetwal on the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would run pg_resetwal on the subscriber");
else
- pg_log_info("running pg_resetwal on the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "running pg_resetwal on the subscriber");
cmd_str = psprintf("\"%s\" -D \"%s\" > \"%s\"", pg_resetwal_path,
subscriber_dir, DEVNULL);
- pg_log_debug("pg_resetwal command is: %s", cmd_str);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "pg_resetwal command is: %s", cmd_str);
if (!dry_run)
{
int rc = system(cmd_str);
if (rc == 0)
- pg_log_info("successfully reset WAL on the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "successfully reset WAL on the subscriber");
else
pg_fatal("could not reset WAL on subscriber: %s", wait_result_to_str(rc));
}
@@ -771,15 +843,17 @@ generate_object_name(PGconn *conn)
"WHERE datname = pg_catalog.current_database()");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain database OID: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain database OID: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
if (PQntuples(res) != 1)
{
- pg_log_error("could not obtain database OID: got %d rows, expected %d row",
- PQntuples(res), 1);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain database OID: got %d rows, expected %d row",
+ PQntuples(res), 1);
disconnect_database(conn, true);
}
@@ -819,8 +893,9 @@ find_publication(PGconn *conn, const char *pubname, const char *dbname)
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not find publication \"%s\" in database \"%s\": %s",
- pubname, dbname, PQerrorMessage(conn));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not find publication \"%s\" in database \"%s\": %s",
+ pubname, dbname, PQerrorMessage(conn));
disconnect_database(conn, true);
}
@@ -873,8 +948,9 @@ setup_publisher(struct LogicalRepInfo *dbinfo)
if (find_publication(conn, dbinfo[i].pubname, dbinfo[i].dbname))
{
/* Reuse existing publication on publisher. */
- pg_log_info("use existing publication \"%s\" in database \"%s\"",
- dbinfo[i].pubname, dbinfo[i].dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "use existing publication \"%s\" in database \"%s\"",
+ dbinfo[i].pubname, dbinfo[i].dbname);
/* Don't remove pre-existing publication if an error occurs. */
dbinfo[i].made_publication = false;
}
@@ -912,8 +988,9 @@ setup_publisher(struct LogicalRepInfo *dbinfo)
res = PQexec(conn, "SELECT pg_log_standby_snapshot()");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not write an additional WAL record: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not write an additional WAL record: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
PQclear(res);
@@ -938,8 +1015,9 @@ server_is_in_recovery(PGconn *conn)
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain recovery progress: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain recovery progress: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -971,7 +1049,8 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
int max_prepared_transactions;
char *max_slot_wal_keep_size;
- pg_log_info("checking settings on publisher");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "checking settings on publisher");
conn = connect_database(dbinfo[0].pubconninfo, true);
@@ -981,7 +1060,8 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
*/
if (server_is_in_recovery(conn))
{
- pg_log_error("primary server cannot be in recovery");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "primary server cannot be in recovery");
disconnect_database(conn, true);
}
@@ -1007,8 +1087,9 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain publisher settings: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain publisher settings: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1022,48 +1103,63 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
PQclear(res);
- pg_log_debug("publisher: wal_level: %s", wal_level);
- pg_log_debug("publisher: max_replication_slots: %d", max_repslots);
- pg_log_debug("publisher: current replication slots: %d", cur_repslots);
- pg_log_debug("publisher: max_wal_senders: %d", max_walsenders);
- pg_log_debug("publisher: current wal senders: %d", cur_walsenders);
- pg_log_debug("publisher: max_prepared_transactions: %d",
- max_prepared_transactions);
- pg_log_debug("publisher: max_slot_wal_keep_size: %s",
- max_slot_wal_keep_size);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: wal_level: %s", wal_level);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: max_replication_slots: %d", max_repslots);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: current replication slots: %d", cur_repslots);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: max_wal_senders: %d", max_walsenders);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: current wal senders: %d", cur_walsenders);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: max_prepared_transactions: %d",
+ max_prepared_transactions);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: max_slot_wal_keep_size: %s",
+ max_slot_wal_keep_size);
disconnect_database(conn, false);
if (strcmp(wal_level, "minimal") == 0)
{
- pg_log_error("publisher requires \"wal_level\" >= \"replica\"");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "publisher requires \"wal_level\" >= \"replica\"");
failed = true;
}
if (max_repslots - cur_repslots < num_dbs)
{
- pg_log_error("publisher requires %d replication slots, but only %d remain",
- num_dbs, max_repslots - cur_repslots);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_replication_slots", cur_repslots + num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "publisher requires %d replication slots, but only %d remain",
+ num_dbs, max_repslots - cur_repslots);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_replication_slots", cur_repslots + num_dbs);
failed = true;
}
if (max_walsenders - cur_walsenders < num_dbs)
{
- pg_log_error("publisher requires %d WAL sender processes, but only %d remain",
- num_dbs, max_walsenders - cur_walsenders);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_wal_senders", cur_walsenders + num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "publisher requires %d WAL sender processes, but only %d remain",
+ num_dbs, max_walsenders - cur_walsenders);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_wal_senders", cur_walsenders + num_dbs);
failed = true;
}
if (max_prepared_transactions != 0 && !dbinfos.two_phase)
{
- pg_log_warning("two_phase option will not be enabled for replication slots");
- pg_log_warning_detail("Subscriptions will be created with the two_phase option disabled. "
- "Prepared transactions will be replicated at COMMIT PREPARED.");
- pg_log_warning_hint("You can use the command-line option --enable-two-phase to enable two_phase.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "two_phase option will not be enabled for replication slots");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_DETAIL,
+ "Subscriptions will be created with the two_phase option disabled. "
+ "Prepared transactions will be replicated at COMMIT PREPARED.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "You can use the command-line option --enable-two-phase to enable two_phase.");
}
/*
@@ -1073,9 +1169,11 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
*/
if (dry_run && (strcmp(max_slot_wal_keep_size, "-1") != 0))
{
- pg_log_warning("required WAL could be removed from the publisher");
- pg_log_warning_hint("Set the configuration parameter \"%s\" to -1 to ensure that required WAL files are not prematurely removed.",
- "max_slot_wal_keep_size");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "required WAL could be removed from the publisher");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Set the configuration parameter \"%s\" to -1 to ensure that required WAL files are not prematurely removed.",
+ "max_slot_wal_keep_size");
}
pg_free(wal_level);
@@ -1106,14 +1204,16 @@ check_subscriber(const struct LogicalRepInfo *dbinfo)
int max_replorigins;
int max_wprocs;
- pg_log_info("checking settings on subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "checking settings on subscriber");
conn = connect_database(dbinfo[0].subconninfo, true);
/* The target server must be a standby */
if (!server_is_in_recovery(conn))
{
- pg_log_error("target server must be a standby");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "target server must be a standby");
disconnect_database(conn, true);
}
@@ -1137,8 +1237,9 @@ check_subscriber(const struct LogicalRepInfo *dbinfo)
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain subscriber settings: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain subscriber settings: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1148,12 +1249,16 @@ check_subscriber(const struct LogicalRepInfo *dbinfo)
if (strcmp(PQgetvalue(res, 3, 0), "") != 0)
primary_slot_name = pg_strdup(PQgetvalue(res, 3, 0));
- pg_log_debug("subscriber: max_logical_replication_workers: %d",
- max_lrworkers);
- pg_log_debug("subscriber: max_active_replication_origins: %d", max_replorigins);
- pg_log_debug("subscriber: max_worker_processes: %d", max_wprocs);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber: max_logical_replication_workers: %d",
+ max_lrworkers);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber: max_active_replication_origins: %d", max_replorigins);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber: max_worker_processes: %d", max_wprocs);
if (primary_slot_name)
- pg_log_debug("subscriber: primary_slot_name: %s", primary_slot_name);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber: primary_slot_name: %s", primary_slot_name);
PQclear(res);
@@ -1161,28 +1266,34 @@ check_subscriber(const struct LogicalRepInfo *dbinfo)
if (max_replorigins < num_dbs)
{
- pg_log_error("subscriber requires %d active replication origins, but only %d remain",
- num_dbs, max_replorigins);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_active_replication_origins", num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "subscriber requires %d active replication origins, but only %d remain",
+ num_dbs, max_replorigins);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_active_replication_origins", num_dbs);
failed = true;
}
if (max_lrworkers < num_dbs)
{
- pg_log_error("subscriber requires %d logical replication workers, but only %d remain",
- num_dbs, max_lrworkers);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_logical_replication_workers", num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "subscriber requires %d logical replication workers, but only %d remain",
+ num_dbs, max_lrworkers);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_logical_replication_workers", num_dbs);
failed = true;
}
if (max_wprocs < num_dbs + 1)
{
- pg_log_error("subscriber requires %d worker processes, but only %d remain",
- num_dbs + 1, max_wprocs);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_worker_processes", num_dbs + 1);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "subscriber requires %d worker processes, but only %d remain",
+ num_dbs + 1, max_wprocs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_worker_processes", num_dbs + 1);
failed = true;
}
@@ -1215,19 +1326,22 @@ drop_existing_subscription(PGconn *conn, const char *subname, const char *dbname
appendPQExpBuffer(query, " DROP SUBSCRIPTION %s;", subname);
if (dry_run)
- pg_log_info("dry-run: would drop subscription \"%s\" in database \"%s\"",
- subname, dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would drop subscription \"%s\" in database \"%s\"",
+ subname, dbname);
else
{
- pg_log_info("dropping subscription \"%s\" in database \"%s\"",
- subname, dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dropping subscription \"%s\" in database \"%s\"",
+ subname, dbname);
res = PQexec(conn, query->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not drop subscription \"%s\": %s",
- subname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not drop subscription \"%s\": %s",
+ subname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1261,8 +1375,9 @@ check_and_drop_existing_subscriptions(PGconn *conn,
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain pre-existing subscriptions: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain pre-existing subscriptions: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1373,7 +1488,8 @@ setup_recovery(const struct LogicalRepInfo *dbinfo, const char *datadir, const c
lsn);
}
- pg_log_debug("recovery parameters:\n%s", recoveryconfcontents->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "recovery parameters:\n%s", recoveryconfcontents->data);
if (!dry_run)
{
@@ -1427,9 +1543,11 @@ drop_primary_replication_slot(struct LogicalRepInfo *dbinfo, const char *slotnam
}
else
{
- pg_log_warning("could not drop replication slot \"%s\" on primary",
- slotname);
- pg_log_warning_hint("Drop this replication slot soon to avoid retention of WAL files.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "could not drop replication slot \"%s\" on primary",
+ slotname);
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop this replication slot soon to avoid retention of WAL files.");
}
}
@@ -1461,9 +1579,11 @@ drop_failover_replication_slots(struct LogicalRepInfo *dbinfo)
}
else
{
- pg_log_warning("could not obtain failover replication slot information: %s",
- PQresultErrorMessage(res));
- pg_log_warning_hint("Drop the failover replication slots on subscriber soon to avoid retention of WAL files.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "could not obtain failover replication slot information: %s",
+ PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop the failover replication slots on subscriber soon to avoid retention of WAL files.");
}
PQclear(res);
@@ -1471,8 +1591,10 @@ drop_failover_replication_slots(struct LogicalRepInfo *dbinfo)
}
else
{
- pg_log_warning("could not drop failover replication slot");
- pg_log_warning_hint("Drop the failover replication slots on subscriber soon to avoid retention of WAL files.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "could not drop failover replication slot");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop the failover replication slots on subscriber soon to avoid retention of WAL files.");
}
}
@@ -1494,11 +1616,13 @@ create_logical_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo)
Assert(conn != NULL);
if (dry_run)
- pg_log_info("dry-run: would create the replication slot \"%s\" in database \"%s\" on publisher",
- slot_name, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would create the replication slot \"%s\" in database \"%s\" on publisher",
+ slot_name, dbinfo->dbname);
else
- pg_log_info("creating the replication slot \"%s\" in database \"%s\" on publisher",
- slot_name, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "creating the replication slot \"%s\" in database \"%s\" on publisher",
+ slot_name, dbinfo->dbname);
slot_name_esc = PQescapeLiteral(conn, slot_name, strlen(slot_name));
@@ -1509,16 +1633,18 @@ create_logical_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo)
PQfreemem(slot_name_esc);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not create replication slot \"%s\" in database \"%s\": %s",
- slot_name, dbinfo->dbname,
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not create replication slot \"%s\" in database \"%s\": %s",
+ slot_name, dbinfo->dbname,
+ PQresultErrorMessage(res));
PQclear(res);
destroyPQExpBuffer(str);
return NULL;
@@ -1547,11 +1673,13 @@ drop_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo,
Assert(conn != NULL);
if (dry_run)
- pg_log_info("dry-run: would drop the replication slot \"%s\" in database \"%s\"",
- slot_name, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would drop the replication slot \"%s\" in database \"%s\"",
+ slot_name, dbinfo->dbname);
else
- pg_log_info("dropping the replication slot \"%s\" in database \"%s\"",
- slot_name, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dropping the replication slot \"%s\" in database \"%s\"",
+ slot_name, dbinfo->dbname);
slot_name_esc = PQescapeLiteral(conn, slot_name, strlen(slot_name));
@@ -1559,15 +1687,17 @@ drop_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo,
PQfreemem(slot_name_esc);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not drop replication slot \"%s\" in database \"%s\": %s",
- slot_name, dbinfo->dbname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not drop replication slot \"%s\" in database \"%s\": %s",
+ slot_name, dbinfo->dbname, PQresultErrorMessage(res));
dbinfo->made_replslot = false; /* don't try again. */
}
@@ -1587,25 +1717,32 @@ pg_ctl_status(const char *pg_ctl_cmd, int rc)
{
if (WIFEXITED(rc))
{
- pg_log_error("pg_ctl failed with exit code %d", WEXITSTATUS(rc));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "pg_ctl failed with exit code %d",
+ WEXITSTATUS(rc));
}
else if (WIFSIGNALED(rc))
{
#if defined(WIN32)
- pg_log_error("pg_ctl was terminated by exception 0x%X",
- WTERMSIG(rc));
- pg_log_error_detail("See C include file \"ntstatus.h\" for a description of the hexadecimal value.");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "pg_ctl was terminated by exception 0x%X",
+ WTERMSIG(rc));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "See C include file \"ntstatus.h\" for a description of the hexadecimal value.");
#else
- pg_log_error("pg_ctl was terminated by signal %d: %s",
- WTERMSIG(rc), pg_strsignal(WTERMSIG(rc)));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "pg_ctl was terminated by signal %d: %s",
+ WTERMSIG(rc), pg_strsignal(WTERMSIG(rc)));
#endif
}
else
{
- pg_log_error("pg_ctl exited with unrecognized status %d", rc);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "pg_ctl exited with unrecognized status %d", rc);
}
- pg_log_error_detail("The failed command was: %s", pg_ctl_cmd);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "The failed command was: %s", pg_ctl_cmd);
exit(1);
}
}
@@ -1650,12 +1787,14 @@ start_standby_server(const struct CreateSubscriberOptions *opt, bool restricted_
if (restrict_logical_worker)
appendPQExpBufferStr(pg_ctl_cmd, " -o \"-c max_logical_replication_workers=0\"");
- pg_log_debug("pg_ctl command is: %s", pg_ctl_cmd->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "pg_ctl command is: %s", pg_ctl_cmd->data);
rc = system(pg_ctl_cmd->data);
pg_ctl_status(pg_ctl_cmd->data, rc);
standby_running = true;
destroyPQExpBuffer(pg_ctl_cmd);
- pg_log_info("server was started");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "server was started");
}
static void
@@ -1666,11 +1805,13 @@ stop_standby_server(const char *datadir)
pg_ctl_cmd = psprintf("\"%s\" stop -D \"%s\" -s", pg_ctl_path,
datadir);
- pg_log_debug("pg_ctl command is: %s", pg_ctl_cmd);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "pg_ctl command is: %s", pg_ctl_cmd);
rc = system(pg_ctl_cmd);
pg_ctl_status(pg_ctl_cmd, rc);
standby_running = false;
- pg_log_info("server was stopped");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "server was stopped");
}
/*
@@ -1689,7 +1830,8 @@ wait_for_end_recovery(const char *conninfo, const struct CreateSubscriberOptions
bool ready = false;
int timer = 0;
- pg_log_info("waiting for the target server to reach the consistent state");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "waiting for the target server to reach the consistent state");
conn = connect_database(conninfo, true);
@@ -1707,7 +1849,8 @@ wait_for_end_recovery(const char *conninfo, const struct CreateSubscriberOptions
if (opt->recovery_timeout > 0 && timer >= opt->recovery_timeout)
{
stop_standby_server(subscriber_dir);
- pg_log_error("recovery timed out");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "recovery timed out");
disconnect_database(conn, true);
}
@@ -1721,8 +1864,10 @@ wait_for_end_recovery(const char *conninfo, const struct CreateSubscriberOptions
if (!ready)
pg_fatal("server did not end recovery");
- pg_log_info("target server reached the consistent state");
- pg_log_info_hint("If pg_createsubscriber fails after this point, you must recreate the physical replica before continuing.");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "target server reached the consistent state");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_HINT,
+ "If pg_createsubscriber fails after this point, you must recreate the physical replica before continuing.");
}
/*
@@ -1749,8 +1894,9 @@ create_publication(PGconn *conn, struct LogicalRepInfo *dbinfo)
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain publication information: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain publication information: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1763,8 +1909,10 @@ create_publication(PGconn *conn, struct LogicalRepInfo *dbinfo)
* pg_createsubscriber_ prefix followed by the exact database oid and
* a random number.
*/
- pg_log_error("publication \"%s\" already exists", dbinfo->pubname);
- pg_log_error_hint("Consider renaming this publication before continuing.");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "publication \"%s\" already exists", dbinfo->pubname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Consider renaming this publication before continuing.");
disconnect_database(conn, true);
}
@@ -1772,24 +1920,28 @@ create_publication(PGconn *conn, struct LogicalRepInfo *dbinfo)
resetPQExpBuffer(str);
if (dry_run)
- pg_log_info("dry-run: would create publication \"%s\" in database \"%s\"",
- dbinfo->pubname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would create publication \"%s\" in database \"%s\"",
+ dbinfo->pubname, dbinfo->dbname);
else
- pg_log_info("creating publication \"%s\" in database \"%s\"",
- dbinfo->pubname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "creating publication \"%s\" in database \"%s\"",
+ dbinfo->pubname, dbinfo->dbname);
appendPQExpBuffer(str, "CREATE PUBLICATION %s FOR ALL TABLES",
ipubname_esc);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not create publication \"%s\" in database \"%s\": %s",
- dbinfo->pubname, dbinfo->dbname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not create publication \"%s\" in database \"%s\": %s",
+ dbinfo->pubname, dbinfo->dbname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
PQclear(res);
@@ -1819,25 +1971,29 @@ drop_publication(PGconn *conn, const char *pubname, const char *dbname,
pubname_esc = PQescapeIdentifier(conn, pubname, strlen(pubname));
if (dry_run)
- pg_log_info("dry-run: would drop publication \"%s\" in database \"%s\"",
- pubname, dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would drop publication \"%s\" in database \"%s\"",
+ pubname, dbname);
else
- pg_log_info("dropping publication \"%s\" in database \"%s\"",
- pubname, dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dropping publication \"%s\" in database \"%s\"",
+ pubname, dbname);
appendPQExpBuffer(str, "DROP PUBLICATION %s", pubname_esc);
PQfreemem(pubname_esc);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not drop publication \"%s\" in database \"%s\": %s",
- pubname, dbname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not drop publication \"%s\" in database \"%s\": %s",
+ pubname, dbname, PQresultErrorMessage(res));
*made_publication = false; /* don't try again. */
/*
@@ -1872,15 +2028,17 @@ check_and_drop_publications(PGconn *conn, struct LogicalRepInfo *dbinfo)
if (drop_all_pubs)
{
- pg_log_info("dropping all existing publications in database \"%s\"",
- dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dropping all existing publications in database \"%s\"",
+ dbinfo->dbname);
/* Fetch all publication names */
res = PQexec(conn, "SELECT pubname FROM pg_catalog.pg_publication;");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain publication information: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain publication information: %s",
+ PQresultErrorMessage(res));
PQclear(res);
disconnect_database(conn, true);
}
@@ -1903,11 +2061,13 @@ check_and_drop_publications(PGconn *conn, struct LogicalRepInfo *dbinfo)
else
{
if (dry_run)
- pg_log_info("dry-run: would preserve existing publication \"%s\" in database \"%s\"",
- dbinfo->pubname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would preserve existing publication \"%s\" in database \"%s\"",
+ dbinfo->pubname, dbinfo->dbname);
else
- pg_log_info("preserve existing publication \"%s\" in database \"%s\"",
- dbinfo->pubname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "preserve existing publication \"%s\" in database \"%s\"",
+ dbinfo->pubname, dbinfo->dbname);
}
}
}
@@ -1941,11 +2101,13 @@ create_subscription(PGconn *conn, const struct LogicalRepInfo *dbinfo)
replslotname_esc = PQescapeLiteral(conn, dbinfo->replslotname, strlen(dbinfo->replslotname));
if (dry_run)
- pg_log_info("dry-run: would create subscription \"%s\" in database \"%s\"",
- dbinfo->subname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would create subscription \"%s\" in database \"%s\"",
+ dbinfo->subname, dbinfo->dbname);
else
- pg_log_info("creating subscription \"%s\" in database \"%s\"",
- dbinfo->subname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "creating subscription \"%s\" in database \"%s\"",
+ dbinfo->subname, dbinfo->dbname);
appendPQExpBuffer(str,
"CREATE SUBSCRIPTION %s CONNECTION %s PUBLICATION %s "
@@ -1959,15 +2121,17 @@ create_subscription(PGconn *conn, const struct LogicalRepInfo *dbinfo)
PQfreemem(pubconninfo_esc);
PQfreemem(replslotname_esc);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not create subscription \"%s\" in database \"%s\": %s",
- dbinfo->subname, dbinfo->dbname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not create subscription \"%s\" in database \"%s\": %s",
+ dbinfo->subname, dbinfo->dbname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
PQclear(res);
@@ -2011,15 +2175,17 @@ set_replication_progress(PGconn *conn, const struct LogicalRepInfo *dbinfo, cons
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain subscription OID: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain subscription OID: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
if (PQntuples(res) != 1 && !dry_run)
{
- pg_log_error("could not obtain subscription OID: got %d rows, expected %d row",
- PQntuples(res), 1);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain subscription OID: got %d rows, expected %d row",
+ PQntuples(res), 1);
disconnect_database(conn, true);
}
@@ -2043,26 +2209,30 @@ set_replication_progress(PGconn *conn, const struct LogicalRepInfo *dbinfo, cons
originname = psprintf("pg_%u", suboid);
if (dry_run)
- pg_log_info("dry-run: would set the replication progress (node name \"%s\", LSN %s) in database \"%s\"",
- originname, lsnstr, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would set the replication progress (node name \"%s\", LSN %s) in database \"%s\"",
+ originname, lsnstr, dbinfo->dbname);
else
- pg_log_info("setting the replication progress (node name \"%s\", LSN %s) in database \"%s\"",
- originname, lsnstr, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "setting the replication progress (node name \"%s\", LSN %s) in database \"%s\"",
+ originname, lsnstr, dbinfo->dbname);
resetPQExpBuffer(str);
appendPQExpBuffer(str,
"SELECT pg_catalog.pg_replication_origin_advance('%s', '%s')",
originname, lsnstr);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not set replication progress for subscription \"%s\": %s",
- dbinfo->subname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not set replication progress for subscription \"%s\": %s",
+ dbinfo->subname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
PQclear(res);
@@ -2093,23 +2263,27 @@ enable_subscription(PGconn *conn, const struct LogicalRepInfo *dbinfo)
subname = PQescapeIdentifier(conn, dbinfo->subname, strlen(dbinfo->subname));
if (dry_run)
- pg_log_info("dry-run: would enable subscription \"%s\" in database \"%s\"",
- dbinfo->subname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would enable subscription \"%s\" in database \"%s\"",
+ dbinfo->subname, dbinfo->dbname);
else
- pg_log_info("enabling subscription \"%s\" in database \"%s\"",
- dbinfo->subname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "enabling subscription \"%s\" in database \"%s\"",
+ dbinfo->subname, dbinfo->dbname);
appendPQExpBuffer(str, "ALTER SUBSCRIPTION %s ENABLE", subname);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not enable subscription \"%s\": %s",
- dbinfo->subname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not enable subscription \"%s\": %s",
+ dbinfo->subname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -2154,7 +2328,9 @@ get_publisher_databases(struct CreateSubscriberOptions *opt,
res = PQexec(conn, "SELECT datname FROM pg_database WHERE datistemplate = false AND datallowconn AND datconnlimit <> -2 ORDER BY 1");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain a list of databases: %s", PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain a list of databases: %s",
+ PQresultErrorMessage(res));
PQclear(res);
disconnect_database(conn, true);
}
@@ -2258,9 +2434,11 @@ main(int argc, char **argv)
#ifndef WIN32
if (geteuid() == 0)
{
- pg_log_error("cannot be executed by \"root\"");
- pg_log_error_hint("You must run %s as the PostgreSQL superuser.",
- progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "cannot be executed by \"root\"");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "You must run %s as the PostgreSQL superuser.",
+ progname);
exit(1);
}
#endif
@@ -2351,7 +2529,9 @@ main(int argc, char **argv)
break;
default:
/* getopt_long already emitted a complaint */
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.",
+ progname);
exit(1);
}
}
@@ -2372,9 +2552,12 @@ main(int argc, char **argv)
if (bad_switch)
{
- pg_log_error("options %s and %s cannot be used together",
- bad_switch, "-a/--all");
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "options %s and %s cannot be used together",
+ bad_switch, "-a/--all");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.",
+ progname);
exit(1);
}
}
@@ -2382,17 +2565,21 @@ main(int argc, char **argv)
/* Any non-option arguments? */
if (optind < argc)
{
- pg_log_error("too many command-line arguments (first is \"%s\")",
- argv[optind]);
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "too many command-line arguments (first is \"%s\")",
+ argv[optind]);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.", progname);
exit(1);
}
/* Required arguments */
if (subscriber_dir == NULL)
{
- pg_log_error("no subscriber data directory specified");
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "no subscriber data directory specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.", progname);
exit(1);
}
@@ -2419,22 +2606,27 @@ main(int argc, char **argv)
* identical entries for physical and logical replication. If there is
* not, we would fail anyway.
*/
- pg_log_error("no publisher connection string specified");
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "no publisher connection string specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.", progname);
exit(1);
}
if (dry_run)
- pg_log_info("Executing in dry-run mode.\n"
- "The target directory will not be modified.");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "Executing in dry-run mode.\n"
+ "The target directory will not be modified.");
- pg_log_info("validating publisher connection string");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "validating publisher connection string");
pub_base_conninfo = get_base_conninfo(opt.pub_conninfo_str,
&dbname_conninfo);
if (pub_base_conninfo == NULL)
exit(1);
- pg_log_info("validating subscriber connection string");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "validating subscriber connection string");
sub_base_conninfo = get_sub_conninfo(&opt);
/*
@@ -2451,7 +2643,8 @@ main(int argc, char **argv)
if (opt.database_names.head == NULL)
{
- pg_log_info("no database was specified");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "no database was specified");
/*
* Try to obtain the dbname from the publisher conninfo. If dbname
@@ -2462,14 +2655,17 @@ main(int argc, char **argv)
simple_string_list_append(&opt.database_names, dbname_conninfo);
num_dbs++;
- pg_log_info("database name \"%s\" was extracted from the publisher connection string",
- dbname_conninfo);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "database name \"%s\" was extracted from the publisher connection string",
+ dbname_conninfo);
}
else
{
- pg_log_error("no database name specified");
- pg_log_error_hint("Try \"%s --help\" for more information.",
- progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "no database name specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.",
+ progname);
exit(1);
}
}
@@ -2477,23 +2673,29 @@ main(int argc, char **argv)
/* Number of object names must match number of databases */
if (num_pubs > 0 && num_pubs != num_dbs)
{
- pg_log_error("wrong number of publication names specified");
- pg_log_error_detail("The number of specified publication names (%d) must match the number of specified database names (%d).",
- num_pubs, num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "wrong number of publication names specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "The number of specified publication names (%d) must match the number of specified database names (%d).",
+ num_pubs, num_dbs);
exit(1);
}
if (num_subs > 0 && num_subs != num_dbs)
{
- pg_log_error("wrong number of subscription names specified");
- pg_log_error_detail("The number of specified subscription names (%d) must match the number of specified database names (%d).",
- num_subs, num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "wrong number of subscription names specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "The number of specified subscription names (%d) must match the number of specified database names (%d).",
+ num_subs, num_dbs);
exit(1);
}
if (num_replslots > 0 && num_replslots != num_dbs)
{
- pg_log_error("wrong number of replication slot names specified");
- pg_log_error_detail("The number of specified replication slot names (%d) must match the number of specified database names (%d).",
- num_replslots, num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "wrong number of replication slot names specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "The number of specified replication slot names (%d) must match the number of specified database names (%d).",
+ num_replslots, num_dbs);
exit(1);
}
@@ -2504,9 +2706,11 @@ main(int argc, char **argv)
dbinfos.objecttypes_to_clean |= OBJECTTYPE_PUBLICATIONS;
else
{
- pg_log_error("invalid object type \"%s\" specified for %s",
- cell->val, "--clean");
- pg_log_error_hint("The valid value is: \"%s\"", "publications");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "invalid object type \"%s\" specified for %s",
+ cell->val, "--clean");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "The valid value is: \"%s\"", "publications");
exit(1);
}
}
@@ -2550,8 +2754,10 @@ main(int argc, char **argv)
*/
if (stat(pidfile, &statbuf) == 0)
{
- pg_log_error("standby server is running");
- pg_log_error_hint("Stop the standby server and try again.");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "standby server is running");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Stop the standby server and try again.");
exit(1);
}
@@ -2560,7 +2766,8 @@ main(int argc, char **argv)
* by command-line options). The goal is to avoid connections during the
* transformation steps.
*/
- pg_log_info("starting the standby server with command-line options");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "starting the standby server with command-line options");
start_standby_server(&opt, true, false);
/* Check if the standby server is ready for logical replication */
@@ -2576,7 +2783,8 @@ main(int argc, char **argv)
* guarantees it) *before* creating the replication slots in
* setup_publisher().
*/
- pg_log_info("stopping the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "stopping the subscriber");
stop_standby_server(subscriber_dir);
/* Create the required objects for each database on publisher */
@@ -2590,7 +2798,8 @@ main(int argc, char **argv)
* until accepting connections. We don't want to start logical replication
* during setup.
*/
- pg_log_info("starting the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "starting the subscriber");
start_standby_server(&opt, true, true);
/* Waiting the subscriber to be promoted */
@@ -2611,7 +2820,8 @@ main(int argc, char **argv)
drop_failover_replication_slots(dbinfos.dbinfo);
/* Stop the subscriber */
- pg_log_info("stopping the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "stopping the subscriber");
stop_standby_server(subscriber_dir);
/* Change system identifier from subscriber */
@@ -2619,7 +2829,8 @@ main(int argc, char **argv)
success = true;
- pg_log_info("Done!");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "Done!");
return 0;
}
--
2.47.3
[application/octet-stream] v14-0002-Add-a-new-argument-l-logdir-to-pg_createsubscrib.patch (15.5K, 3-v14-0002-Add-a-new-argument-l-logdir-to-pg_createsubscrib.patch)
download | inline diff:
From 8d1547905824e4a9299b11bb8de2a9ff93b3ea83 Mon Sep 17 00:00:00 2001
From: Gyan Sreejith <[email protected]>
Date: Thu, 19 Mar 2026 14:08:07 -0400
Subject: [PATCH v14 2/2] Add a new argument -l <logdir> to
pg_createsubscriber.
Enabling the option to write messages to log files in the specified directory.
A new directory is created if required. A subdirectory is created with timestamp as its name, and it will contain two new logfiles:
1. pg_createsubscriber_server.log - captures messages related to starting and stopping the standby server.
2. pg_createsubscriber_internal.log - captures internal diagnostic output from pg_createsubscriber.
For example, if we specify -l abc as an argument, and if the timestamp on running it is 20260119T204317.204, a directory abc is created if it doesn't exist already, with 20260119T204317.204 as its subdirectory and it will contain the two log files pg_createsubscriber_server.log and pg_createsubscriber_internal.log
---
doc/src/sgml/ref/pg_createsubscriber.sgml | 29 +++
src/bin/pg_basebackup/pg_createsubscriber.c | 172 +++++++++++++++++-
.../t/040_pg_createsubscriber.pl | 41 ++++-
3 files changed, 230 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/pg_createsubscriber.sgml b/doc/src/sgml/ref/pg_createsubscriber.sgml
index cf45ff3573d..ff635ba26cb 100644
--- a/doc/src/sgml/ref/pg_createsubscriber.sgml
+++ b/doc/src/sgml/ref/pg_createsubscriber.sgml
@@ -136,6 +136,35 @@ PostgreSQL documentation
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>-l <replaceable class="parameter">directory</replaceable></option></term>
+ <term><option>--logdir=<replaceable class="parameter">directory</replaceable></option></term>
+ <listitem>
+ <para>
+ 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 <application>pg_createsubscriber</application>
+ 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.
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>pg_createsubscriber_server.log</literal> which captures logs
+ related to stopping and starting the standby server,
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>pg_createsubscriber_internal.log</literal> which captures
+ internal diagnostic output (validations, checks, etc.)
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>-n</option></term>
<term><option>--dry-run</option></term>
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index c3df83025d7..b9b8d40a4f6 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -55,10 +55,14 @@
#define INCLUDED_CONF_FILE "pg_createsubscriber.conf"
#define INCLUDED_CONF_FILE_DISABLED INCLUDED_CONF_FILE ".disabled"
+#define SERVER_LOG_FILE_NAME "pg_createsubscriber_server"
+#define INTERNAL_LOG_FILE_NAME "pg_createsubscriber_internal"
+
/* Command-line options */
struct CreateSubscriberOptions
{
char *config_file; /* configuration file */
+ char *log_dir; /* log directory name */
char *pub_conninfo_str; /* publisher connection string */
char *socket_dir; /* directory for Unix-domain socket, if any */
char *sub_port; /* subscriber port number */
@@ -155,8 +159,14 @@ static void get_publisher_databases(struct CreateSubscriberOptions *opt,
static void pg_createsub_log(enum pg_log_level, enum pg_log_part,
const char *pg_restrict fmt,...)
pg_attribute_printf(3, 4);
+static void pg_createsub_log_v(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt, va_list args)
+ pg_attribute_printf(3, 0);
pg_noreturn static void pg_fatal(const char *pg_restrict fmt,...)
pg_attribute_printf(1, 2);
+static void internal_log_file_write(enum pg_log_level level,
+ const char *pg_restrict fmt, va_list args)
+ pg_attribute_printf(2, 0);
#define WAIT_INTERVAL 1 /* 1 second */
@@ -178,6 +188,9 @@ static pg_prng_state prng_state;
static char *pg_ctl_path = NULL;
static char *pg_resetwal_path = NULL;
+static FILE *internal_log_file_fp = NULL; /* File ptr to log all messages to */
+static char logdir[MAXPGPATH]; /* Directory log files are put (if specified) */
+
/* standby / subscriber data directory */
static char *subscriber_dir = NULL;
@@ -186,8 +199,28 @@ static bool standby_running = false;
static bool recovery_params_set = false;
/*
- * Report a message with a given log level
+ * Report a message with a given log level to stderr and log file
+ * (if specified).
*/
+static void
+pg_createsub_log_v(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt, va_list args)
+{
+ if (internal_log_file_fp != NULL)
+ {
+ /* Output to both stderr and the log file */
+ va_list arg_cpy;
+
+ va_copy(arg_cpy, args);
+ pg_log_generic_v(level, part, fmt, arg_cpy);
+ va_end(arg_cpy);
+
+ internal_log_file_write(level, fmt, args);
+ }
+ else
+ pg_log_generic_v(level, part, fmt, args);
+}
+
static void
pg_createsub_log(enum pg_log_level level, enum pg_log_part part,
const char *pg_restrict fmt,...)
@@ -196,7 +229,7 @@ pg_createsub_log(enum pg_log_level level, enum pg_log_part part,
va_start(args, fmt);
- pg_log_generic_v(level, part, fmt, args);
+ pg_createsub_log_v(level, part, fmt, args);
va_end(args);
}
@@ -211,7 +244,7 @@ pg_fatal(const char *pg_restrict fmt,...)
va_start(args, fmt);
- pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, args);
+ pg_createsub_log_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, args);
va_end(args);
@@ -319,6 +352,12 @@ cleanup_objects_atexit(void)
if (standby_running)
stop_standby_server(subscriber_dir);
+
+ if (internal_log_file_fp != NULL)
+ {
+ fclose(internal_log_file_fp);
+ internal_log_file_fp = NULL;
+ }
}
static void
@@ -333,6 +372,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 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"));
@@ -767,6 +807,7 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
bool crc_ok;
struct timeval tv;
+ char *out_file;
char *cmd_str;
pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
@@ -805,8 +846,18 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
"running pg_resetwal on the subscriber");
- cmd_str = psprintf("\"%s\" -D \"%s\" > \"%s\"", pg_resetwal_path,
- subscriber_dir, DEVNULL);
+ /*
+ * Redirecting the output to the logfile if specified. Since the output
+ * would be very short, around one line, we do not provide a separate file
+ * for it; it's done as a part of the server log.
+ */
+ if (opt->log_dir)
+ out_file = psprintf("%s/%s.log", logdir, SERVER_LOG_FILE_NAME);
+ else
+ out_file = DEVNULL;
+
+ cmd_str = psprintf("\"%s\" -D \"%s\" >> \"%s\"", pg_resetwal_path,
+ subscriber_dir, out_file);
pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
"pg_resetwal command is: %s", cmd_str);
@@ -1029,6 +1080,89 @@ server_is_in_recovery(PGconn *conn)
return ret == 0;
}
+static void
+internal_log_file_write(enum pg_log_level level, const char *pg_restrict fmt,
+ va_list args)
+{
+ Assert(internal_log_file_fp);
+
+ /* Do nothing if log level is too low. */
+ if (level < __pg_log_level)
+ return;
+
+ vfprintf(internal_log_file_fp, _(fmt), args);
+
+ fprintf(internal_log_file_fp, "\n");
+ fflush(internal_log_file_fp);
+}
+
+/*
+ * Open a new logfile with proper permissions.
+ * From src/backend/postmaster/syslogger.c
+ */
+static FILE *
+logfile_open(const char *filename, const char *mode)
+{
+ FILE *fh;
+ mode_t oumask;
+
+ oumask = umask((mode_t) ((~(S_IRUSR | S_IWUSR)) & (S_IRWXU | S_IRWXG | S_IRWXO)));
+ fh = fopen(filename, mode);
+ umask(oumask);
+
+ if (fh)
+ {
+ setvbuf(fh, NULL, PG_IOLBF, 0);
+
+#ifdef WIN32
+ /* use CRLF line endings on Windows */
+ _setmode(_fileno(fh), _O_TEXT);
+#endif
+ }
+ else
+ pg_fatal("could not open log file \"%s\": %m",
+ filename);
+
+ return fh;
+}
+
+static void
+make_output_dirs(const char *log_basedir)
+{
+ char timestamp[128];
+ struct timeval tval;
+ time_t now;
+ struct tm tmbuf;
+ 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));
+
+ /* append milliseconds */
+ snprintf(timestamp + strlen(timestamp),
+ sizeof(timestamp) - strlen(timestamp), ".%03u",
+ (unsigned int) (tval.tv_usec / 1000));
+
+ /* Build timestamp directory path */
+ len = snprintf(logdir, MAXPGPATH, "%s/%s", log_basedir, timestamp);
+
+ if (len >= MAXPGPATH)
+ pg_fatal("directory path for log files, %s/%s, is too long",
+ logdir, timestamp);
+
+ /* Create base directory (ignore if exists) */
+ if (mkdir(log_basedir, S_IRWXU) < 0 && errno != EEXIST)
+ pg_fatal("could not create directory \"%s\": %m", log_basedir);
+
+ /* Create BASE_DIR/$timestamp */
+ if (mkdir(logdir, S_IRWXU) < 0)
+ pg_fatal("could not create directory \"%s\": %m", logdir);
+}
+
/*
* Is the primary server ready for logical replication?
*
@@ -1787,6 +1921,9 @@ 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)
+ appendPQExpBuffer(pg_ctl_cmd, " -l \"%s/%s.log\"", logdir, SERVER_LOG_FILE_NAME);
+
pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
"pg_ctl command is: %s", pg_ctl_cmd->data);
rc = system(pg_ctl_cmd->data);
@@ -2357,6 +2494,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'},
@@ -2415,6 +2553,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;
@@ -2445,7 +2584,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)
@@ -2466,6 +2605,10 @@ 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);
+ break;
case 'n':
dry_run = true;
break;
@@ -2613,6 +2756,20 @@ main(int argc, char **argv)
exit(1);
}
+ if (opt.log_dir != NULL)
+ {
+ char *internal_log_file;
+
+ make_output_dirs(opt.log_dir);
+ internal_log_file = psprintf("%s/%s.log", logdir,
+ 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);
+
+ pg_free(internal_log_file);
+ }
+
if (dry_run)
pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
"Executing in dry-run mode.\n"
@@ -2832,5 +2989,8 @@ main(int argc, char **argv)
pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
"Done!");
+ if (internal_log_file_fp != NULL)
+ fclose(internal_log_file_fp);
+
return 0;
}
diff --git a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
index 0c27fca7bb7..d3af1561551 100644
--- a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
+++ b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
@@ -14,6 +14,7 @@ program_version_ok('pg_createsubscriber');
program_options_handling_ok('pg_createsubscriber');
my $datadir = PostgreSQL::Test::Utils::tempdir;
+my $logdir = PostgreSQL::Test::Utils::tempdir;
# Generate a database with a name made of a range of ASCII characters.
# Extracted from 002_pg_upgrade.pl.
@@ -362,9 +363,35 @@ command_ok(
'--subscription' => 'sub2',
'--database' => $db1,
'--database' => $db2,
+ '--logdir' => $logdir,
],
'run pg_createsubscriber --dry-run on node S');
+# Check that the log files were created
+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 $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 $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;
is($node_s->safe_psql('postgres', 'SELECT pg_catalog.pg_is_in_recovery()'),
@@ -444,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);
));
@@ -540,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');
@@ -555,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(
@@ -581,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");
--
2.47.3
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-20 11:37 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
@ 2026-03-20 17:00 ` Gyan Sreejith <[email protected]>
2026-03-21 09:57 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
0 siblings, 1 reply; 55+ messages in thread
From: Gyan Sreejith @ 2026-03-20 17:00 UTC (permalink / raw)
To: Kuroda, Hayato/黒田 隼人 <[email protected]>; +Cc: Amit Kapila <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
Thank you, Kuroda-san and Nisha!
I have fixed everything that Nisha suggested. And I agree that all messages
should be written to both the log file and the terminal.
Thank you,
Gyan
Attachments:
[application/octet-stream] v15-0002-Add-a-new-argument-l-logdir-to-pg_createsubscrib.patch (15.7K, 3-v15-0002-Add-a-new-argument-l-logdir-to-pg_createsubscrib.patch)
download | inline diff:
From e1c62305bda2ae932621d31ee55058b7326122aa Mon Sep 17 00:00:00 2001
From: Gyan Sreejith <[email protected]>
Date: Fri, 20 Mar 2026 12:39:36 -0400
Subject: [PATCH v15 2/2] Add a new argument -l <logdir> to
pg_createsubscriber.
Enabling the option to write messages to log files in the specified directory.
A new directory is created if required. A subdirectory is created with timestamp as its name, and it will contain two new logfiles:
1. pg_createsubscriber_server.log - captures messages related to starting and stopping the standby server.
2. pg_createsubscriber_internal.log - captures internal diagnostic output from pg_createsubscriber.
For example, if we specify -l abc as an argument, and if the timestamp on running it is 20260119T204317.204, a directory abc is created if it doesn't exist already, with 20260119T204317.204 as its subdirectory and it will contain the two log files pg_createsubscriber_server.log and pg_createsubscriber_internal.log
---
doc/src/sgml/ref/pg_createsubscriber.sgml | 29 +++
src/bin/pg_basebackup/pg_createsubscriber.c | 174 +++++++++++++++++-
.../t/040_pg_createsubscriber.pl | 41 ++++-
3 files changed, 232 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/pg_createsubscriber.sgml b/doc/src/sgml/ref/pg_createsubscriber.sgml
index cf45ff3573d..ff635ba26cb 100644
--- a/doc/src/sgml/ref/pg_createsubscriber.sgml
+++ b/doc/src/sgml/ref/pg_createsubscriber.sgml
@@ -136,6 +136,35 @@ PostgreSQL documentation
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>-l <replaceable class="parameter">directory</replaceable></option></term>
+ <term><option>--logdir=<replaceable class="parameter">directory</replaceable></option></term>
+ <listitem>
+ <para>
+ 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 <application>pg_createsubscriber</application>
+ 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.
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>pg_createsubscriber_server.log</literal> which captures logs
+ related to stopping and starting the standby server,
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>pg_createsubscriber_internal.log</literal> which captures
+ internal diagnostic output (validations, checks, etc.)
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>-n</option></term>
<term><option>--dry-run</option></term>
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index c3df83025d7..f2d8cd38abf 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -55,10 +55,14 @@
#define INCLUDED_CONF_FILE "pg_createsubscriber.conf"
#define INCLUDED_CONF_FILE_DISABLED INCLUDED_CONF_FILE ".disabled"
+#define SERVER_LOG_FILE_NAME "pg_createsubscriber_server"
+#define INTERNAL_LOG_FILE_NAME "pg_createsubscriber_internal"
+
/* Command-line options */
struct CreateSubscriberOptions
{
char *config_file; /* configuration file */
+ char *log_dir; /* log directory name */
char *pub_conninfo_str; /* publisher connection string */
char *socket_dir; /* directory for Unix-domain socket, if any */
char *sub_port; /* subscriber port number */
@@ -155,8 +159,14 @@ static void get_publisher_databases(struct CreateSubscriberOptions *opt,
static void pg_createsub_log(enum pg_log_level, enum pg_log_part,
const char *pg_restrict fmt,...)
pg_attribute_printf(3, 4);
+static void pg_createsub_log_v(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt, va_list args)
+ pg_attribute_printf(3, 0);
pg_noreturn static void pg_fatal(const char *pg_restrict fmt,...)
pg_attribute_printf(1, 2);
+static void internal_log_file_write(enum pg_log_level level,
+ const char *pg_restrict fmt, va_list args)
+ pg_attribute_printf(2, 0);
#define WAIT_INTERVAL 1 /* 1 second */
@@ -178,6 +188,9 @@ static pg_prng_state prng_state;
static char *pg_ctl_path = NULL;
static char *pg_resetwal_path = NULL;
+static FILE *internal_log_file_fp = NULL; /* File ptr to log all messages to */
+static char logdir[MAXPGPATH]; /* Directory log files are put (if specified) */
+
/* standby / subscriber data directory */
static char *subscriber_dir = NULL;
@@ -186,8 +199,28 @@ static bool standby_running = false;
static bool recovery_params_set = false;
/*
- * Report a message with a given log level
+ * Report a message with a given log level to stderr and log file
+ * (if specified).
*/
+static void
+pg_createsub_log_v(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt, va_list args)
+{
+ if (internal_log_file_fp != NULL)
+ {
+ /* Output to both stderr and the log file */
+ va_list arg_cpy;
+
+ va_copy(arg_cpy, args);
+ pg_log_generic_v(level, part, fmt, arg_cpy);
+ va_end(arg_cpy);
+
+ internal_log_file_write(level, fmt, args);
+ }
+ else
+ pg_log_generic_v(level, part, fmt, args);
+}
+
static void
pg_createsub_log(enum pg_log_level level, enum pg_log_part part,
const char *pg_restrict fmt,...)
@@ -196,7 +229,7 @@ pg_createsub_log(enum pg_log_level level, enum pg_log_part part,
va_start(args, fmt);
- pg_log_generic_v(level, part, fmt, args);
+ pg_createsub_log_v(level, part, fmt, args);
va_end(args);
}
@@ -211,7 +244,7 @@ pg_fatal(const char *pg_restrict fmt,...)
va_start(args, fmt);
- pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, args);
+ pg_createsub_log_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, args);
va_end(args);
@@ -319,6 +352,12 @@ cleanup_objects_atexit(void)
if (standby_running)
stop_standby_server(subscriber_dir);
+
+ if (internal_log_file_fp != NULL)
+ {
+ fclose(internal_log_file_fp);
+ internal_log_file_fp = NULL;
+ }
}
static void
@@ -333,6 +372,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 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"));
@@ -767,6 +807,7 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
bool crc_ok;
struct timeval tv;
+ char *out_file;
char *cmd_str;
pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
@@ -805,8 +846,20 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
"running pg_resetwal on the subscriber");
- cmd_str = psprintf("\"%s\" -D \"%s\" > \"%s\"", pg_resetwal_path,
- subscriber_dir, DEVNULL);
+ /*
+ * Redirecting the output to the logfile if specified. Since the output
+ * would be very short, around one line, we do not provide a separate file
+ * for it; it's done as a part of the server log.
+ */
+ if (opt->log_dir)
+ out_file = psprintf("%s/%s.log", logdir, SERVER_LOG_FILE_NAME);
+ else
+ out_file = DEVNULL;
+
+ cmd_str = psprintf("\"%s\" -D \"%s\" >> \"%s\"", pg_resetwal_path,
+ subscriber_dir, out_file);
+ if (opt->log_dir)
+ pg_free(out_file);
pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
"pg_resetwal command is: %s", cmd_str);
@@ -823,6 +876,7 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
}
pg_free(cf);
+ pg_free(cmd_str);
}
/*
@@ -1029,6 +1083,89 @@ server_is_in_recovery(PGconn *conn)
return ret == 0;
}
+static void
+internal_log_file_write(enum pg_log_level level, const char *pg_restrict fmt,
+ va_list args)
+{
+ Assert(internal_log_file_fp);
+
+ /* Do nothing if log level is too low. */
+ if (level < __pg_log_level)
+ return;
+
+ vfprintf(internal_log_file_fp, _(fmt), args);
+
+ fprintf(internal_log_file_fp, "\n");
+ fflush(internal_log_file_fp);
+}
+
+/*
+ * Open a new logfile with proper permissions.
+ * From src/backend/postmaster/syslogger.c
+ */
+static FILE *
+logfile_open(const char *filename, const char *mode)
+{
+ FILE *fh;
+ mode_t oumask;
+
+ oumask = umask((mode_t) ((~(S_IRUSR | S_IWUSR)) & (S_IRWXU | S_IRWXG | S_IRWXO)));
+ fh = fopen(filename, mode);
+ umask(oumask);
+
+ if (fh)
+ {
+ setvbuf(fh, NULL, PG_IOLBF, 0);
+
+#ifdef WIN32
+ /* use CRLF line endings on Windows */
+ _setmode(_fileno(fh), _O_TEXT);
+#endif
+ }
+ else
+ pg_fatal("could not open log file \"%s\": %m",
+ filename);
+
+ return fh;
+}
+
+static void
+make_output_dirs(const char *log_basedir)
+{
+ char timestamp[128];
+ struct timeval tval;
+ time_t now;
+ struct tm tmbuf;
+ 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));
+
+ /* append milliseconds */
+ snprintf(timestamp + strlen(timestamp),
+ sizeof(timestamp) - strlen(timestamp), ".%03u",
+ (unsigned int) (tval.tv_usec / 1000));
+
+ /* Build timestamp directory path */
+ len = snprintf(logdir, MAXPGPATH, "%s/%s", log_basedir, timestamp);
+
+ if (len >= MAXPGPATH)
+ pg_fatal("directory path for log files, %s/%s, is too long",
+ logdir, timestamp);
+
+ /* Create base directory (ignore if exists) */
+ if (mkdir(log_basedir, S_IRWXU) < 0 && errno != EEXIST)
+ pg_fatal("could not create directory \"%s\": %m", log_basedir);
+
+ /* Create BASE_DIR/$timestamp */
+ if (mkdir(logdir, S_IRWXU) < 0)
+ pg_fatal("could not create directory \"%s\": %m", logdir);
+}
+
/*
* Is the primary server ready for logical replication?
*
@@ -1787,6 +1924,9 @@ 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)
+ appendPQExpBuffer(pg_ctl_cmd, " -l \"%s/%s.log\"", logdir, SERVER_LOG_FILE_NAME);
+
pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
"pg_ctl command is: %s", pg_ctl_cmd->data);
rc = system(pg_ctl_cmd->data);
@@ -2357,6 +2497,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'},
@@ -2415,6 +2556,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;
@@ -2445,7 +2587,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)
@@ -2466,6 +2608,10 @@ 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);
+ break;
case 'n':
dry_run = true;
break;
@@ -2613,6 +2759,19 @@ main(int argc, char **argv)
exit(1);
}
+ if (opt.log_dir != NULL)
+ {
+ char *internal_log_file;
+
+ make_output_dirs(opt.log_dir);
+ internal_log_file = psprintf("%s/%s.log", logdir,
+ INTERNAL_LOG_FILE_NAME);
+
+ /* logfile_open() will exit if there is an error */
+ internal_log_file_fp = logfile_open(internal_log_file, "a");
+ pg_free(internal_log_file);
+ }
+
if (dry_run)
pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
"Executing in dry-run mode.\n"
@@ -2832,5 +2991,8 @@ main(int argc, char **argv)
pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
"Done!");
+ if (internal_log_file_fp != NULL)
+ fclose(internal_log_file_fp);
+
return 0;
}
diff --git a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
index 0c27fca7bb7..858082c70df 100644
--- a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
+++ b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
@@ -14,6 +14,7 @@ program_version_ok('pg_createsubscriber');
program_options_handling_ok('pg_createsubscriber');
my $datadir = PostgreSQL::Test::Utils::tempdir;
+my $logdir = PostgreSQL::Test::Utils::tempdir;
# Generate a database with a name made of a range of ASCII characters.
# Extracted from 002_pg_upgrade.pl.
@@ -362,9 +363,35 @@ command_ok(
'--subscription' => 'sub2',
'--database' => $db1,
'--database' => $db2,
+ '--logdir' => $logdir,
],
'run pg_createsubscriber --dry-run on node S');
+# Check that the log files were created
+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 $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 $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;
is($node_s->safe_psql('postgres', 'SELECT pg_catalog.pg_is_in_recovery()'),
@@ -444,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);
));
@@ -540,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');
@@ -555,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(
@@ -581,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");
--
2.43.0
[application/octet-stream] v15-0001-pg_createsubscriber-use-own-reporting-functions.patch (55.1K, 4-v15-0001-pg_createsubscriber-use-own-reporting-functions.patch)
download | inline diff:
From ee6b65eb5c777c5063dd1b1cf0b39fd604539da6 Mon Sep 17 00:00:00 2001
From: Hayato Kuroda <[email protected]>
Date: Wed, 18 Mar 2026 20:20:50 +0900
Subject: [PATCH v15 1/2] pg_createsubscriber: use own reporting functions
This commit converts all pg_log_xxx families to call a new reporting function
pg_createsub_log(). Also, pg_fatal() is overwritten to use its own.
This commit changes nothing from the outside, but is needed for the upcoming
commit.
---
src/bin/pg_basebackup/pg_createsubscriber.c | 717 +++++++++++++-------
1 file changed, 464 insertions(+), 253 deletions(-)
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index 2bc84505aab..c3df83025d7 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -31,6 +31,12 @@
#include "fe_utils/version.h"
#include "getopt_long.h"
+/*
+ * For now, pg_createsubscriber does not use common/logging.c; use our own
+ * pg_fatal.
+ */
+#undef pg_fatal
+
#define DEFAULT_SUB_PORT "50432"
#define OBJECTTYPE_PUBLICATIONS 0x0001
@@ -146,6 +152,11 @@ 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 pg_createsub_log(enum pg_log_level, enum pg_log_part,
+ const char *pg_restrict fmt,...)
+ pg_attribute_printf(3, 4);
+pg_noreturn static void pg_fatal(const char *pg_restrict fmt,...)
+ pg_attribute_printf(1, 2);
#define WAIT_INTERVAL 1 /* 1 second */
@@ -174,6 +185,38 @@ static bool recovery_ended = false;
static bool standby_running = false;
static bool recovery_params_set = false;
+/*
+ * Report a message with a given log level
+ */
+static void
+pg_createsub_log(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt,...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+
+ pg_log_generic_v(level, part, fmt, args);
+
+ va_end(args);
+}
+
+/*
+ * Report a fatal error and exit
+ */
+static void
+pg_fatal(const char *pg_restrict fmt,...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+
+ pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, args);
+
+ va_end(args);
+
+ exit(1);
+}
/*
* Clean up objects created by pg_createsubscriber.
@@ -205,7 +248,8 @@ cleanup_objects_atexit(void)
if (durable_rename(conf_filename, conf_filename_disabled) != 0)
{
/* durable_rename() has already logged something. */
- pg_log_warning_hint("A manual removal of the recovery parameters may be required.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "A manual removal of the recovery parameters may be required.");
}
}
@@ -219,9 +263,11 @@ cleanup_objects_atexit(void)
*/
if (recovery_ended)
{
- pg_log_warning("failed after the end of recovery");
- pg_log_warning_hint("The target server cannot be used as a physical replica anymore. "
- "You must recreate the physical replica before continuing.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "failed after the end of recovery");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "The target server cannot be used as a physical replica anymore. "
+ "You must recreate the physical replica before continuing.");
}
for (int i = 0; i < num_dbs; i++)
@@ -251,17 +297,21 @@ cleanup_objects_atexit(void)
*/
if (dbinfo->made_publication)
{
- pg_log_warning("publication \"%s\" created in database \"%s\" on primary was left behind",
- dbinfo->pubname,
- dbinfo->dbname);
- pg_log_warning_hint("Drop this publication before trying again.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "publication \"%s\" created in database \"%s\" on primary was left behind",
+ dbinfo->pubname,
+ dbinfo->dbname);
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop this publication before trying again.");
}
if (dbinfo->made_replslot)
{
- pg_log_warning("replication slot \"%s\" created in database \"%s\" on primary was left behind",
- dbinfo->replslotname,
- dbinfo->dbname);
- pg_log_warning_hint("Drop this replication slot soon to avoid retention of WAL files.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "replication slot \"%s\" created in database \"%s\" on primary was left behind",
+ dbinfo->replslotname,
+ dbinfo->dbname);
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop this replication slot soon to avoid retention of WAL files.");
}
}
}
@@ -342,7 +392,8 @@ get_base_conninfo(const char *conninfo, char **dbname)
conn_opts = PQconninfoParse(conninfo, &errmsg);
if (conn_opts == NULL)
{
- pg_log_error("could not parse connection string: %s", errmsg);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not parse connection string: %s", errmsg);
PQfreemem(errmsg);
return NULL;
}
@@ -426,7 +477,8 @@ get_exec_path(const char *argv0, const char *progname)
progname, full_path, "pg_createsubscriber");
}
- pg_log_debug("%s path is: %s", progname, exec_path);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "%s path is: %s", progname, exec_path);
return exec_path;
}
@@ -443,8 +495,9 @@ check_data_directory(const char *datadir)
uint32 major_version;
char *version_str;
- pg_log_info("checking if directory \"%s\" is a cluster data directory",
- datadir);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "checking if directory \"%s\" is a cluster data directory",
+ datadir);
if (stat(datadir, &statbuf) != 0)
{
@@ -462,9 +515,11 @@ check_data_directory(const char *datadir)
major_version = GET_PG_MAJORVERSION_NUM(get_pg_version(datadir, &version_str));
if (major_version != PG_MAJORVERSION_NUM)
{
- pg_log_error("data directory is of wrong version");
- pg_log_error_detail("File \"%s\" contains \"%s\", which is not compatible with this program's version \"%s\".",
- "PG_VERSION", version_str, PG_MAJORVERSION);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "data directory is of wrong version");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "File \"%s\" contains \"%s\", which is not compatible with this program's version \"%s\".",
+ "PG_VERSION", version_str, PG_MAJORVERSION);
exit(1);
}
}
@@ -547,14 +602,16 @@ store_pub_sub_info(const struct CreateSubscriberOptions *opt,
dbinfo[i].subname = NULL;
/* Other fields will be filled later */
- pg_log_debug("publisher(%d): publication: %s ; replication slot: %s ; connection string: %s", i,
- dbinfo[i].pubname ? dbinfo[i].pubname : "(auto)",
- dbinfo[i].replslotname ? dbinfo[i].replslotname : "(auto)",
- dbinfo[i].pubconninfo);
- pg_log_debug("subscriber(%d): subscription: %s ; connection string: %s, two_phase: %s", i,
- dbinfo[i].subname ? dbinfo[i].subname : "(auto)",
- dbinfo[i].subconninfo,
- dbinfos.two_phase ? "true" : "false");
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher(%d): publication: %s ; replication slot: %s ; connection string: %s", i,
+ dbinfo[i].pubname ? dbinfo[i].pubname : "(auto)",
+ dbinfo[i].replslotname ? dbinfo[i].replslotname : "(auto)",
+ dbinfo[i].pubconninfo);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber(%d): subscription: %s ; connection string: %s, two_phase: %s", i,
+ dbinfo[i].subname ? dbinfo[i].subname : "(auto)",
+ dbinfo[i].subconninfo,
+ dbinfos.two_phase ? "true" : "false");
if (num_pubs > 0)
pubcell = pubcell->next;
@@ -582,8 +639,9 @@ connect_database(const char *conninfo, bool exit_on_error)
conn = PQconnectdb(conninfo);
if (PQstatus(conn) != CONNECTION_OK)
{
- pg_log_error("connection to database failed: %s",
- PQerrorMessage(conn));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "connection to database failed: %s",
+ PQerrorMessage(conn));
PQfinish(conn);
if (exit_on_error)
@@ -595,8 +653,9 @@ connect_database(const char *conninfo, bool exit_on_error)
res = PQexec(conn, ALWAYS_SECURE_SEARCH_PATH_SQL);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not clear \"search_path\": %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not clear \"search_path\": %s",
+ PQresultErrorMessage(res));
PQclear(res);
PQfinish(conn);
@@ -635,27 +694,31 @@ get_primary_sysid(const char *conninfo)
PGresult *res;
uint64 sysid;
- pg_log_info("getting system identifier from publisher");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "getting system identifier from publisher");
conn = connect_database(conninfo, true);
res = PQexec(conn, "SELECT system_identifier FROM pg_catalog.pg_control_system()");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not get system identifier: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not get system identifier: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
if (PQntuples(res) != 1)
{
- pg_log_error("could not get system identifier: got %d rows, expected %d row",
- PQntuples(res), 1);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not get system identifier: got %d rows, expected %d row",
+ PQntuples(res), 1);
disconnect_database(conn, true);
}
sysid = strtou64(PQgetvalue(res, 0, 0), NULL, 10);
- pg_log_info("system identifier is %" PRIu64 " on publisher", sysid);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "system identifier is %" PRIu64 " on publisher", sysid);
PQclear(res);
disconnect_database(conn, false);
@@ -675,7 +738,8 @@ get_standby_sysid(const char *datadir)
bool crc_ok;
uint64 sysid;
- pg_log_info("getting system identifier from subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "getting system identifier from subscriber");
cf = get_controlfile(datadir, &crc_ok);
if (!crc_ok)
@@ -683,7 +747,8 @@ get_standby_sysid(const char *datadir)
sysid = cf->system_identifier;
- pg_log_info("system identifier is %" PRIu64 " on subscriber", sysid);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "system identifier is %" PRIu64 " on subscriber", sysid);
pg_free(cf);
@@ -704,7 +769,8 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
char *cmd_str;
- pg_log_info("modifying system identifier of subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "modifying system identifier of subscriber");
cf = get_controlfile(subscriber_dir, &crc_ok);
if (!crc_ok)
@@ -721,31 +787,37 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
cf->system_identifier |= getpid() & 0xFFF;
if (dry_run)
- pg_log_info("dry-run: would set system identifier to %" PRIu64 " on subscriber",
- cf->system_identifier);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would set system identifier to %" PRIu64 " on subscriber",
+ cf->system_identifier);
else
{
update_controlfile(subscriber_dir, cf, true);
- pg_log_info("system identifier is %" PRIu64 " on subscriber",
- cf->system_identifier);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "system identifier is %" PRIu64 " on subscriber",
+ cf->system_identifier);
}
if (dry_run)
- pg_log_info("dry-run: would run pg_resetwal on the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would run pg_resetwal on the subscriber");
else
- pg_log_info("running pg_resetwal on the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "running pg_resetwal on the subscriber");
cmd_str = psprintf("\"%s\" -D \"%s\" > \"%s\"", pg_resetwal_path,
subscriber_dir, DEVNULL);
- pg_log_debug("pg_resetwal command is: %s", cmd_str);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "pg_resetwal command is: %s", cmd_str);
if (!dry_run)
{
int rc = system(cmd_str);
if (rc == 0)
- pg_log_info("successfully reset WAL on the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "successfully reset WAL on the subscriber");
else
pg_fatal("could not reset WAL on subscriber: %s", wait_result_to_str(rc));
}
@@ -771,15 +843,17 @@ generate_object_name(PGconn *conn)
"WHERE datname = pg_catalog.current_database()");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain database OID: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain database OID: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
if (PQntuples(res) != 1)
{
- pg_log_error("could not obtain database OID: got %d rows, expected %d row",
- PQntuples(res), 1);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain database OID: got %d rows, expected %d row",
+ PQntuples(res), 1);
disconnect_database(conn, true);
}
@@ -819,8 +893,9 @@ find_publication(PGconn *conn, const char *pubname, const char *dbname)
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not find publication \"%s\" in database \"%s\": %s",
- pubname, dbname, PQerrorMessage(conn));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not find publication \"%s\" in database \"%s\": %s",
+ pubname, dbname, PQerrorMessage(conn));
disconnect_database(conn, true);
}
@@ -873,8 +948,9 @@ setup_publisher(struct LogicalRepInfo *dbinfo)
if (find_publication(conn, dbinfo[i].pubname, dbinfo[i].dbname))
{
/* Reuse existing publication on publisher. */
- pg_log_info("use existing publication \"%s\" in database \"%s\"",
- dbinfo[i].pubname, dbinfo[i].dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "use existing publication \"%s\" in database \"%s\"",
+ dbinfo[i].pubname, dbinfo[i].dbname);
/* Don't remove pre-existing publication if an error occurs. */
dbinfo[i].made_publication = false;
}
@@ -912,8 +988,9 @@ setup_publisher(struct LogicalRepInfo *dbinfo)
res = PQexec(conn, "SELECT pg_log_standby_snapshot()");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not write an additional WAL record: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not write an additional WAL record: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
PQclear(res);
@@ -938,8 +1015,9 @@ server_is_in_recovery(PGconn *conn)
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain recovery progress: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain recovery progress: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -971,7 +1049,8 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
int max_prepared_transactions;
char *max_slot_wal_keep_size;
- pg_log_info("checking settings on publisher");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "checking settings on publisher");
conn = connect_database(dbinfo[0].pubconninfo, true);
@@ -981,7 +1060,8 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
*/
if (server_is_in_recovery(conn))
{
- pg_log_error("primary server cannot be in recovery");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "primary server cannot be in recovery");
disconnect_database(conn, true);
}
@@ -1007,8 +1087,9 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain publisher settings: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain publisher settings: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1022,48 +1103,63 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
PQclear(res);
- pg_log_debug("publisher: wal_level: %s", wal_level);
- pg_log_debug("publisher: max_replication_slots: %d", max_repslots);
- pg_log_debug("publisher: current replication slots: %d", cur_repslots);
- pg_log_debug("publisher: max_wal_senders: %d", max_walsenders);
- pg_log_debug("publisher: current wal senders: %d", cur_walsenders);
- pg_log_debug("publisher: max_prepared_transactions: %d",
- max_prepared_transactions);
- pg_log_debug("publisher: max_slot_wal_keep_size: %s",
- max_slot_wal_keep_size);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: wal_level: %s", wal_level);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: max_replication_slots: %d", max_repslots);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: current replication slots: %d", cur_repslots);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: max_wal_senders: %d", max_walsenders);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: current wal senders: %d", cur_walsenders);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: max_prepared_transactions: %d",
+ max_prepared_transactions);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: max_slot_wal_keep_size: %s",
+ max_slot_wal_keep_size);
disconnect_database(conn, false);
if (strcmp(wal_level, "minimal") == 0)
{
- pg_log_error("publisher requires \"wal_level\" >= \"replica\"");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "publisher requires \"wal_level\" >= \"replica\"");
failed = true;
}
if (max_repslots - cur_repslots < num_dbs)
{
- pg_log_error("publisher requires %d replication slots, but only %d remain",
- num_dbs, max_repslots - cur_repslots);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_replication_slots", cur_repslots + num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "publisher requires %d replication slots, but only %d remain",
+ num_dbs, max_repslots - cur_repslots);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_replication_slots", cur_repslots + num_dbs);
failed = true;
}
if (max_walsenders - cur_walsenders < num_dbs)
{
- pg_log_error("publisher requires %d WAL sender processes, but only %d remain",
- num_dbs, max_walsenders - cur_walsenders);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_wal_senders", cur_walsenders + num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "publisher requires %d WAL sender processes, but only %d remain",
+ num_dbs, max_walsenders - cur_walsenders);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_wal_senders", cur_walsenders + num_dbs);
failed = true;
}
if (max_prepared_transactions != 0 && !dbinfos.two_phase)
{
- pg_log_warning("two_phase option will not be enabled for replication slots");
- pg_log_warning_detail("Subscriptions will be created with the two_phase option disabled. "
- "Prepared transactions will be replicated at COMMIT PREPARED.");
- pg_log_warning_hint("You can use the command-line option --enable-two-phase to enable two_phase.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "two_phase option will not be enabled for replication slots");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_DETAIL,
+ "Subscriptions will be created with the two_phase option disabled. "
+ "Prepared transactions will be replicated at COMMIT PREPARED.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "You can use the command-line option --enable-two-phase to enable two_phase.");
}
/*
@@ -1073,9 +1169,11 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
*/
if (dry_run && (strcmp(max_slot_wal_keep_size, "-1") != 0))
{
- pg_log_warning("required WAL could be removed from the publisher");
- pg_log_warning_hint("Set the configuration parameter \"%s\" to -1 to ensure that required WAL files are not prematurely removed.",
- "max_slot_wal_keep_size");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "required WAL could be removed from the publisher");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Set the configuration parameter \"%s\" to -1 to ensure that required WAL files are not prematurely removed.",
+ "max_slot_wal_keep_size");
}
pg_free(wal_level);
@@ -1106,14 +1204,16 @@ check_subscriber(const struct LogicalRepInfo *dbinfo)
int max_replorigins;
int max_wprocs;
- pg_log_info("checking settings on subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "checking settings on subscriber");
conn = connect_database(dbinfo[0].subconninfo, true);
/* The target server must be a standby */
if (!server_is_in_recovery(conn))
{
- pg_log_error("target server must be a standby");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "target server must be a standby");
disconnect_database(conn, true);
}
@@ -1137,8 +1237,9 @@ check_subscriber(const struct LogicalRepInfo *dbinfo)
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain subscriber settings: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain subscriber settings: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1148,12 +1249,16 @@ check_subscriber(const struct LogicalRepInfo *dbinfo)
if (strcmp(PQgetvalue(res, 3, 0), "") != 0)
primary_slot_name = pg_strdup(PQgetvalue(res, 3, 0));
- pg_log_debug("subscriber: max_logical_replication_workers: %d",
- max_lrworkers);
- pg_log_debug("subscriber: max_active_replication_origins: %d", max_replorigins);
- pg_log_debug("subscriber: max_worker_processes: %d", max_wprocs);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber: max_logical_replication_workers: %d",
+ max_lrworkers);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber: max_active_replication_origins: %d", max_replorigins);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber: max_worker_processes: %d", max_wprocs);
if (primary_slot_name)
- pg_log_debug("subscriber: primary_slot_name: %s", primary_slot_name);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber: primary_slot_name: %s", primary_slot_name);
PQclear(res);
@@ -1161,28 +1266,34 @@ check_subscriber(const struct LogicalRepInfo *dbinfo)
if (max_replorigins < num_dbs)
{
- pg_log_error("subscriber requires %d active replication origins, but only %d remain",
- num_dbs, max_replorigins);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_active_replication_origins", num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "subscriber requires %d active replication origins, but only %d remain",
+ num_dbs, max_replorigins);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_active_replication_origins", num_dbs);
failed = true;
}
if (max_lrworkers < num_dbs)
{
- pg_log_error("subscriber requires %d logical replication workers, but only %d remain",
- num_dbs, max_lrworkers);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_logical_replication_workers", num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "subscriber requires %d logical replication workers, but only %d remain",
+ num_dbs, max_lrworkers);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_logical_replication_workers", num_dbs);
failed = true;
}
if (max_wprocs < num_dbs + 1)
{
- pg_log_error("subscriber requires %d worker processes, but only %d remain",
- num_dbs + 1, max_wprocs);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_worker_processes", num_dbs + 1);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "subscriber requires %d worker processes, but only %d remain",
+ num_dbs + 1, max_wprocs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_worker_processes", num_dbs + 1);
failed = true;
}
@@ -1215,19 +1326,22 @@ drop_existing_subscription(PGconn *conn, const char *subname, const char *dbname
appendPQExpBuffer(query, " DROP SUBSCRIPTION %s;", subname);
if (dry_run)
- pg_log_info("dry-run: would drop subscription \"%s\" in database \"%s\"",
- subname, dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would drop subscription \"%s\" in database \"%s\"",
+ subname, dbname);
else
{
- pg_log_info("dropping subscription \"%s\" in database \"%s\"",
- subname, dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dropping subscription \"%s\" in database \"%s\"",
+ subname, dbname);
res = PQexec(conn, query->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not drop subscription \"%s\": %s",
- subname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not drop subscription \"%s\": %s",
+ subname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1261,8 +1375,9 @@ check_and_drop_existing_subscriptions(PGconn *conn,
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain pre-existing subscriptions: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain pre-existing subscriptions: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1373,7 +1488,8 @@ setup_recovery(const struct LogicalRepInfo *dbinfo, const char *datadir, const c
lsn);
}
- pg_log_debug("recovery parameters:\n%s", recoveryconfcontents->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "recovery parameters:\n%s", recoveryconfcontents->data);
if (!dry_run)
{
@@ -1427,9 +1543,11 @@ drop_primary_replication_slot(struct LogicalRepInfo *dbinfo, const char *slotnam
}
else
{
- pg_log_warning("could not drop replication slot \"%s\" on primary",
- slotname);
- pg_log_warning_hint("Drop this replication slot soon to avoid retention of WAL files.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "could not drop replication slot \"%s\" on primary",
+ slotname);
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop this replication slot soon to avoid retention of WAL files.");
}
}
@@ -1461,9 +1579,11 @@ drop_failover_replication_slots(struct LogicalRepInfo *dbinfo)
}
else
{
- pg_log_warning("could not obtain failover replication slot information: %s",
- PQresultErrorMessage(res));
- pg_log_warning_hint("Drop the failover replication slots on subscriber soon to avoid retention of WAL files.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "could not obtain failover replication slot information: %s",
+ PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop the failover replication slots on subscriber soon to avoid retention of WAL files.");
}
PQclear(res);
@@ -1471,8 +1591,10 @@ drop_failover_replication_slots(struct LogicalRepInfo *dbinfo)
}
else
{
- pg_log_warning("could not drop failover replication slot");
- pg_log_warning_hint("Drop the failover replication slots on subscriber soon to avoid retention of WAL files.");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "could not drop failover replication slot");
+ pg_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop the failover replication slots on subscriber soon to avoid retention of WAL files.");
}
}
@@ -1494,11 +1616,13 @@ create_logical_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo)
Assert(conn != NULL);
if (dry_run)
- pg_log_info("dry-run: would create the replication slot \"%s\" in database \"%s\" on publisher",
- slot_name, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would create the replication slot \"%s\" in database \"%s\" on publisher",
+ slot_name, dbinfo->dbname);
else
- pg_log_info("creating the replication slot \"%s\" in database \"%s\" on publisher",
- slot_name, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "creating the replication slot \"%s\" in database \"%s\" on publisher",
+ slot_name, dbinfo->dbname);
slot_name_esc = PQescapeLiteral(conn, slot_name, strlen(slot_name));
@@ -1509,16 +1633,18 @@ create_logical_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo)
PQfreemem(slot_name_esc);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not create replication slot \"%s\" in database \"%s\": %s",
- slot_name, dbinfo->dbname,
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not create replication slot \"%s\" in database \"%s\": %s",
+ slot_name, dbinfo->dbname,
+ PQresultErrorMessage(res));
PQclear(res);
destroyPQExpBuffer(str);
return NULL;
@@ -1547,11 +1673,13 @@ drop_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo,
Assert(conn != NULL);
if (dry_run)
- pg_log_info("dry-run: would drop the replication slot \"%s\" in database \"%s\"",
- slot_name, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would drop the replication slot \"%s\" in database \"%s\"",
+ slot_name, dbinfo->dbname);
else
- pg_log_info("dropping the replication slot \"%s\" in database \"%s\"",
- slot_name, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dropping the replication slot \"%s\" in database \"%s\"",
+ slot_name, dbinfo->dbname);
slot_name_esc = PQescapeLiteral(conn, slot_name, strlen(slot_name));
@@ -1559,15 +1687,17 @@ drop_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo,
PQfreemem(slot_name_esc);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not drop replication slot \"%s\" in database \"%s\": %s",
- slot_name, dbinfo->dbname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not drop replication slot \"%s\" in database \"%s\": %s",
+ slot_name, dbinfo->dbname, PQresultErrorMessage(res));
dbinfo->made_replslot = false; /* don't try again. */
}
@@ -1587,25 +1717,32 @@ pg_ctl_status(const char *pg_ctl_cmd, int rc)
{
if (WIFEXITED(rc))
{
- pg_log_error("pg_ctl failed with exit code %d", WEXITSTATUS(rc));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "pg_ctl failed with exit code %d",
+ WEXITSTATUS(rc));
}
else if (WIFSIGNALED(rc))
{
#if defined(WIN32)
- pg_log_error("pg_ctl was terminated by exception 0x%X",
- WTERMSIG(rc));
- pg_log_error_detail("See C include file \"ntstatus.h\" for a description of the hexadecimal value.");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "pg_ctl was terminated by exception 0x%X",
+ WTERMSIG(rc));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "See C include file \"ntstatus.h\" for a description of the hexadecimal value.");
#else
- pg_log_error("pg_ctl was terminated by signal %d: %s",
- WTERMSIG(rc), pg_strsignal(WTERMSIG(rc)));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "pg_ctl was terminated by signal %d: %s",
+ WTERMSIG(rc), pg_strsignal(WTERMSIG(rc)));
#endif
}
else
{
- pg_log_error("pg_ctl exited with unrecognized status %d", rc);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "pg_ctl exited with unrecognized status %d", rc);
}
- pg_log_error_detail("The failed command was: %s", pg_ctl_cmd);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "The failed command was: %s", pg_ctl_cmd);
exit(1);
}
}
@@ -1650,12 +1787,14 @@ start_standby_server(const struct CreateSubscriberOptions *opt, bool restricted_
if (restrict_logical_worker)
appendPQExpBufferStr(pg_ctl_cmd, " -o \"-c max_logical_replication_workers=0\"");
- pg_log_debug("pg_ctl command is: %s", pg_ctl_cmd->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "pg_ctl command is: %s", pg_ctl_cmd->data);
rc = system(pg_ctl_cmd->data);
pg_ctl_status(pg_ctl_cmd->data, rc);
standby_running = true;
destroyPQExpBuffer(pg_ctl_cmd);
- pg_log_info("server was started");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "server was started");
}
static void
@@ -1666,11 +1805,13 @@ stop_standby_server(const char *datadir)
pg_ctl_cmd = psprintf("\"%s\" stop -D \"%s\" -s", pg_ctl_path,
datadir);
- pg_log_debug("pg_ctl command is: %s", pg_ctl_cmd);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "pg_ctl command is: %s", pg_ctl_cmd);
rc = system(pg_ctl_cmd);
pg_ctl_status(pg_ctl_cmd, rc);
standby_running = false;
- pg_log_info("server was stopped");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "server was stopped");
}
/*
@@ -1689,7 +1830,8 @@ wait_for_end_recovery(const char *conninfo, const struct CreateSubscriberOptions
bool ready = false;
int timer = 0;
- pg_log_info("waiting for the target server to reach the consistent state");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "waiting for the target server to reach the consistent state");
conn = connect_database(conninfo, true);
@@ -1707,7 +1849,8 @@ wait_for_end_recovery(const char *conninfo, const struct CreateSubscriberOptions
if (opt->recovery_timeout > 0 && timer >= opt->recovery_timeout)
{
stop_standby_server(subscriber_dir);
- pg_log_error("recovery timed out");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "recovery timed out");
disconnect_database(conn, true);
}
@@ -1721,8 +1864,10 @@ wait_for_end_recovery(const char *conninfo, const struct CreateSubscriberOptions
if (!ready)
pg_fatal("server did not end recovery");
- pg_log_info("target server reached the consistent state");
- pg_log_info_hint("If pg_createsubscriber fails after this point, you must recreate the physical replica before continuing.");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "target server reached the consistent state");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_HINT,
+ "If pg_createsubscriber fails after this point, you must recreate the physical replica before continuing.");
}
/*
@@ -1749,8 +1894,9 @@ create_publication(PGconn *conn, struct LogicalRepInfo *dbinfo)
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain publication information: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain publication information: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1763,8 +1909,10 @@ create_publication(PGconn *conn, struct LogicalRepInfo *dbinfo)
* pg_createsubscriber_ prefix followed by the exact database oid and
* a random number.
*/
- pg_log_error("publication \"%s\" already exists", dbinfo->pubname);
- pg_log_error_hint("Consider renaming this publication before continuing.");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "publication \"%s\" already exists", dbinfo->pubname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Consider renaming this publication before continuing.");
disconnect_database(conn, true);
}
@@ -1772,24 +1920,28 @@ create_publication(PGconn *conn, struct LogicalRepInfo *dbinfo)
resetPQExpBuffer(str);
if (dry_run)
- pg_log_info("dry-run: would create publication \"%s\" in database \"%s\"",
- dbinfo->pubname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would create publication \"%s\" in database \"%s\"",
+ dbinfo->pubname, dbinfo->dbname);
else
- pg_log_info("creating publication \"%s\" in database \"%s\"",
- dbinfo->pubname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "creating publication \"%s\" in database \"%s\"",
+ dbinfo->pubname, dbinfo->dbname);
appendPQExpBuffer(str, "CREATE PUBLICATION %s FOR ALL TABLES",
ipubname_esc);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not create publication \"%s\" in database \"%s\": %s",
- dbinfo->pubname, dbinfo->dbname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not create publication \"%s\" in database \"%s\": %s",
+ dbinfo->pubname, dbinfo->dbname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
PQclear(res);
@@ -1819,25 +1971,29 @@ drop_publication(PGconn *conn, const char *pubname, const char *dbname,
pubname_esc = PQescapeIdentifier(conn, pubname, strlen(pubname));
if (dry_run)
- pg_log_info("dry-run: would drop publication \"%s\" in database \"%s\"",
- pubname, dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would drop publication \"%s\" in database \"%s\"",
+ pubname, dbname);
else
- pg_log_info("dropping publication \"%s\" in database \"%s\"",
- pubname, dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dropping publication \"%s\" in database \"%s\"",
+ pubname, dbname);
appendPQExpBuffer(str, "DROP PUBLICATION %s", pubname_esc);
PQfreemem(pubname_esc);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not drop publication \"%s\" in database \"%s\": %s",
- pubname, dbname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not drop publication \"%s\" in database \"%s\": %s",
+ pubname, dbname, PQresultErrorMessage(res));
*made_publication = false; /* don't try again. */
/*
@@ -1872,15 +2028,17 @@ check_and_drop_publications(PGconn *conn, struct LogicalRepInfo *dbinfo)
if (drop_all_pubs)
{
- pg_log_info("dropping all existing publications in database \"%s\"",
- dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dropping all existing publications in database \"%s\"",
+ dbinfo->dbname);
/* Fetch all publication names */
res = PQexec(conn, "SELECT pubname FROM pg_catalog.pg_publication;");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain publication information: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain publication information: %s",
+ PQresultErrorMessage(res));
PQclear(res);
disconnect_database(conn, true);
}
@@ -1903,11 +2061,13 @@ check_and_drop_publications(PGconn *conn, struct LogicalRepInfo *dbinfo)
else
{
if (dry_run)
- pg_log_info("dry-run: would preserve existing publication \"%s\" in database \"%s\"",
- dbinfo->pubname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would preserve existing publication \"%s\" in database \"%s\"",
+ dbinfo->pubname, dbinfo->dbname);
else
- pg_log_info("preserve existing publication \"%s\" in database \"%s\"",
- dbinfo->pubname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "preserve existing publication \"%s\" in database \"%s\"",
+ dbinfo->pubname, dbinfo->dbname);
}
}
}
@@ -1941,11 +2101,13 @@ create_subscription(PGconn *conn, const struct LogicalRepInfo *dbinfo)
replslotname_esc = PQescapeLiteral(conn, dbinfo->replslotname, strlen(dbinfo->replslotname));
if (dry_run)
- pg_log_info("dry-run: would create subscription \"%s\" in database \"%s\"",
- dbinfo->subname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would create subscription \"%s\" in database \"%s\"",
+ dbinfo->subname, dbinfo->dbname);
else
- pg_log_info("creating subscription \"%s\" in database \"%s\"",
- dbinfo->subname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "creating subscription \"%s\" in database \"%s\"",
+ dbinfo->subname, dbinfo->dbname);
appendPQExpBuffer(str,
"CREATE SUBSCRIPTION %s CONNECTION %s PUBLICATION %s "
@@ -1959,15 +2121,17 @@ create_subscription(PGconn *conn, const struct LogicalRepInfo *dbinfo)
PQfreemem(pubconninfo_esc);
PQfreemem(replslotname_esc);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not create subscription \"%s\" in database \"%s\": %s",
- dbinfo->subname, dbinfo->dbname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not create subscription \"%s\" in database \"%s\": %s",
+ dbinfo->subname, dbinfo->dbname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
PQclear(res);
@@ -2011,15 +2175,17 @@ set_replication_progress(PGconn *conn, const struct LogicalRepInfo *dbinfo, cons
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain subscription OID: %s",
- PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain subscription OID: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
if (PQntuples(res) != 1 && !dry_run)
{
- pg_log_error("could not obtain subscription OID: got %d rows, expected %d row",
- PQntuples(res), 1);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain subscription OID: got %d rows, expected %d row",
+ PQntuples(res), 1);
disconnect_database(conn, true);
}
@@ -2043,26 +2209,30 @@ set_replication_progress(PGconn *conn, const struct LogicalRepInfo *dbinfo, cons
originname = psprintf("pg_%u", suboid);
if (dry_run)
- pg_log_info("dry-run: would set the replication progress (node name \"%s\", LSN %s) in database \"%s\"",
- originname, lsnstr, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would set the replication progress (node name \"%s\", LSN %s) in database \"%s\"",
+ originname, lsnstr, dbinfo->dbname);
else
- pg_log_info("setting the replication progress (node name \"%s\", LSN %s) in database \"%s\"",
- originname, lsnstr, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "setting the replication progress (node name \"%s\", LSN %s) in database \"%s\"",
+ originname, lsnstr, dbinfo->dbname);
resetPQExpBuffer(str);
appendPQExpBuffer(str,
"SELECT pg_catalog.pg_replication_origin_advance('%s', '%s')",
originname, lsnstr);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not set replication progress for subscription \"%s\": %s",
- dbinfo->subname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not set replication progress for subscription \"%s\": %s",
+ dbinfo->subname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
PQclear(res);
@@ -2093,23 +2263,27 @@ enable_subscription(PGconn *conn, const struct LogicalRepInfo *dbinfo)
subname = PQescapeIdentifier(conn, dbinfo->subname, strlen(dbinfo->subname));
if (dry_run)
- pg_log_info("dry-run: would enable subscription \"%s\" in database \"%s\"",
- dbinfo->subname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would enable subscription \"%s\" in database \"%s\"",
+ dbinfo->subname, dbinfo->dbname);
else
- pg_log_info("enabling subscription \"%s\" in database \"%s\"",
- dbinfo->subname, dbinfo->dbname);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "enabling subscription \"%s\" in database \"%s\"",
+ dbinfo->subname, dbinfo->dbname);
appendPQExpBuffer(str, "ALTER SUBSCRIPTION %s ENABLE", subname);
- pg_log_debug("command is: %s", str->data);
+ pg_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not enable subscription \"%s\": %s",
- dbinfo->subname, PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not enable subscription \"%s\": %s",
+ dbinfo->subname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -2154,7 +2328,9 @@ get_publisher_databases(struct CreateSubscriberOptions *opt,
res = PQexec(conn, "SELECT datname FROM pg_database WHERE datistemplate = false AND datallowconn AND datconnlimit <> -2 ORDER BY 1");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain a list of databases: %s", PQresultErrorMessage(res));
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain a list of databases: %s",
+ PQresultErrorMessage(res));
PQclear(res);
disconnect_database(conn, true);
}
@@ -2258,9 +2434,11 @@ main(int argc, char **argv)
#ifndef WIN32
if (geteuid() == 0)
{
- pg_log_error("cannot be executed by \"root\"");
- pg_log_error_hint("You must run %s as the PostgreSQL superuser.",
- progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "cannot be executed by \"root\"");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "You must run %s as the PostgreSQL superuser.",
+ progname);
exit(1);
}
#endif
@@ -2351,7 +2529,9 @@ main(int argc, char **argv)
break;
default:
/* getopt_long already emitted a complaint */
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.",
+ progname);
exit(1);
}
}
@@ -2372,9 +2552,12 @@ main(int argc, char **argv)
if (bad_switch)
{
- pg_log_error("options %s and %s cannot be used together",
- bad_switch, "-a/--all");
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "options %s and %s cannot be used together",
+ bad_switch, "-a/--all");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.",
+ progname);
exit(1);
}
}
@@ -2382,17 +2565,21 @@ main(int argc, char **argv)
/* Any non-option arguments? */
if (optind < argc)
{
- pg_log_error("too many command-line arguments (first is \"%s\")",
- argv[optind]);
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "too many command-line arguments (first is \"%s\")",
+ argv[optind]);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.", progname);
exit(1);
}
/* Required arguments */
if (subscriber_dir == NULL)
{
- pg_log_error("no subscriber data directory specified");
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "no subscriber data directory specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.", progname);
exit(1);
}
@@ -2419,22 +2606,27 @@ main(int argc, char **argv)
* identical entries for physical and logical replication. If there is
* not, we would fail anyway.
*/
- pg_log_error("no publisher connection string specified");
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "no publisher connection string specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.", progname);
exit(1);
}
if (dry_run)
- pg_log_info("Executing in dry-run mode.\n"
- "The target directory will not be modified.");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "Executing in dry-run mode.\n"
+ "The target directory will not be modified.");
- pg_log_info("validating publisher connection string");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "validating publisher connection string");
pub_base_conninfo = get_base_conninfo(opt.pub_conninfo_str,
&dbname_conninfo);
if (pub_base_conninfo == NULL)
exit(1);
- pg_log_info("validating subscriber connection string");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "validating subscriber connection string");
sub_base_conninfo = get_sub_conninfo(&opt);
/*
@@ -2451,7 +2643,8 @@ main(int argc, char **argv)
if (opt.database_names.head == NULL)
{
- pg_log_info("no database was specified");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "no database was specified");
/*
* Try to obtain the dbname from the publisher conninfo. If dbname
@@ -2462,14 +2655,17 @@ main(int argc, char **argv)
simple_string_list_append(&opt.database_names, dbname_conninfo);
num_dbs++;
- pg_log_info("database name \"%s\" was extracted from the publisher connection string",
- dbname_conninfo);
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "database name \"%s\" was extracted from the publisher connection string",
+ dbname_conninfo);
}
else
{
- pg_log_error("no database name specified");
- pg_log_error_hint("Try \"%s --help\" for more information.",
- progname);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "no database name specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.",
+ progname);
exit(1);
}
}
@@ -2477,23 +2673,29 @@ main(int argc, char **argv)
/* Number of object names must match number of databases */
if (num_pubs > 0 && num_pubs != num_dbs)
{
- pg_log_error("wrong number of publication names specified");
- pg_log_error_detail("The number of specified publication names (%d) must match the number of specified database names (%d).",
- num_pubs, num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "wrong number of publication names specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "The number of specified publication names (%d) must match the number of specified database names (%d).",
+ num_pubs, num_dbs);
exit(1);
}
if (num_subs > 0 && num_subs != num_dbs)
{
- pg_log_error("wrong number of subscription names specified");
- pg_log_error_detail("The number of specified subscription names (%d) must match the number of specified database names (%d).",
- num_subs, num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "wrong number of subscription names specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "The number of specified subscription names (%d) must match the number of specified database names (%d).",
+ num_subs, num_dbs);
exit(1);
}
if (num_replslots > 0 && num_replslots != num_dbs)
{
- pg_log_error("wrong number of replication slot names specified");
- pg_log_error_detail("The number of specified replication slot names (%d) must match the number of specified database names (%d).",
- num_replslots, num_dbs);
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "wrong number of replication slot names specified");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "The number of specified replication slot names (%d) must match the number of specified database names (%d).",
+ num_replslots, num_dbs);
exit(1);
}
@@ -2504,9 +2706,11 @@ main(int argc, char **argv)
dbinfos.objecttypes_to_clean |= OBJECTTYPE_PUBLICATIONS;
else
{
- pg_log_error("invalid object type \"%s\" specified for %s",
- cell->val, "--clean");
- pg_log_error_hint("The valid value is: \"%s\"", "publications");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "invalid object type \"%s\" specified for %s",
+ cell->val, "--clean");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "The valid value is: \"%s\"", "publications");
exit(1);
}
}
@@ -2550,8 +2754,10 @@ main(int argc, char **argv)
*/
if (stat(pidfile, &statbuf) == 0)
{
- pg_log_error("standby server is running");
- pg_log_error_hint("Stop the standby server and try again.");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "standby server is running");
+ pg_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Stop the standby server and try again.");
exit(1);
}
@@ -2560,7 +2766,8 @@ main(int argc, char **argv)
* by command-line options). The goal is to avoid connections during the
* transformation steps.
*/
- pg_log_info("starting the standby server with command-line options");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "starting the standby server with command-line options");
start_standby_server(&opt, true, false);
/* Check if the standby server is ready for logical replication */
@@ -2576,7 +2783,8 @@ main(int argc, char **argv)
* guarantees it) *before* creating the replication slots in
* setup_publisher().
*/
- pg_log_info("stopping the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "stopping the subscriber");
stop_standby_server(subscriber_dir);
/* Create the required objects for each database on publisher */
@@ -2590,7 +2798,8 @@ main(int argc, char **argv)
* until accepting connections. We don't want to start logical replication
* during setup.
*/
- pg_log_info("starting the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "starting the subscriber");
start_standby_server(&opt, true, true);
/* Waiting the subscriber to be promoted */
@@ -2611,7 +2820,8 @@ main(int argc, char **argv)
drop_failover_replication_slots(dbinfos.dbinfo);
/* Stop the subscriber */
- pg_log_info("stopping the subscriber");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "stopping the subscriber");
stop_standby_server(subscriber_dir);
/* Change system identifier from subscriber */
@@ -2619,7 +2829,8 @@ main(int argc, char **argv)
success = true;
- pg_log_info("Done!");
+ pg_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "Done!");
return 0;
}
--
2.43.0
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-20 11:37 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-20 17:00 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
@ 2026-03-21 09:57 ` Amit Kapila <[email protected]>
2026-03-21 23:09 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
0 siblings, 1 reply; 55+ messages in thread
From: Amit Kapila @ 2026-03-21 09:57 UTC (permalink / raw)
To: Gyan Sreejith <[email protected]>; +Cc: Kuroda, Hayato/黒田 隼人 <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
On Fri, Mar 20, 2026 at 10:30 PM Gyan Sreejith <[email protected]> wrote:
>
0001:
=====
*
+static void pg_createsub_log(enum pg_log_level, enum pg_log_part,
+ const char *pg_restrict fmt,...)
+ pg_attribute_printf(3, 4);
+pg_noreturn static void pg_fatal(const char *pg_restrict fmt,...)
+ pg_attribute_printf(1, 2);
I see similar functions in other modules are named a bit differently.
For example, see report_manifest_error(), report_backup_error(),
report_fatal_error(). Then I see many other error reporting functions
named similarly in code, some examples are: report_invalid_record(),
report_invalid_page(), report_namespace_conflict(), and
report_recovery_conflict().
Based on the above information, can we consider renaming the above
functions to report_createsub_log() and report_createsub_fatal()?
Other than the above point, 0001 LGTM.
--
With Regards,
Amit Kapila.
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-20 11:37 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-20 17:00 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-21 09:57 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
@ 2026-03-21 23:09 ` Gyan Sreejith <[email protected]>
2026-03-23 03:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber shveta malik <[email protected]>
2026-03-23 09:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber shveta malik <[email protected]>
0 siblings, 2 replies; 55+ messages in thread
From: Gyan Sreejith @ 2026-03-21 23:09 UTC (permalink / raw)
To: Amit Kapila <[email protected]>; +Cc: Kuroda, Hayato/黒田 隼人 <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
On Sat, Mar 21, 2026 at 5:57 AM Amit Kapila <[email protected]> wrote:
>
> Based on the above information, can we consider renaming the above
functions to report_createsub_log() and report_createsub_fatal()?
>
> Other than the above point, 0001 LGTM.
>
I have renamed the functions.
Regards,
Gyan
Attachments:
[application/octet-stream] v16-0001-pg_createsubscriber-use-own-reporting-functions.patch (60.2K, 3-v16-0001-pg_createsubscriber-use-own-reporting-functions.patch)
download | inline diff:
From 684c3b44be04ca9341fca8d9d6b7057a422a1092 Mon Sep 17 00:00:00 2001
From: Hayato Kuroda <[email protected]>
Date: Wed, 18 Mar 2026 20:20:50 +0900
Subject: [PATCH v16 1/2] pg_createsubscriber: use own reporting functions
This commit converts all pg_log_xxx families to call a new reporting function
pg_createsub_log(). Also, pg_fatal() is overwritten to use its own.
This commit changes nothing from the outside, but is needed for the upcoming
commit.
---
src/bin/pg_basebackup/pg_createsubscriber.c | 749 +++++++++++++-------
1 file changed, 477 insertions(+), 272 deletions(-)
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index 2bc84505aab..b2bc9dae0b8 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -146,6 +146,11 @@ 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 report_createsub_log(enum pg_log_level, enum pg_log_part,
+ const char *pg_restrict fmt,...)
+ pg_attribute_printf(3, 4);
+pg_noreturn static void report_createsub_fatal(const char *pg_restrict fmt,...)
+ pg_attribute_printf(1, 2);
#define WAIT_INTERVAL 1 /* 1 second */
@@ -174,6 +179,38 @@ static bool recovery_ended = false;
static bool standby_running = false;
static bool recovery_params_set = false;
+/*
+ * Report a message with a given log level
+ */
+static void
+report_createsub_log(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt,...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+
+ pg_log_generic_v(level, part, fmt, args);
+
+ va_end(args);
+}
+
+/*
+ * Report a fatal error and exit
+ */
+static void
+report_createsub_fatal(const char *pg_restrict fmt,...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+
+ pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, args);
+
+ va_end(args);
+
+ exit(1);
+}
/*
* Clean up objects created by pg_createsubscriber.
@@ -205,7 +242,8 @@ cleanup_objects_atexit(void)
if (durable_rename(conf_filename, conf_filename_disabled) != 0)
{
/* durable_rename() has already logged something. */
- pg_log_warning_hint("A manual removal of the recovery parameters may be required.");
+ report_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "A manual removal of the recovery parameters may be required.");
}
}
@@ -219,9 +257,11 @@ cleanup_objects_atexit(void)
*/
if (recovery_ended)
{
- pg_log_warning("failed after the end of recovery");
- pg_log_warning_hint("The target server cannot be used as a physical replica anymore. "
- "You must recreate the physical replica before continuing.");
+ report_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "failed after the end of recovery");
+ report_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "The target server cannot be used as a physical replica anymore. "
+ "You must recreate the physical replica before continuing.");
}
for (int i = 0; i < num_dbs; i++)
@@ -251,17 +291,21 @@ cleanup_objects_atexit(void)
*/
if (dbinfo->made_publication)
{
- pg_log_warning("publication \"%s\" created in database \"%s\" on primary was left behind",
- dbinfo->pubname,
- dbinfo->dbname);
- pg_log_warning_hint("Drop this publication before trying again.");
+ report_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "publication \"%s\" created in database \"%s\" on primary was left behind",
+ dbinfo->pubname,
+ dbinfo->dbname);
+ report_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop this publication before trying again.");
}
if (dbinfo->made_replslot)
{
- pg_log_warning("replication slot \"%s\" created in database \"%s\" on primary was left behind",
- dbinfo->replslotname,
- dbinfo->dbname);
- pg_log_warning_hint("Drop this replication slot soon to avoid retention of WAL files.");
+ report_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "replication slot \"%s\" created in database \"%s\" on primary was left behind",
+ dbinfo->replslotname,
+ dbinfo->dbname);
+ report_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop this replication slot soon to avoid retention of WAL files.");
}
}
}
@@ -342,7 +386,8 @@ get_base_conninfo(const char *conninfo, char **dbname)
conn_opts = PQconninfoParse(conninfo, &errmsg);
if (conn_opts == NULL)
{
- pg_log_error("could not parse connection string: %s", errmsg);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not parse connection string: %s", errmsg);
PQfreemem(errmsg);
return NULL;
}
@@ -419,14 +464,15 @@ get_exec_path(const char *argv0, const char *progname)
strlcpy(full_path, progname, sizeof(full_path));
if (ret == -1)
- pg_fatal("program \"%s\" is needed by %s but was not found in the same directory as \"%s\"",
- progname, "pg_createsubscriber", full_path);
+ report_createsub_fatal("program \"%s\" is needed by %s but was not found in the same directory as \"%s\"",
+ progname, "pg_createsubscriber", full_path);
else
- pg_fatal("program \"%s\" was found by \"%s\" but was not the same version as %s",
- progname, full_path, "pg_createsubscriber");
+ report_createsub_fatal("program \"%s\" was found by \"%s\" but was not the same version as %s",
+ progname, full_path, "pg_createsubscriber");
}
- pg_log_debug("%s path is: %s", progname, exec_path);
+ report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "%s path is: %s", progname, exec_path);
return exec_path;
}
@@ -443,15 +489,16 @@ check_data_directory(const char *datadir)
uint32 major_version;
char *version_str;
- pg_log_info("checking if directory \"%s\" is a cluster data directory",
- datadir);
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "checking if directory \"%s\" is a cluster data directory",
+ datadir);
if (stat(datadir, &statbuf) != 0)
{
if (errno == ENOENT)
- pg_fatal("data directory \"%s\" does not exist", datadir);
+ report_createsub_fatal("data directory \"%s\" does not exist", datadir);
else
- pg_fatal("could not access directory \"%s\": %m", datadir);
+ report_createsub_fatal("could not access directory \"%s\": %m", datadir);
}
/*
@@ -462,9 +509,11 @@ check_data_directory(const char *datadir)
major_version = GET_PG_MAJORVERSION_NUM(get_pg_version(datadir, &version_str));
if (major_version != PG_MAJORVERSION_NUM)
{
- pg_log_error("data directory is of wrong version");
- pg_log_error_detail("File \"%s\" contains \"%s\", which is not compatible with this program's version \"%s\".",
- "PG_VERSION", version_str, PG_MAJORVERSION);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "data directory is of wrong version");
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "File \"%s\" contains \"%s\", which is not compatible with this program's version \"%s\".",
+ "PG_VERSION", version_str, PG_MAJORVERSION);
exit(1);
}
}
@@ -547,14 +596,16 @@ store_pub_sub_info(const struct CreateSubscriberOptions *opt,
dbinfo[i].subname = NULL;
/* Other fields will be filled later */
- pg_log_debug("publisher(%d): publication: %s ; replication slot: %s ; connection string: %s", i,
- dbinfo[i].pubname ? dbinfo[i].pubname : "(auto)",
- dbinfo[i].replslotname ? dbinfo[i].replslotname : "(auto)",
- dbinfo[i].pubconninfo);
- pg_log_debug("subscriber(%d): subscription: %s ; connection string: %s, two_phase: %s", i,
- dbinfo[i].subname ? dbinfo[i].subname : "(auto)",
- dbinfo[i].subconninfo,
- dbinfos.two_phase ? "true" : "false");
+ report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher(%d): publication: %s ; replication slot: %s ; connection string: %s", i,
+ dbinfo[i].pubname ? dbinfo[i].pubname : "(auto)",
+ dbinfo[i].replslotname ? dbinfo[i].replslotname : "(auto)",
+ dbinfo[i].pubconninfo);
+ report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber(%d): subscription: %s ; connection string: %s, two_phase: %s", i,
+ dbinfo[i].subname ? dbinfo[i].subname : "(auto)",
+ dbinfo[i].subconninfo,
+ dbinfos.two_phase ? "true" : "false");
if (num_pubs > 0)
pubcell = pubcell->next;
@@ -582,8 +633,9 @@ connect_database(const char *conninfo, bool exit_on_error)
conn = PQconnectdb(conninfo);
if (PQstatus(conn) != CONNECTION_OK)
{
- pg_log_error("connection to database failed: %s",
- PQerrorMessage(conn));
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "connection to database failed: %s",
+ PQerrorMessage(conn));
PQfinish(conn);
if (exit_on_error)
@@ -595,8 +647,9 @@ connect_database(const char *conninfo, bool exit_on_error)
res = PQexec(conn, ALWAYS_SECURE_SEARCH_PATH_SQL);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not clear \"search_path\": %s",
- PQresultErrorMessage(res));
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not clear \"search_path\": %s",
+ PQresultErrorMessage(res));
PQclear(res);
PQfinish(conn);
@@ -635,27 +688,31 @@ get_primary_sysid(const char *conninfo)
PGresult *res;
uint64 sysid;
- pg_log_info("getting system identifier from publisher");
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "getting system identifier from publisher");
conn = connect_database(conninfo, true);
res = PQexec(conn, "SELECT system_identifier FROM pg_catalog.pg_control_system()");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not get system identifier: %s",
- PQresultErrorMessage(res));
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not get system identifier: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
if (PQntuples(res) != 1)
{
- pg_log_error("could not get system identifier: got %d rows, expected %d row",
- PQntuples(res), 1);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not get system identifier: got %d rows, expected %d row",
+ PQntuples(res), 1);
disconnect_database(conn, true);
}
sysid = strtou64(PQgetvalue(res, 0, 0), NULL, 10);
- pg_log_info("system identifier is %" PRIu64 " on publisher", sysid);
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "system identifier is %" PRIu64 " on publisher", sysid);
PQclear(res);
disconnect_database(conn, false);
@@ -675,15 +732,17 @@ get_standby_sysid(const char *datadir)
bool crc_ok;
uint64 sysid;
- pg_log_info("getting system identifier from subscriber");
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "getting system identifier from subscriber");
cf = get_controlfile(datadir, &crc_ok);
if (!crc_ok)
- pg_fatal("control file appears to be corrupt");
+ report_createsub_fatal("control file appears to be corrupt");
sysid = cf->system_identifier;
- pg_log_info("system identifier is %" PRIu64 " on subscriber", sysid);
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "system identifier is %" PRIu64 " on subscriber", sysid);
pg_free(cf);
@@ -704,11 +763,12 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
char *cmd_str;
- pg_log_info("modifying system identifier of subscriber");
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "modifying system identifier of subscriber");
cf = get_controlfile(subscriber_dir, &crc_ok);
if (!crc_ok)
- pg_fatal("control file appears to be corrupt");
+ report_createsub_fatal("control file appears to be corrupt");
/*
* Select a new system identifier.
@@ -721,33 +781,39 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
cf->system_identifier |= getpid() & 0xFFF;
if (dry_run)
- pg_log_info("dry-run: would set system identifier to %" PRIu64 " on subscriber",
- cf->system_identifier);
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would set system identifier to %" PRIu64 " on subscriber",
+ cf->system_identifier);
else
{
update_controlfile(subscriber_dir, cf, true);
- pg_log_info("system identifier is %" PRIu64 " on subscriber",
- cf->system_identifier);
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "system identifier is %" PRIu64 " on subscriber",
+ cf->system_identifier);
}
if (dry_run)
- pg_log_info("dry-run: would run pg_resetwal on the subscriber");
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would run pg_resetwal on the subscriber");
else
- pg_log_info("running pg_resetwal on the subscriber");
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "running pg_resetwal on the subscriber");
cmd_str = psprintf("\"%s\" -D \"%s\" > \"%s\"", pg_resetwal_path,
subscriber_dir, DEVNULL);
- pg_log_debug("pg_resetwal command is: %s", cmd_str);
+ report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "pg_resetwal command is: %s", cmd_str);
if (!dry_run)
{
int rc = system(cmd_str);
if (rc == 0)
- pg_log_info("successfully reset WAL on the subscriber");
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "successfully reset WAL on the subscriber");
else
- pg_fatal("could not reset WAL on subscriber: %s", wait_result_to_str(rc));
+ report_createsub_fatal("could not reset WAL on subscriber: %s", wait_result_to_str(rc));
}
pg_free(cf);
@@ -771,15 +837,17 @@ generate_object_name(PGconn *conn)
"WHERE datname = pg_catalog.current_database()");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain database OID: %s",
- PQresultErrorMessage(res));
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain database OID: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
if (PQntuples(res) != 1)
{
- pg_log_error("could not obtain database OID: got %d rows, expected %d row",
- PQntuples(res), 1);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain database OID: got %d rows, expected %d row",
+ PQntuples(res), 1);
disconnect_database(conn, true);
}
@@ -819,8 +887,9 @@ find_publication(PGconn *conn, const char *pubname, const char *dbname)
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not find publication \"%s\" in database \"%s\": %s",
- pubname, dbname, PQerrorMessage(conn));
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not find publication \"%s\" in database \"%s\": %s",
+ pubname, dbname, PQerrorMessage(conn));
disconnect_database(conn, true);
}
@@ -873,8 +942,9 @@ setup_publisher(struct LogicalRepInfo *dbinfo)
if (find_publication(conn, dbinfo[i].pubname, dbinfo[i].dbname))
{
/* Reuse existing publication on publisher. */
- pg_log_info("use existing publication \"%s\" in database \"%s\"",
- dbinfo[i].pubname, dbinfo[i].dbname);
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "use existing publication \"%s\" in database \"%s\"",
+ dbinfo[i].pubname, dbinfo[i].dbname);
/* Don't remove pre-existing publication if an error occurs. */
dbinfo[i].made_publication = false;
}
@@ -912,8 +982,9 @@ setup_publisher(struct LogicalRepInfo *dbinfo)
res = PQexec(conn, "SELECT pg_log_standby_snapshot()");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not write an additional WAL record: %s",
- PQresultErrorMessage(res));
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not write an additional WAL record: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
PQclear(res);
@@ -938,8 +1009,9 @@ server_is_in_recovery(PGconn *conn)
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain recovery progress: %s",
- PQresultErrorMessage(res));
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain recovery progress: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -971,7 +1043,8 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
int max_prepared_transactions;
char *max_slot_wal_keep_size;
- pg_log_info("checking settings on publisher");
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "checking settings on publisher");
conn = connect_database(dbinfo[0].pubconninfo, true);
@@ -981,7 +1054,8 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
*/
if (server_is_in_recovery(conn))
{
- pg_log_error("primary server cannot be in recovery");
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "primary server cannot be in recovery");
disconnect_database(conn, true);
}
@@ -1007,8 +1081,9 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain publisher settings: %s",
- PQresultErrorMessage(res));
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain publisher settings: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1022,48 +1097,63 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
PQclear(res);
- pg_log_debug("publisher: wal_level: %s", wal_level);
- pg_log_debug("publisher: max_replication_slots: %d", max_repslots);
- pg_log_debug("publisher: current replication slots: %d", cur_repslots);
- pg_log_debug("publisher: max_wal_senders: %d", max_walsenders);
- pg_log_debug("publisher: current wal senders: %d", cur_walsenders);
- pg_log_debug("publisher: max_prepared_transactions: %d",
- max_prepared_transactions);
- pg_log_debug("publisher: max_slot_wal_keep_size: %s",
- max_slot_wal_keep_size);
+ report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: wal_level: %s", wal_level);
+ report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: max_replication_slots: %d", max_repslots);
+ report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: current replication slots: %d", cur_repslots);
+ report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: max_wal_senders: %d", max_walsenders);
+ report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: current wal senders: %d", cur_walsenders);
+ report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: max_prepared_transactions: %d",
+ max_prepared_transactions);
+ report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "publisher: max_slot_wal_keep_size: %s",
+ max_slot_wal_keep_size);
disconnect_database(conn, false);
if (strcmp(wal_level, "minimal") == 0)
{
- pg_log_error("publisher requires \"wal_level\" >= \"replica\"");
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "publisher requires \"wal_level\" >= \"replica\"");
failed = true;
}
if (max_repslots - cur_repslots < num_dbs)
{
- pg_log_error("publisher requires %d replication slots, but only %d remain",
- num_dbs, max_repslots - cur_repslots);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_replication_slots", cur_repslots + num_dbs);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "publisher requires %d replication slots, but only %d remain",
+ num_dbs, max_repslots - cur_repslots);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_replication_slots", cur_repslots + num_dbs);
failed = true;
}
if (max_walsenders - cur_walsenders < num_dbs)
{
- pg_log_error("publisher requires %d WAL sender processes, but only %d remain",
- num_dbs, max_walsenders - cur_walsenders);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_wal_senders", cur_walsenders + num_dbs);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "publisher requires %d WAL sender processes, but only %d remain",
+ num_dbs, max_walsenders - cur_walsenders);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_wal_senders", cur_walsenders + num_dbs);
failed = true;
}
if (max_prepared_transactions != 0 && !dbinfos.two_phase)
{
- pg_log_warning("two_phase option will not be enabled for replication slots");
- pg_log_warning_detail("Subscriptions will be created with the two_phase option disabled. "
- "Prepared transactions will be replicated at COMMIT PREPARED.");
- pg_log_warning_hint("You can use the command-line option --enable-two-phase to enable two_phase.");
+ report_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "two_phase option will not be enabled for replication slots");
+ report_createsub_log(PG_LOG_WARNING, PG_LOG_DETAIL,
+ "Subscriptions will be created with the two_phase option disabled. "
+ "Prepared transactions will be replicated at COMMIT PREPARED.");
+ report_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "You can use the command-line option --enable-two-phase to enable two_phase.");
}
/*
@@ -1073,9 +1163,11 @@ check_publisher(const struct LogicalRepInfo *dbinfo)
*/
if (dry_run && (strcmp(max_slot_wal_keep_size, "-1") != 0))
{
- pg_log_warning("required WAL could be removed from the publisher");
- pg_log_warning_hint("Set the configuration parameter \"%s\" to -1 to ensure that required WAL files are not prematurely removed.",
- "max_slot_wal_keep_size");
+ report_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "required WAL could be removed from the publisher");
+ report_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Set the configuration parameter \"%s\" to -1 to ensure that required WAL files are not prematurely removed.",
+ "max_slot_wal_keep_size");
}
pg_free(wal_level);
@@ -1106,14 +1198,16 @@ check_subscriber(const struct LogicalRepInfo *dbinfo)
int max_replorigins;
int max_wprocs;
- pg_log_info("checking settings on subscriber");
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "checking settings on subscriber");
conn = connect_database(dbinfo[0].subconninfo, true);
/* The target server must be a standby */
if (!server_is_in_recovery(conn))
{
- pg_log_error("target server must be a standby");
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "target server must be a standby");
disconnect_database(conn, true);
}
@@ -1137,8 +1231,9 @@ check_subscriber(const struct LogicalRepInfo *dbinfo)
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain subscriber settings: %s",
- PQresultErrorMessage(res));
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain subscriber settings: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1148,12 +1243,16 @@ check_subscriber(const struct LogicalRepInfo *dbinfo)
if (strcmp(PQgetvalue(res, 3, 0), "") != 0)
primary_slot_name = pg_strdup(PQgetvalue(res, 3, 0));
- pg_log_debug("subscriber: max_logical_replication_workers: %d",
- max_lrworkers);
- pg_log_debug("subscriber: max_active_replication_origins: %d", max_replorigins);
- pg_log_debug("subscriber: max_worker_processes: %d", max_wprocs);
+ report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber: max_logical_replication_workers: %d",
+ max_lrworkers);
+ report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber: max_active_replication_origins: %d", max_replorigins);
+ report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber: max_worker_processes: %d", max_wprocs);
if (primary_slot_name)
- pg_log_debug("subscriber: primary_slot_name: %s", primary_slot_name);
+ report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "subscriber: primary_slot_name: %s", primary_slot_name);
PQclear(res);
@@ -1161,28 +1260,34 @@ check_subscriber(const struct LogicalRepInfo *dbinfo)
if (max_replorigins < num_dbs)
{
- pg_log_error("subscriber requires %d active replication origins, but only %d remain",
- num_dbs, max_replorigins);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_active_replication_origins", num_dbs);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "subscriber requires %d active replication origins, but only %d remain",
+ num_dbs, max_replorigins);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_active_replication_origins", num_dbs);
failed = true;
}
if (max_lrworkers < num_dbs)
{
- pg_log_error("subscriber requires %d logical replication workers, but only %d remain",
- num_dbs, max_lrworkers);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_logical_replication_workers", num_dbs);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "subscriber requires %d logical replication workers, but only %d remain",
+ num_dbs, max_lrworkers);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_logical_replication_workers", num_dbs);
failed = true;
}
if (max_wprocs < num_dbs + 1)
{
- pg_log_error("subscriber requires %d worker processes, but only %d remain",
- num_dbs + 1, max_wprocs);
- pg_log_error_hint("Increase the configuration parameter \"%s\" to at least %d.",
- "max_worker_processes", num_dbs + 1);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "subscriber requires %d worker processes, but only %d remain",
+ num_dbs + 1, max_wprocs);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Increase the configuration parameter \"%s\" to at least %d.",
+ "max_worker_processes", num_dbs + 1);
failed = true;
}
@@ -1215,19 +1320,22 @@ drop_existing_subscription(PGconn *conn, const char *subname, const char *dbname
appendPQExpBuffer(query, " DROP SUBSCRIPTION %s;", subname);
if (dry_run)
- pg_log_info("dry-run: would drop subscription \"%s\" in database \"%s\"",
- subname, dbname);
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would drop subscription \"%s\" in database \"%s\"",
+ subname, dbname);
else
{
- pg_log_info("dropping subscription \"%s\" in database \"%s\"",
- subname, dbname);
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dropping subscription \"%s\" in database \"%s\"",
+ subname, dbname);
res = PQexec(conn, query->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not drop subscription \"%s\": %s",
- subname, PQresultErrorMessage(res));
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not drop subscription \"%s\": %s",
+ subname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1261,8 +1369,9 @@ check_and_drop_existing_subscriptions(PGconn *conn,
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain pre-existing subscriptions: %s",
- PQresultErrorMessage(res));
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain pre-existing subscriptions: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1373,7 +1482,8 @@ setup_recovery(const struct LogicalRepInfo *dbinfo, const char *datadir, const c
lsn);
}
- pg_log_debug("recovery parameters:\n%s", recoveryconfcontents->data);
+ report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "recovery parameters:\n%s", recoveryconfcontents->data);
if (!dry_run)
{
@@ -1385,10 +1495,10 @@ setup_recovery(const struct LogicalRepInfo *dbinfo, const char *datadir, const c
INCLUDED_CONF_FILE);
fd = fopen(conf_filename, "w");
if (fd == NULL)
- pg_fatal("could not open file \"%s\": %m", conf_filename);
+ report_createsub_fatal("could not open file \"%s\": %m", conf_filename);
if (fwrite(recoveryconfcontents->data, recoveryconfcontents->len, 1, fd) != 1)
- pg_fatal("could not write to file \"%s\": %m", conf_filename);
+ report_createsub_fatal("could not write to file \"%s\": %m", conf_filename);
fclose(fd);
recovery_params_set = true;
@@ -1427,9 +1537,11 @@ drop_primary_replication_slot(struct LogicalRepInfo *dbinfo, const char *slotnam
}
else
{
- pg_log_warning("could not drop replication slot \"%s\" on primary",
- slotname);
- pg_log_warning_hint("Drop this replication slot soon to avoid retention of WAL files.");
+ report_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "could not drop replication slot \"%s\" on primary",
+ slotname);
+ report_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop this replication slot soon to avoid retention of WAL files.");
}
}
@@ -1461,9 +1573,11 @@ drop_failover_replication_slots(struct LogicalRepInfo *dbinfo)
}
else
{
- pg_log_warning("could not obtain failover replication slot information: %s",
- PQresultErrorMessage(res));
- pg_log_warning_hint("Drop the failover replication slots on subscriber soon to avoid retention of WAL files.");
+ report_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "could not obtain failover replication slot information: %s",
+ PQresultErrorMessage(res));
+ report_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop the failover replication slots on subscriber soon to avoid retention of WAL files.");
}
PQclear(res);
@@ -1471,8 +1585,10 @@ drop_failover_replication_slots(struct LogicalRepInfo *dbinfo)
}
else
{
- pg_log_warning("could not drop failover replication slot");
- pg_log_warning_hint("Drop the failover replication slots on subscriber soon to avoid retention of WAL files.");
+ report_createsub_log(PG_LOG_WARNING, PG_LOG_PRIMARY,
+ "could not drop failover replication slot");
+ report_createsub_log(PG_LOG_WARNING, PG_LOG_HINT,
+ "Drop the failover replication slots on subscriber soon to avoid retention of WAL files.");
}
}
@@ -1494,11 +1610,13 @@ create_logical_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo)
Assert(conn != NULL);
if (dry_run)
- pg_log_info("dry-run: would create the replication slot \"%s\" in database \"%s\" on publisher",
- slot_name, dbinfo->dbname);
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would create the replication slot \"%s\" in database \"%s\" on publisher",
+ slot_name, dbinfo->dbname);
else
- pg_log_info("creating the replication slot \"%s\" in database \"%s\" on publisher",
- slot_name, dbinfo->dbname);
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "creating the replication slot \"%s\" in database \"%s\" on publisher",
+ slot_name, dbinfo->dbname);
slot_name_esc = PQescapeLiteral(conn, slot_name, strlen(slot_name));
@@ -1509,16 +1627,18 @@ create_logical_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo)
PQfreemem(slot_name_esc);
- pg_log_debug("command is: %s", str->data);
+ report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not create replication slot \"%s\" in database \"%s\": %s",
- slot_name, dbinfo->dbname,
- PQresultErrorMessage(res));
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not create replication slot \"%s\" in database \"%s\": %s",
+ slot_name, dbinfo->dbname,
+ PQresultErrorMessage(res));
PQclear(res);
destroyPQExpBuffer(str);
return NULL;
@@ -1547,11 +1667,13 @@ drop_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo,
Assert(conn != NULL);
if (dry_run)
- pg_log_info("dry-run: would drop the replication slot \"%s\" in database \"%s\"",
- slot_name, dbinfo->dbname);
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would drop the replication slot \"%s\" in database \"%s\"",
+ slot_name, dbinfo->dbname);
else
- pg_log_info("dropping the replication slot \"%s\" in database \"%s\"",
- slot_name, dbinfo->dbname);
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dropping the replication slot \"%s\" in database \"%s\"",
+ slot_name, dbinfo->dbname);
slot_name_esc = PQescapeLiteral(conn, slot_name, strlen(slot_name));
@@ -1559,15 +1681,17 @@ drop_replication_slot(PGconn *conn, struct LogicalRepInfo *dbinfo,
PQfreemem(slot_name_esc);
- pg_log_debug("command is: %s", str->data);
+ report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not drop replication slot \"%s\" in database \"%s\": %s",
- slot_name, dbinfo->dbname, PQresultErrorMessage(res));
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not drop replication slot \"%s\" in database \"%s\": %s",
+ slot_name, dbinfo->dbname, PQresultErrorMessage(res));
dbinfo->made_replslot = false; /* don't try again. */
}
@@ -1587,25 +1711,32 @@ pg_ctl_status(const char *pg_ctl_cmd, int rc)
{
if (WIFEXITED(rc))
{
- pg_log_error("pg_ctl failed with exit code %d", WEXITSTATUS(rc));
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "pg_ctl failed with exit code %d",
+ WEXITSTATUS(rc));
}
else if (WIFSIGNALED(rc))
{
#if defined(WIN32)
- pg_log_error("pg_ctl was terminated by exception 0x%X",
- WTERMSIG(rc));
- pg_log_error_detail("See C include file \"ntstatus.h\" for a description of the hexadecimal value.");
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "pg_ctl was terminated by exception 0x%X",
+ WTERMSIG(rc));
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "See C include file \"ntstatus.h\" for a description of the hexadecimal value.");
#else
- pg_log_error("pg_ctl was terminated by signal %d: %s",
- WTERMSIG(rc), pg_strsignal(WTERMSIG(rc)));
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "pg_ctl was terminated by signal %d: %s",
+ WTERMSIG(rc), pg_strsignal(WTERMSIG(rc)));
#endif
}
else
{
- pg_log_error("pg_ctl exited with unrecognized status %d", rc);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "pg_ctl exited with unrecognized status %d", rc);
}
- pg_log_error_detail("The failed command was: %s", pg_ctl_cmd);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "The failed command was: %s", pg_ctl_cmd);
exit(1);
}
}
@@ -1650,12 +1781,14 @@ start_standby_server(const struct CreateSubscriberOptions *opt, bool restricted_
if (restrict_logical_worker)
appendPQExpBufferStr(pg_ctl_cmd, " -o \"-c max_logical_replication_workers=0\"");
- pg_log_debug("pg_ctl command is: %s", pg_ctl_cmd->data);
+ report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "pg_ctl command is: %s", pg_ctl_cmd->data);
rc = system(pg_ctl_cmd->data);
pg_ctl_status(pg_ctl_cmd->data, rc);
standby_running = true;
destroyPQExpBuffer(pg_ctl_cmd);
- pg_log_info("server was started");
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "server was started");
}
static void
@@ -1666,11 +1799,13 @@ stop_standby_server(const char *datadir)
pg_ctl_cmd = psprintf("\"%s\" stop -D \"%s\" -s", pg_ctl_path,
datadir);
- pg_log_debug("pg_ctl command is: %s", pg_ctl_cmd);
+ report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "pg_ctl command is: %s", pg_ctl_cmd);
rc = system(pg_ctl_cmd);
pg_ctl_status(pg_ctl_cmd, rc);
standby_running = false;
- pg_log_info("server was stopped");
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "server was stopped");
}
/*
@@ -1689,7 +1824,8 @@ wait_for_end_recovery(const char *conninfo, const struct CreateSubscriberOptions
bool ready = false;
int timer = 0;
- pg_log_info("waiting for the target server to reach the consistent state");
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "waiting for the target server to reach the consistent state");
conn = connect_database(conninfo, true);
@@ -1707,7 +1843,8 @@ wait_for_end_recovery(const char *conninfo, const struct CreateSubscriberOptions
if (opt->recovery_timeout > 0 && timer >= opt->recovery_timeout)
{
stop_standby_server(subscriber_dir);
- pg_log_error("recovery timed out");
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "recovery timed out");
disconnect_database(conn, true);
}
@@ -1719,10 +1856,12 @@ wait_for_end_recovery(const char *conninfo, const struct CreateSubscriberOptions
disconnect_database(conn, false);
if (!ready)
- pg_fatal("server did not end recovery");
+ report_createsub_fatal("server did not end recovery");
- pg_log_info("target server reached the consistent state");
- pg_log_info_hint("If pg_createsubscriber fails after this point, you must recreate the physical replica before continuing.");
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "target server reached the consistent state");
+ report_createsub_log(PG_LOG_INFO, PG_LOG_HINT,
+ "If pg_createsubscriber fails after this point, you must recreate the physical replica before continuing.");
}
/*
@@ -1749,8 +1888,9 @@ create_publication(PGconn *conn, struct LogicalRepInfo *dbinfo)
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain publication information: %s",
- PQresultErrorMessage(res));
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain publication information: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -1763,8 +1903,10 @@ create_publication(PGconn *conn, struct LogicalRepInfo *dbinfo)
* pg_createsubscriber_ prefix followed by the exact database oid and
* a random number.
*/
- pg_log_error("publication \"%s\" already exists", dbinfo->pubname);
- pg_log_error_hint("Consider renaming this publication before continuing.");
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "publication \"%s\" already exists", dbinfo->pubname);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Consider renaming this publication before continuing.");
disconnect_database(conn, true);
}
@@ -1772,24 +1914,28 @@ create_publication(PGconn *conn, struct LogicalRepInfo *dbinfo)
resetPQExpBuffer(str);
if (dry_run)
- pg_log_info("dry-run: would create publication \"%s\" in database \"%s\"",
- dbinfo->pubname, dbinfo->dbname);
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would create publication \"%s\" in database \"%s\"",
+ dbinfo->pubname, dbinfo->dbname);
else
- pg_log_info("creating publication \"%s\" in database \"%s\"",
- dbinfo->pubname, dbinfo->dbname);
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "creating publication \"%s\" in database \"%s\"",
+ dbinfo->pubname, dbinfo->dbname);
appendPQExpBuffer(str, "CREATE PUBLICATION %s FOR ALL TABLES",
ipubname_esc);
- pg_log_debug("command is: %s", str->data);
+ report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not create publication \"%s\" in database \"%s\": %s",
- dbinfo->pubname, dbinfo->dbname, PQresultErrorMessage(res));
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not create publication \"%s\" in database \"%s\": %s",
+ dbinfo->pubname, dbinfo->dbname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
PQclear(res);
@@ -1819,25 +1965,29 @@ drop_publication(PGconn *conn, const char *pubname, const char *dbname,
pubname_esc = PQescapeIdentifier(conn, pubname, strlen(pubname));
if (dry_run)
- pg_log_info("dry-run: would drop publication \"%s\" in database \"%s\"",
- pubname, dbname);
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would drop publication \"%s\" in database \"%s\"",
+ pubname, dbname);
else
- pg_log_info("dropping publication \"%s\" in database \"%s\"",
- pubname, dbname);
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dropping publication \"%s\" in database \"%s\"",
+ pubname, dbname);
appendPQExpBuffer(str, "DROP PUBLICATION %s", pubname_esc);
PQfreemem(pubname_esc);
- pg_log_debug("command is: %s", str->data);
+ report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not drop publication \"%s\" in database \"%s\": %s",
- pubname, dbname, PQresultErrorMessage(res));
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not drop publication \"%s\" in database \"%s\": %s",
+ pubname, dbname, PQresultErrorMessage(res));
*made_publication = false; /* don't try again. */
/*
@@ -1872,15 +2022,17 @@ check_and_drop_publications(PGconn *conn, struct LogicalRepInfo *dbinfo)
if (drop_all_pubs)
{
- pg_log_info("dropping all existing publications in database \"%s\"",
- dbinfo->dbname);
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dropping all existing publications in database \"%s\"",
+ dbinfo->dbname);
/* Fetch all publication names */
res = PQexec(conn, "SELECT pubname FROM pg_catalog.pg_publication;");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain publication information: %s",
- PQresultErrorMessage(res));
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain publication information: %s",
+ PQresultErrorMessage(res));
PQclear(res);
disconnect_database(conn, true);
}
@@ -1903,11 +2055,13 @@ check_and_drop_publications(PGconn *conn, struct LogicalRepInfo *dbinfo)
else
{
if (dry_run)
- pg_log_info("dry-run: would preserve existing publication \"%s\" in database \"%s\"",
- dbinfo->pubname, dbinfo->dbname);
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would preserve existing publication \"%s\" in database \"%s\"",
+ dbinfo->pubname, dbinfo->dbname);
else
- pg_log_info("preserve existing publication \"%s\" in database \"%s\"",
- dbinfo->pubname, dbinfo->dbname);
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "preserve existing publication \"%s\" in database \"%s\"",
+ dbinfo->pubname, dbinfo->dbname);
}
}
}
@@ -1941,11 +2095,13 @@ create_subscription(PGconn *conn, const struct LogicalRepInfo *dbinfo)
replslotname_esc = PQescapeLiteral(conn, dbinfo->replslotname, strlen(dbinfo->replslotname));
if (dry_run)
- pg_log_info("dry-run: would create subscription \"%s\" in database \"%s\"",
- dbinfo->subname, dbinfo->dbname);
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would create subscription \"%s\" in database \"%s\"",
+ dbinfo->subname, dbinfo->dbname);
else
- pg_log_info("creating subscription \"%s\" in database \"%s\"",
- dbinfo->subname, dbinfo->dbname);
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "creating subscription \"%s\" in database \"%s\"",
+ dbinfo->subname, dbinfo->dbname);
appendPQExpBuffer(str,
"CREATE SUBSCRIPTION %s CONNECTION %s PUBLICATION %s "
@@ -1959,15 +2115,17 @@ create_subscription(PGconn *conn, const struct LogicalRepInfo *dbinfo)
PQfreemem(pubconninfo_esc);
PQfreemem(replslotname_esc);
- pg_log_debug("command is: %s", str->data);
+ report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not create subscription \"%s\" in database \"%s\": %s",
- dbinfo->subname, dbinfo->dbname, PQresultErrorMessage(res));
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not create subscription \"%s\" in database \"%s\": %s",
+ dbinfo->subname, dbinfo->dbname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
PQclear(res);
@@ -2011,15 +2169,17 @@ set_replication_progress(PGconn *conn, const struct LogicalRepInfo *dbinfo, cons
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain subscription OID: %s",
- PQresultErrorMessage(res));
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain subscription OID: %s",
+ PQresultErrorMessage(res));
disconnect_database(conn, true);
}
if (PQntuples(res) != 1 && !dry_run)
{
- pg_log_error("could not obtain subscription OID: got %d rows, expected %d row",
- PQntuples(res), 1);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain subscription OID: got %d rows, expected %d row",
+ PQntuples(res), 1);
disconnect_database(conn, true);
}
@@ -2043,26 +2203,30 @@ set_replication_progress(PGconn *conn, const struct LogicalRepInfo *dbinfo, cons
originname = psprintf("pg_%u", suboid);
if (dry_run)
- pg_log_info("dry-run: would set the replication progress (node name \"%s\", LSN %s) in database \"%s\"",
- originname, lsnstr, dbinfo->dbname);
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would set the replication progress (node name \"%s\", LSN %s) in database \"%s\"",
+ originname, lsnstr, dbinfo->dbname);
else
- pg_log_info("setting the replication progress (node name \"%s\", LSN %s) in database \"%s\"",
- originname, lsnstr, dbinfo->dbname);
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "setting the replication progress (node name \"%s\", LSN %s) in database \"%s\"",
+ originname, lsnstr, dbinfo->dbname);
resetPQExpBuffer(str);
appendPQExpBuffer(str,
"SELECT pg_catalog.pg_replication_origin_advance('%s', '%s')",
originname, lsnstr);
- pg_log_debug("command is: %s", str->data);
+ report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not set replication progress for subscription \"%s\": %s",
- dbinfo->subname, PQresultErrorMessage(res));
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not set replication progress for subscription \"%s\": %s",
+ dbinfo->subname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
PQclear(res);
@@ -2093,23 +2257,27 @@ enable_subscription(PGconn *conn, const struct LogicalRepInfo *dbinfo)
subname = PQescapeIdentifier(conn, dbinfo->subname, strlen(dbinfo->subname));
if (dry_run)
- pg_log_info("dry-run: would enable subscription \"%s\" in database \"%s\"",
- dbinfo->subname, dbinfo->dbname);
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "dry-run: would enable subscription \"%s\" in database \"%s\"",
+ dbinfo->subname, dbinfo->dbname);
else
- pg_log_info("enabling subscription \"%s\" in database \"%s\"",
- dbinfo->subname, dbinfo->dbname);
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "enabling subscription \"%s\" in database \"%s\"",
+ dbinfo->subname, dbinfo->dbname);
appendPQExpBuffer(str, "ALTER SUBSCRIPTION %s ENABLE", subname);
- pg_log_debug("command is: %s", str->data);
+ report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
+ "command is: %s", str->data);
if (!dry_run)
{
res = PQexec(conn, str->data);
if (PQresultStatus(res) != PGRES_COMMAND_OK)
{
- pg_log_error("could not enable subscription \"%s\": %s",
- dbinfo->subname, PQresultErrorMessage(res));
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not enable subscription \"%s\": %s",
+ dbinfo->subname, PQresultErrorMessage(res));
disconnect_database(conn, true);
}
@@ -2154,7 +2322,9 @@ get_publisher_databases(struct CreateSubscriberOptions *opt,
res = PQexec(conn, "SELECT datname FROM pg_database WHERE datistemplate = false AND datallowconn AND datconnlimit <> -2 ORDER BY 1");
if (PQresultStatus(res) != PGRES_TUPLES_OK)
{
- pg_log_error("could not obtain a list of databases: %s", PQresultErrorMessage(res));
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "could not obtain a list of databases: %s",
+ PQresultErrorMessage(res));
PQclear(res);
disconnect_database(conn, true);
}
@@ -2258,9 +2428,11 @@ main(int argc, char **argv)
#ifndef WIN32
if (geteuid() == 0)
{
- pg_log_error("cannot be executed by \"root\"");
- pg_log_error_hint("You must run %s as the PostgreSQL superuser.",
- progname);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "cannot be executed by \"root\"");
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "You must run %s as the PostgreSQL superuser.",
+ progname);
exit(1);
}
#endif
@@ -2282,7 +2454,7 @@ main(int argc, char **argv)
num_dbs++;
}
else
- pg_fatal("database \"%s\" specified more than once for -d/--database", optarg);
+ report_createsub_fatal("database \"%s\" specified more than once for -d/--database", optarg);
break;
case 'D':
subscriber_dir = pg_strdup(optarg);
@@ -2323,7 +2495,7 @@ main(int argc, char **argv)
num_pubs++;
}
else
- pg_fatal("publication \"%s\" specified more than once for --publication", optarg);
+ report_createsub_fatal("publication \"%s\" specified more than once for --publication", optarg);
break;
case 3:
if (!simple_string_list_member(&opt.replslot_names, optarg))
@@ -2332,7 +2504,7 @@ main(int argc, char **argv)
num_replslots++;
}
else
- pg_fatal("replication slot \"%s\" specified more than once for --replication-slot", optarg);
+ report_createsub_fatal("replication slot \"%s\" specified more than once for --replication-slot", optarg);
break;
case 4:
if (!simple_string_list_member(&opt.sub_names, optarg))
@@ -2341,17 +2513,19 @@ main(int argc, char **argv)
num_subs++;
}
else
- pg_fatal("subscription \"%s\" specified more than once for --subscription", optarg);
+ report_createsub_fatal("subscription \"%s\" specified more than once for --subscription", optarg);
break;
case 5:
if (!simple_string_list_member(&opt.objecttypes_to_clean, optarg))
simple_string_list_append(&opt.objecttypes_to_clean, optarg);
else
- pg_fatal("object type \"%s\" specified more than once for --clean", optarg);
+ report_createsub_fatal("object type \"%s\" specified more than once for --clean", optarg);
break;
default:
/* getopt_long already emitted a complaint */
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.",
+ progname);
exit(1);
}
}
@@ -2372,9 +2546,12 @@ main(int argc, char **argv)
if (bad_switch)
{
- pg_log_error("options %s and %s cannot be used together",
- bad_switch, "-a/--all");
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "options %s and %s cannot be used together",
+ bad_switch, "-a/--all");
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.",
+ progname);
exit(1);
}
}
@@ -2382,17 +2559,21 @@ main(int argc, char **argv)
/* Any non-option arguments? */
if (optind < argc)
{
- pg_log_error("too many command-line arguments (first is \"%s\")",
- argv[optind]);
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "too many command-line arguments (first is \"%s\")",
+ argv[optind]);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.", progname);
exit(1);
}
/* Required arguments */
if (subscriber_dir == NULL)
{
- pg_log_error("no subscriber data directory specified");
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "no subscriber data directory specified");
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.", progname);
exit(1);
}
@@ -2402,7 +2583,7 @@ main(int argc, char **argv)
char cwd[MAXPGPATH];
if (!getcwd(cwd, MAXPGPATH))
- pg_fatal("could not determine current directory");
+ report_createsub_fatal("could not determine current directory");
opt.socket_dir = pg_strdup(cwd);
canonicalize_path(opt.socket_dir);
}
@@ -2419,22 +2600,27 @@ main(int argc, char **argv)
* identical entries for physical and logical replication. If there is
* not, we would fail anyway.
*/
- pg_log_error("no publisher connection string specified");
- pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "no publisher connection string specified");
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.", progname);
exit(1);
}
if (dry_run)
- pg_log_info("Executing in dry-run mode.\n"
- "The target directory will not be modified.");
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "Executing in dry-run mode.\n"
+ "The target directory will not be modified.");
- pg_log_info("validating publisher connection string");
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "validating publisher connection string");
pub_base_conninfo = get_base_conninfo(opt.pub_conninfo_str,
&dbname_conninfo);
if (pub_base_conninfo == NULL)
exit(1);
- pg_log_info("validating subscriber connection string");
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "validating subscriber connection string");
sub_base_conninfo = get_sub_conninfo(&opt);
/*
@@ -2451,7 +2637,8 @@ main(int argc, char **argv)
if (opt.database_names.head == NULL)
{
- pg_log_info("no database was specified");
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "no database was specified");
/*
* Try to obtain the dbname from the publisher conninfo. If dbname
@@ -2462,14 +2649,17 @@ main(int argc, char **argv)
simple_string_list_append(&opt.database_names, dbname_conninfo);
num_dbs++;
- pg_log_info("database name \"%s\" was extracted from the publisher connection string",
- dbname_conninfo);
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "database name \"%s\" was extracted from the publisher connection string",
+ dbname_conninfo);
}
else
{
- pg_log_error("no database name specified");
- pg_log_error_hint("Try \"%s --help\" for more information.",
- progname);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "no database name specified");
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Try \"%s --help\" for more information.",
+ progname);
exit(1);
}
}
@@ -2477,23 +2667,29 @@ main(int argc, char **argv)
/* Number of object names must match number of databases */
if (num_pubs > 0 && num_pubs != num_dbs)
{
- pg_log_error("wrong number of publication names specified");
- pg_log_error_detail("The number of specified publication names (%d) must match the number of specified database names (%d).",
- num_pubs, num_dbs);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "wrong number of publication names specified");
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "The number of specified publication names (%d) must match the number of specified database names (%d).",
+ num_pubs, num_dbs);
exit(1);
}
if (num_subs > 0 && num_subs != num_dbs)
{
- pg_log_error("wrong number of subscription names specified");
- pg_log_error_detail("The number of specified subscription names (%d) must match the number of specified database names (%d).",
- num_subs, num_dbs);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "wrong number of subscription names specified");
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "The number of specified subscription names (%d) must match the number of specified database names (%d).",
+ num_subs, num_dbs);
exit(1);
}
if (num_replslots > 0 && num_replslots != num_dbs)
{
- pg_log_error("wrong number of replication slot names specified");
- pg_log_error_detail("The number of specified replication slot names (%d) must match the number of specified database names (%d).",
- num_replslots, num_dbs);
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "wrong number of replication slot names specified");
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_DETAIL,
+ "The number of specified replication slot names (%d) must match the number of specified database names (%d).",
+ num_replslots, num_dbs);
exit(1);
}
@@ -2504,9 +2700,11 @@ main(int argc, char **argv)
dbinfos.objecttypes_to_clean |= OBJECTTYPE_PUBLICATIONS;
else
{
- pg_log_error("invalid object type \"%s\" specified for %s",
- cell->val, "--clean");
- pg_log_error_hint("The valid value is: \"%s\"", "publications");
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "invalid object type \"%s\" specified for %s",
+ cell->val, "--clean");
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "The valid value is: \"%s\"", "publications");
exit(1);
}
}
@@ -2537,7 +2735,7 @@ main(int argc, char **argv)
pub_sysid = get_primary_sysid(dbinfos.dbinfo[0].pubconninfo);
sub_sysid = get_standby_sysid(subscriber_dir);
if (pub_sysid != sub_sysid)
- pg_fatal("subscriber data directory is not a copy of the source database cluster");
+ report_createsub_fatal("subscriber data directory is not a copy of the source database cluster");
/* Subscriber PID file */
snprintf(pidfile, MAXPGPATH, "%s/postmaster.pid", subscriber_dir);
@@ -2550,8 +2748,10 @@ main(int argc, char **argv)
*/
if (stat(pidfile, &statbuf) == 0)
{
- pg_log_error("standby server is running");
- pg_log_error_hint("Stop the standby server and try again.");
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_PRIMARY,
+ "standby server is running");
+ report_createsub_log(PG_LOG_ERROR, PG_LOG_HINT,
+ "Stop the standby server and try again.");
exit(1);
}
@@ -2560,7 +2760,8 @@ main(int argc, char **argv)
* by command-line options). The goal is to avoid connections during the
* transformation steps.
*/
- pg_log_info("starting the standby server with command-line options");
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "starting the standby server with command-line options");
start_standby_server(&opt, true, false);
/* Check if the standby server is ready for logical replication */
@@ -2576,7 +2777,8 @@ main(int argc, char **argv)
* guarantees it) *before* creating the replication slots in
* setup_publisher().
*/
- pg_log_info("stopping the subscriber");
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "stopping the subscriber");
stop_standby_server(subscriber_dir);
/* Create the required objects for each database on publisher */
@@ -2590,7 +2792,8 @@ main(int argc, char **argv)
* until accepting connections. We don't want to start logical replication
* during setup.
*/
- pg_log_info("starting the subscriber");
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "starting the subscriber");
start_standby_server(&opt, true, true);
/* Waiting the subscriber to be promoted */
@@ -2611,7 +2814,8 @@ main(int argc, char **argv)
drop_failover_replication_slots(dbinfos.dbinfo);
/* Stop the subscriber */
- pg_log_info("stopping the subscriber");
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "stopping the subscriber");
stop_standby_server(subscriber_dir);
/* Change system identifier from subscriber */
@@ -2619,7 +2823,8 @@ main(int argc, char **argv)
success = true;
- pg_log_info("Done!");
+ report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
+ "Done!");
return 0;
}
--
2.43.0
[application/octet-stream] v16-0002-Add-a-new-argument-l-logdir-to-pg_createsubscrib.patch (15.7K, 4-v16-0002-Add-a-new-argument-l-logdir-to-pg_createsubscrib.patch)
download | inline diff:
From c75aec55d1e11fad447b9dda40a9b9831d3eeaad Mon Sep 17 00:00:00 2001
From: Gyan Sreejith <[email protected]>
Date: Sat, 21 Mar 2026 19:05:34 -0400
Subject: [PATCH v16 2/2] Add a new argument -l <logdir> to
pg_createsubscriber.
Enabling the option to write messages to log files in the specified directory.
A new directory is created if required. A subdirectory is created with timestamp as its name, and it will contain two new logfiles:
1. pg_createsubscriber_server.log - captures messages related to starting and stopping the standby server.
2. pg_createsubscriber_internal.log - captures internal diagnostic output from pg_createsubscriber.
For example, if we specify -l abc as an argument, and if the timestamp on running it is 20260119T204317.204, a directory abc is created if it doesn't exist already, with 20260119T204317.204 as its subdirectory and it will contain the two log files pg_createsubscriber_server.log and pg_createsubscriber_internal.log
---
doc/src/sgml/ref/pg_createsubscriber.sgml | 29 +++
src/bin/pg_basebackup/pg_createsubscriber.c | 174 +++++++++++++++++-
.../t/040_pg_createsubscriber.pl | 41 ++++-
3 files changed, 232 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/pg_createsubscriber.sgml b/doc/src/sgml/ref/pg_createsubscriber.sgml
index cf45ff3573d..ff635ba26cb 100644
--- a/doc/src/sgml/ref/pg_createsubscriber.sgml
+++ b/doc/src/sgml/ref/pg_createsubscriber.sgml
@@ -136,6 +136,35 @@ PostgreSQL documentation
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>-l <replaceable class="parameter">directory</replaceable></option></term>
+ <term><option>--logdir=<replaceable class="parameter">directory</replaceable></option></term>
+ <listitem>
+ <para>
+ 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 <application>pg_createsubscriber</application>
+ 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.
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>pg_createsubscriber_server.log</literal> which captures logs
+ related to stopping and starting the standby server,
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>pg_createsubscriber_internal.log</literal> which captures
+ internal diagnostic output (validations, checks, etc.)
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>-n</option></term>
<term><option>--dry-run</option></term>
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index b2bc9dae0b8..f0da8a22354 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -49,10 +49,14 @@
#define INCLUDED_CONF_FILE "pg_createsubscriber.conf"
#define INCLUDED_CONF_FILE_DISABLED INCLUDED_CONF_FILE ".disabled"
+#define SERVER_LOG_FILE_NAME "pg_createsubscriber_server"
+#define INTERNAL_LOG_FILE_NAME "pg_createsubscriber_internal"
+
/* Command-line options */
struct CreateSubscriberOptions
{
char *config_file; /* configuration file */
+ char *log_dir; /* log directory name */
char *pub_conninfo_str; /* publisher connection string */
char *socket_dir; /* directory for Unix-domain socket, if any */
char *sub_port; /* subscriber port number */
@@ -149,8 +153,14 @@ static void get_publisher_databases(struct CreateSubscriberOptions *opt,
static void report_createsub_log(enum pg_log_level, enum pg_log_part,
const char *pg_restrict fmt,...)
pg_attribute_printf(3, 4);
+static void report_createsub_log_v(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt, va_list args)
+ pg_attribute_printf(3, 0);
pg_noreturn static void report_createsub_fatal(const char *pg_restrict fmt,...)
pg_attribute_printf(1, 2);
+static void internal_log_file_write(enum pg_log_level level,
+ const char *pg_restrict fmt, va_list args)
+ pg_attribute_printf(2, 0);
#define WAIT_INTERVAL 1 /* 1 second */
@@ -172,6 +182,9 @@ static pg_prng_state prng_state;
static char *pg_ctl_path = NULL;
static char *pg_resetwal_path = NULL;
+static FILE *internal_log_file_fp = NULL; /* File ptr to log all messages to */
+static char logdir[MAXPGPATH]; /* Directory log files are put (if specified) */
+
/* standby / subscriber data directory */
static char *subscriber_dir = NULL;
@@ -180,8 +193,28 @@ static bool standby_running = false;
static bool recovery_params_set = false;
/*
- * Report a message with a given log level
+ * Report a message with a given log level to stderr and log file
+ * (if specified).
*/
+static void
+report_createsub_log_v(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt, va_list args)
+{
+ if (internal_log_file_fp != NULL)
+ {
+ /* Output to both stderr and the log file */
+ va_list arg_cpy;
+
+ va_copy(arg_cpy, args);
+ pg_log_generic_v(level, part, fmt, arg_cpy);
+ va_end(arg_cpy);
+
+ internal_log_file_write(level, fmt, args);
+ }
+ else
+ pg_log_generic_v(level, part, fmt, args);
+}
+
static void
report_createsub_log(enum pg_log_level level, enum pg_log_part part,
const char *pg_restrict fmt,...)
@@ -190,7 +223,7 @@ report_createsub_log(enum pg_log_level level, enum pg_log_part part,
va_start(args, fmt);
- pg_log_generic_v(level, part, fmt, args);
+ report_createsub_log_v(level, part, fmt, args);
va_end(args);
}
@@ -205,7 +238,7 @@ report_createsub_fatal(const char *pg_restrict fmt,...)
va_start(args, fmt);
- pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, args);
+ report_createsub_log_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, args);
va_end(args);
@@ -313,6 +346,12 @@ cleanup_objects_atexit(void)
if (standby_running)
stop_standby_server(subscriber_dir);
+
+ if (internal_log_file_fp != NULL)
+ {
+ fclose(internal_log_file_fp);
+ internal_log_file_fp = NULL;
+ }
}
static void
@@ -327,6 +366,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 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"));
@@ -761,6 +801,7 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
bool crc_ok;
struct timeval tv;
+ char *out_file;
char *cmd_str;
report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
@@ -799,8 +840,20 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
"running pg_resetwal on the subscriber");
- cmd_str = psprintf("\"%s\" -D \"%s\" > \"%s\"", pg_resetwal_path,
- subscriber_dir, DEVNULL);
+ /*
+ * Redirecting the output to the logfile if specified. Since the output
+ * would be very short, around one line, we do not provide a separate file
+ * for it; it's done as a part of the server log.
+ */
+ if (opt->log_dir)
+ out_file = psprintf("%s/%s.log", logdir, SERVER_LOG_FILE_NAME);
+ else
+ out_file = DEVNULL;
+
+ cmd_str = psprintf("\"%s\" -D \"%s\" >> \"%s\"", pg_resetwal_path,
+ subscriber_dir, out_file);
+ if (opt->log_dir)
+ pg_free(out_file);
report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
"pg_resetwal command is: %s", cmd_str);
@@ -817,6 +870,7 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
}
pg_free(cf);
+ pg_free(cmd_str);
}
/*
@@ -1023,6 +1077,89 @@ server_is_in_recovery(PGconn *conn)
return ret == 0;
}
+static void
+internal_log_file_write(enum pg_log_level level, const char *pg_restrict fmt,
+ va_list args)
+{
+ Assert(internal_log_file_fp);
+
+ /* Do nothing if log level is too low. */
+ if (level < __pg_log_level)
+ return;
+
+ vfprintf(internal_log_file_fp, _(fmt), args);
+
+ fprintf(internal_log_file_fp, "\n");
+ fflush(internal_log_file_fp);
+}
+
+/*
+ * Open a new logfile with proper permissions.
+ * From src/backend/postmaster/syslogger.c
+ */
+static FILE *
+logfile_open(const char *filename, const char *mode)
+{
+ FILE *fh;
+ mode_t oumask;
+
+ oumask = umask((mode_t) ((~(S_IRUSR | S_IWUSR)) & (S_IRWXU | S_IRWXG | S_IRWXO)));
+ fh = fopen(filename, mode);
+ umask(oumask);
+
+ if (fh)
+ {
+ setvbuf(fh, NULL, PG_IOLBF, 0);
+
+#ifdef WIN32
+ /* use CRLF line endings on Windows */
+ _setmode(_fileno(fh), _O_TEXT);
+#endif
+ }
+ else
+ pg_fatal("could not open log file \"%s\": %m",
+ filename);
+
+ return fh;
+}
+
+static void
+make_output_dirs(const char *log_basedir)
+{
+ char timestamp[128];
+ struct timeval tval;
+ time_t now;
+ struct tm tmbuf;
+ 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));
+
+ /* append milliseconds */
+ snprintf(timestamp + strlen(timestamp),
+ sizeof(timestamp) - strlen(timestamp), ".%03u",
+ (unsigned int) (tval.tv_usec / 1000));
+
+ /* Build timestamp directory path */
+ len = snprintf(logdir, MAXPGPATH, "%s/%s", log_basedir, timestamp);
+
+ if (len >= MAXPGPATH)
+ pg_fatal("directory path for log files, %s/%s, is too long",
+ logdir, timestamp);
+
+ /* Create base directory (ignore if exists) */
+ if (mkdir(log_basedir, S_IRWXU) < 0 && errno != EEXIST)
+ pg_fatal("could not create directory \"%s\": %m", log_basedir);
+
+ /* Create BASE_DIR/$timestamp */
+ if (mkdir(logdir, S_IRWXU) < 0)
+ pg_fatal("could not create directory \"%s\": %m", logdir);
+}
+
/*
* Is the primary server ready for logical replication?
*
@@ -1781,6 +1918,9 @@ 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)
+ appendPQExpBuffer(pg_ctl_cmd, " -l \"%s/%s.log\"", logdir, SERVER_LOG_FILE_NAME);
+
report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
"pg_ctl command is: %s", pg_ctl_cmd->data);
rc = system(pg_ctl_cmd->data);
@@ -2351,6 +2491,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'},
@@ -2409,6 +2550,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;
@@ -2439,7 +2581,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)
@@ -2460,6 +2602,10 @@ 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);
+ break;
case 'n':
dry_run = true;
break;
@@ -2607,6 +2753,19 @@ main(int argc, char **argv)
exit(1);
}
+ if (opt.log_dir != NULL)
+ {
+ char *internal_log_file;
+
+ make_output_dirs(opt.log_dir);
+ internal_log_file = psprintf("%s/%s.log", logdir,
+ INTERNAL_LOG_FILE_NAME);
+
+ /* logfile_open() will exit if there is an error */
+ internal_log_file_fp = logfile_open(internal_log_file, "a");
+ pg_free(internal_log_file);
+ }
+
if (dry_run)
report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
"Executing in dry-run mode.\n"
@@ -2826,5 +2985,8 @@ main(int argc, char **argv)
report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
"Done!");
+ if (internal_log_file_fp != NULL)
+ fclose(internal_log_file_fp);
+
return 0;
}
diff --git a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
index 0c27fca7bb7..858082c70df 100644
--- a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
+++ b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
@@ -14,6 +14,7 @@ program_version_ok('pg_createsubscriber');
program_options_handling_ok('pg_createsubscriber');
my $datadir = PostgreSQL::Test::Utils::tempdir;
+my $logdir = PostgreSQL::Test::Utils::tempdir;
# Generate a database with a name made of a range of ASCII characters.
# Extracted from 002_pg_upgrade.pl.
@@ -362,9 +363,35 @@ command_ok(
'--subscription' => 'sub2',
'--database' => $db1,
'--database' => $db2,
+ '--logdir' => $logdir,
],
'run pg_createsubscriber --dry-run on node S');
+# Check that the log files were created
+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 $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 $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;
is($node_s->safe_psql('postgres', 'SELECT pg_catalog.pg_is_in_recovery()'),
@@ -444,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);
));
@@ -540,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');
@@ -555,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(
@@ -581,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");
--
2.43.0
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-20 11:37 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-20 17:00 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-21 09:57 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-21 23:09 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
@ 2026-03-23 03:15 ` shveta malik <[email protected]>
2026-03-23 07:16 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
1 sibling, 1 reply; 55+ messages in thread
From: shveta malik @ 2026-03-23 03:15 UTC (permalink / raw)
To: Gyan Sreejith <[email protected]>; +Cc: Amit Kapila <[email protected]>; Kuroda, Hayato/黒田 隼人 <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>; shveta malik <[email protected]>
On Sun, Mar 22, 2026 at 4:39 AM Gyan Sreejith <[email protected]> wrote:
>
>
> On Sat, Mar 21, 2026 at 5:57 AM Amit Kapila <[email protected]> wrote:
>>
>>
>> Based on the above information, can we consider renaming the above
>>
>> functions to report_createsub_log() and report_createsub_fatal()?
>>
>> Other than the above point, 0001 LGTM.
>
>
> I have renamed the functions.
>
001 looks good. I like the new function's name.
Commit message still refers old function name, it can be corrected
during commit.
thanks
Shveta
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-20 11:37 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-20 17:00 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-21 09:57 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-21 23:09 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-23 03:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber shveta malik <[email protected]>
@ 2026-03-23 07:16 ` Amit Kapila <[email protected]>
2026-03-23 09:54 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
0 siblings, 1 reply; 55+ messages in thread
From: Amit Kapila @ 2026-03-23 07:16 UTC (permalink / raw)
To: shveta malik <[email protected]>; +Cc: Gyan Sreejith <[email protected]>; Kuroda, Hayato/黒田 隼人 <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
On Mon, Mar 23, 2026 at 8:46 AM shveta malik <[email protected]> wrote:
>
> On Sun, Mar 22, 2026 at 4:39 AM Gyan Sreejith <[email protected]> wrote:
> >
> >
> > On Sat, Mar 21, 2026 at 5:57 AM Amit Kapila <[email protected]> wrote:
> >>
> >>
> >> Based on the above information, can we consider renaming the above
> >>
> >> functions to report_createsub_log() and report_createsub_fatal()?
> >>
> >> Other than the above point, 0001 LGTM.
> >
> >
> > I have renamed the functions.
> >
>
> 001 looks good.
>
Pushed 0001.
--
With Regards,
Amit Kapila.
^ permalink raw reply [nested|flat] 55+ messages in thread
* RE: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-20 11:37 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-20 17:00 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-21 09:57 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-21 23:09 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-23 03:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber shveta malik <[email protected]>
2026-03-23 07:16 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
@ 2026-03-23 09:54 ` Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-24 01:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
0 siblings, 1 reply; 55+ messages in thread
From: Kuroda, Hayato/黒田 隼人 @ 2026-03-23 09:54 UTC (permalink / raw)
To: Gyan Sreejith <[email protected]>; 'Amit Kapila' <[email protected]>; shveta malik <[email protected]>; +Cc: Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
Dear Gyan,
Can you rebase the patch because 0001 is now pushed?
Also, below contains my minor comments.
01.
Found that pg_log_generic_v() has some prefix but internal_log_file_write() does not.
It means output strings are not the same. For example, on terminal:
```
pg_createsubscriber: error: standby server is running
pg_createsubscriber: hint: Stop the standby server and try again.
```
But on log file:
```
standby server is running
Stop the standby server and try again.
```
It's because pg_log_generic_v() has the format like below. I.e., the program name
is printed at the begining, and some prefix also exists in some cases.
${program name}: {error: |warning: |detail: |hint: } content
I cannot find such a difference on pg_upgrade: no prefix exists in any cases.
So, what should be here? My preference is to basically follow pg_log_generic_v()
But remove the program name. How about others?
02.
Can we check the permission of generated directory in the test?
Best regards,
Hayato Kuroda
FUJITSU LIMITED
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-20 11:37 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-20 17:00 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-21 09:57 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-21 23:09 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-23 03:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber shveta malik <[email protected]>
2026-03-23 07:16 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-23 09:54 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
@ 2026-03-24 01:23 ` Gyan Sreejith <[email protected]>
2026-03-24 03:02 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Chao Li <[email protected]>
2026-03-24 10:06 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
0 siblings, 2 replies; 55+ messages in thread
From: Gyan Sreejith @ 2026-03-24 01:23 UTC (permalink / raw)
To: Kuroda, Hayato/黒田 隼人 <[email protected]>; +Cc: Amit Kapila <[email protected]>; shveta malik <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
On Mon, Mar 23, 2026 at 2:24 AM Chao Li <[email protected]> wrote:
+ /* Build timestamp directory path */
+ len = snprintf(logdir, MAXPGPATH, "%s/%s", log_basedir, timestamp);
+
+ if (len >= MAXPGPATH)
+ pg_fatal("directory path for log files, %s/%s, is too long",
+ logdir, timestamp);
```
> In the pg_fatal call, I believe logdir should be log_basedir.
We are writing into logdir, and the if will be true only if it is too long.
Hence we should be checking logdir.
> The biggest problem I see with this patch is here.
internal_log_file_write doesn’t handle “%m”. I think we can check the
implementation of pg_log_generic_v how %m is handled. The key code snippet
is:
```
> errno = save_errno;
> va_copy(ap2, ap);
> required_len = vsnprintf(NULL, 0, fmt, ap2) + 1;
> va_end(ap2);
```
> Where, vsnprintf points to pg_vsnprintf, and pg_vsnprintf calls dopr to
handle %m.
I have saved and restored errno similar to that. The code snippet you
pointed out is, as far as I understand, where they are calculating how much
space to allocate (including the \0 at the end). I think it will be handled
automatically as long as errno is not overwritten - which it will now be.
Thank you!
>The other problem is, with internal_log_file_write, HINT, DETAIL prefix
are no longer printed, is that intentional?
I could add a switch-case to print it out. Is that important? What do you
think?
I have fixed the rest of your suggestions. Thank you, Chao Li!
On Mon, Mar 23, 2026 at 5:55 AM shveta malik <[email protected]> wrote:
> We can get rid of below alignment related changes in unrelated test parts.
They are added when I run pgperltidy. Anyone else trying to change the file
after this would see the same thing if we don't change it. Should I move it
into another patch?
I have fixed the rest of it. Thank you, Shveta Malik!
On Mon, Mar 23, 2026 at 5:55 AM Kuroda, Hayato/黒田 隼人 <
[email protected]> wrote:
> 01.
> Found that pg_log_generic_v() has some prefix but
> internal_log_file_write() does not.
> It means output strings are not the same. For example, on terminal:
>
> ```
> pg_createsubscriber: error: standby server is running
> pg_createsubscriber: hint: Stop the standby server and try again.
> ```
>
> But on log file:
> ```
> standby server is running
> Stop the standby server and try again.
> ```
>
> It's because pg_log_generic_v() has the format like below. I.e., the
> program name
> is printed at the begining, and some prefix also exists in some cases.
>
> ${program name}: {error: |warning: |detail: |hint: } content
>
> I cannot find such a difference on pg_upgrade: no prefix exists in any
> cases.
> So, what should be here? My preference is to basically follow
> pg_log_generic_v()
> But remove the program name. How about others?
>
I haven't changed the output of pg_log_generic_v() yet. Shall I add the
prefix to the output? I have done the rest of your suggestions. Thank you!
Regards,
Gyan Sreejith
Attachments:
[application/x-patch] v17-0001-Add-a-new-argument-l-logdir-to-pg_createsubscrib.patch (16.4K, 3-v17-0001-Add-a-new-argument-l-logdir-to-pg_createsubscrib.patch)
download | inline diff:
From b83f96eb26f26162e06542c4f05226cca6b5613e Mon Sep 17 00:00:00 2001
From: Gyan Sreejith <[email protected]>
Date: Mon, 23 Mar 2026 21:21:56 -0400
Subject: [PATCH v17] Add a new argument -l <logdir> to pg_createsubscriber.
Enabling the option to write messages to log files in the specified directory.
A new directory is created if required. A subdirectory is created with timestamp as its name, and it will contain two new logfiles:
1. pg_createsubscriber_server.log - captures messages related to starting and stopping the standby server.
2. pg_createsubscriber_internal.log - captures internal diagnostic output from pg_createsubscriber.
For example, if we specify -l abc as an argument, and if the timestamp on running it is 20260119T204317.204, a directory abc is created if it doesn't exist already, with 20260119T204317.204 as its subdirectory and it will contain the two log files pg_createsubscriber_server.log and pg_createsubscriber_internal.log
---
doc/src/sgml/ref/pg_createsubscriber.sgml | 29 +++
src/bin/pg_basebackup/pg_createsubscriber.c | 174 +++++++++++++++++-
.../t/040_pg_createsubscriber.pl | 48 ++++-
3 files changed, 239 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/pg_createsubscriber.sgml b/doc/src/sgml/ref/pg_createsubscriber.sgml
index 6e17cee18eb..5ac61c7b558 100644
--- a/doc/src/sgml/ref/pg_createsubscriber.sgml
+++ b/doc/src/sgml/ref/pg_createsubscriber.sgml
@@ -136,6 +136,35 @@ PostgreSQL documentation
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>-l <replaceable class="parameter">directory</replaceable></option></term>
+ <term><option>--logdir=<replaceable class="parameter">directory</replaceable></option></term>
+ <listitem>
+ <para>
+ 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 <application>pg_createsubscriber</application>
+ 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.
+ <itemizedlist>
+ <listitem>
+ <para>
+ <filename>pg_createsubscriber_server.log</filename> which captures logs
+ related to stopping and starting the standby server,
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <filename>pg_createsubscriber_internal.log</filename> which captures
+ internal diagnostic output (validations, checks, etc.)
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>-n</option></term>
<term><option>--dry-run</option></term>
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index b2bc9dae0b8..63ffd337613 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -20,6 +20,7 @@
#include "common/connect.h"
#include "common/controldata_utils.h"
+#include "common/file_perm.h"
#include "common/file_utils.h"
#include "common/logging.h"
#include "common/pg_prng.h"
@@ -49,10 +50,14 @@
#define INCLUDED_CONF_FILE "pg_createsubscriber.conf"
#define INCLUDED_CONF_FILE_DISABLED INCLUDED_CONF_FILE ".disabled"
+#define SERVER_LOG_FILE_NAME "pg_createsubscriber_server"
+#define INTERNAL_LOG_FILE_NAME "pg_createsubscriber_internal"
+
/* Command-line options */
struct CreateSubscriberOptions
{
char *config_file; /* configuration file */
+ char *log_dir; /* log directory name */
char *pub_conninfo_str; /* publisher connection string */
char *socket_dir; /* directory for Unix-domain socket, if any */
char *sub_port; /* subscriber port number */
@@ -149,8 +154,14 @@ static void get_publisher_databases(struct CreateSubscriberOptions *opt,
static void report_createsub_log(enum pg_log_level, enum pg_log_part,
const char *pg_restrict fmt,...)
pg_attribute_printf(3, 4);
+static void report_createsub_log_v(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt, va_list args)
+ pg_attribute_printf(3, 0);
pg_noreturn static void report_createsub_fatal(const char *pg_restrict fmt,...)
pg_attribute_printf(1, 2);
+static void internal_log_file_write(enum pg_log_level level,
+ const char *pg_restrict fmt, va_list args)
+ pg_attribute_printf(2, 0);
#define WAIT_INTERVAL 1 /* 1 second */
@@ -172,6 +183,9 @@ static pg_prng_state prng_state;
static char *pg_ctl_path = NULL;
static char *pg_resetwal_path = NULL;
+static FILE *internal_log_file_fp = NULL; /* File ptr to log all messages to */
+static char logdir[MAXPGPATH]; /* Directory log files are put (if specified) */
+
/* standby / subscriber data directory */
static char *subscriber_dir = NULL;
@@ -180,8 +194,29 @@ static bool standby_running = false;
static bool recovery_params_set = false;
/*
- * Report a message with a given log level
+ * Report a message with a given log level to stderr and log file
+ * (if specified).
*/
+static void
+report_createsub_log_v(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt, va_list args)
+{
+ int save_errno = errno;
+
+ if (internal_log_file_fp != NULL)
+ {
+ /* Output to both stderr and the log file */
+ va_list arg_cpy;
+
+ va_copy(arg_cpy, args);
+ internal_log_file_write(level, fmt, arg_cpy);
+ va_end(arg_cpy);
+ /* Restore errno in case internal_log_file_write changed it */
+ errno = save_errno;
+ }
+ pg_log_generic_v(level, part, fmt, args);
+}
+
static void
report_createsub_log(enum pg_log_level level, enum pg_log_part part,
const char *pg_restrict fmt,...)
@@ -190,7 +225,7 @@ report_createsub_log(enum pg_log_level level, enum pg_log_part part,
va_start(args, fmt);
- pg_log_generic_v(level, part, fmt, args);
+ report_createsub_log_v(level, part, fmt, args);
va_end(args);
}
@@ -205,7 +240,7 @@ report_createsub_fatal(const char *pg_restrict fmt,...)
va_start(args, fmt);
- pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, args);
+ report_createsub_log_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, args);
va_end(args);
@@ -313,6 +348,13 @@ cleanup_objects_atexit(void)
if (standby_running)
stop_standby_server(subscriber_dir);
+
+ if (internal_log_file_fp != NULL)
+ {
+ if (fclose(internal_log_file_fp) != 0)
+ report_createsub_fatal("could not close %s/%s.log: %m", logdir, INTERNAL_LOG_FILE_NAME);
+ internal_log_file_fp = NULL;
+ }
}
static void
@@ -327,6 +369,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 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"));
@@ -761,6 +804,7 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
bool crc_ok;
struct timeval tv;
+ char *out_file;
char *cmd_str;
report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
@@ -799,8 +843,20 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
"running pg_resetwal on the subscriber");
- cmd_str = psprintf("\"%s\" -D \"%s\" > \"%s\"", pg_resetwal_path,
- subscriber_dir, DEVNULL);
+ /*
+ * Redirecting the output to the logfile if specified. Since the output
+ * would be very short, around one line, we do not provide a separate file
+ * for it; it's done as a part of the server log.
+ */
+ if (opt->log_dir)
+ out_file = psprintf("%s/%s.log", logdir, SERVER_LOG_FILE_NAME);
+ else
+ out_file = DEVNULL;
+
+ cmd_str = psprintf("\"%s\" -D \"%s\" >> \"%s\"", pg_resetwal_path,
+ subscriber_dir, out_file);
+ if (opt->log_dir)
+ pg_free(out_file);
report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
"pg_resetwal command is: %s", cmd_str);
@@ -817,6 +873,7 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
}
pg_free(cf);
+ pg_free(cmd_str);
}
/*
@@ -1023,6 +1080,89 @@ server_is_in_recovery(PGconn *conn)
return ret == 0;
}
+static void
+internal_log_file_write(enum pg_log_level level, const char *pg_restrict fmt,
+ va_list args)
+{
+ Assert(internal_log_file_fp);
+
+ /* Do nothing if log level is too low. */
+ if (level < __pg_log_level)
+ return;
+
+ vfprintf(internal_log_file_fp, _(fmt), args);
+
+ fprintf(internal_log_file_fp, "\n");
+ fflush(internal_log_file_fp);
+}
+
+/*
+ * Open a new logfile with proper permissions.
+ * From src/backend/postmaster/syslogger.c
+ */
+static FILE *
+logfile_open(const char *filename, const char *mode)
+{
+ FILE *fh;
+ mode_t oumask;
+
+ oumask = umask((mode_t) ((~(S_IRUSR | S_IWUSR)) & (S_IRWXU | S_IRWXG | S_IRWXO)));
+ fh = fopen(filename, mode);
+ umask(oumask);
+
+ if (fh)
+ {
+ setvbuf(fh, NULL, PG_IOLBF, 0);
+
+#ifdef WIN32
+ /* use CRLF line endings on Windows */
+ _setmode(_fileno(fh), _O_TEXT);
+#endif
+ }
+ else
+ report_createsub_fatal("could not open log file \"%s\": %m",
+ filename);
+
+ return fh;
+}
+
+static void
+make_output_dirs(const char *log_basedir)
+{
+ char timestamp[128];
+ struct timeval tval;
+ time_t now;
+ struct tm tmbuf;
+ 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));
+
+ /* Append milliseconds */
+ snprintf(timestamp + strlen(timestamp),
+ sizeof(timestamp) - strlen(timestamp), ".%03u",
+ (unsigned int) (tval.tv_usec / 1000));
+
+ /* Build timestamp directory path */
+ len = snprintf(logdir, MAXPGPATH, "%s/%s", log_basedir, timestamp);
+
+ if (len >= MAXPGPATH)
+ report_createsub_fatal("directory path for log files, %s/%s, is too long",
+ logdir, timestamp);
+
+ /* Create base directory (ignore if exists) */
+ if (mkdir(log_basedir, pg_dir_create_mode) < 0 && errno != EEXIST)
+ report_createsub_fatal("could not create directory \"%s\": %m", log_basedir);
+
+ /* Create a timestamp-named subdirectory under the base directory */
+ if (mkdir(logdir, pg_dir_create_mode) < 0)
+ report_createsub_fatal("could not create directory \"%s\": %m", logdir);
+}
+
/*
* Is the primary server ready for logical replication?
*
@@ -1781,6 +1921,9 @@ 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)
+ appendPQExpBuffer(pg_ctl_cmd, " -l \"%s/%s.log\"", logdir, SERVER_LOG_FILE_NAME);
+
report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
"pg_ctl command is: %s", pg_ctl_cmd->data);
rc = system(pg_ctl_cmd->data);
@@ -2351,6 +2494,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'},
@@ -2409,6 +2553,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;
@@ -2439,7 +2584,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)
@@ -2460,6 +2605,10 @@ 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);
+ break;
case 'n':
dry_run = true;
break;
@@ -2607,6 +2756,19 @@ main(int argc, char **argv)
exit(1);
}
+ if (opt.log_dir != NULL)
+ {
+ char *internal_log_file;
+
+ make_output_dirs(opt.log_dir);
+ internal_log_file = psprintf("%s/%s.log", logdir,
+ INTERNAL_LOG_FILE_NAME);
+
+ /* logfile_open() will exit if there is an error */
+ internal_log_file_fp = logfile_open(internal_log_file, "a");
+ pg_free(internal_log_file);
+ }
+
if (dry_run)
report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
"Executing in dry-run mode.\n"
diff --git a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
index 0c27fca7bb7..239ea58d9a0 100644
--- a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
+++ b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
@@ -5,6 +5,8 @@
use strict;
use warnings FATAL => 'all';
+use File::Basename;
+use File::stat;
use PostgreSQL::Test::Cluster;
use PostgreSQL::Test::Utils;
use Test::More;
@@ -14,6 +16,7 @@ program_version_ok('pg_createsubscriber');
program_options_handling_ok('pg_createsubscriber');
my $datadir = PostgreSQL::Test::Utils::tempdir;
+my $logdir = PostgreSQL::Test::Utils::tempdir;
# Generate a database with a name made of a range of ASCII characters.
# Extracted from 002_pg_upgrade.pl.
@@ -362,9 +365,40 @@ command_ok(
'--subscription' => 'sub2',
'--database' => $db1,
'--database' => $db2,
+ '--logdir' => $logdir,
],
'run pg_createsubscriber --dry-run on node S');
+# Check that the log files were created
+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 $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 $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");
+my $timestamp_dir = dirname($internal_log_files[0]);
+my $timestamp_dir_stat = stat($timestamp_dir);
+my $timestamp_dir_mode = $timestamp_dir_stat->mode & 07777;
+is($timestamp_dir_mode, 0700,
+ "Directory with .log files has permissions S_IRWXU");
+
# Check if node S is still a standby
$node_s->start;
is($node_s->safe_psql('postgres', 'SELECT pg_catalog.pg_is_in_recovery()'),
@@ -444,7 +478,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);
));
@@ -540,8 +575,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');
@@ -555,8 +589,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(
@@ -581,7 +617,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");
--
2.43.0
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-20 11:37 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-20 17:00 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-21 09:57 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-21 23:09 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-23 03:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber shveta malik <[email protected]>
2026-03-23 07:16 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-23 09:54 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-24 01:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
@ 2026-03-24 03:02 ` Chao Li <[email protected]>
2026-03-24 03:31 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Chao Li <[email protected]>
2026-03-24 06:31 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-24 09:47 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
1 sibling, 3 replies; 55+ messages in thread
From: Chao Li @ 2026-03-24 03:02 UTC (permalink / raw)
To: Gyan Sreejith <[email protected]>; +Cc: "Kuroda, Hayato/黒田 隼人" <[email protected]>; Amit Kapila <[email protected]>; shveta malik <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
> On Mar 24, 2026, at 09:23, Gyan Sreejith <[email protected]> wrote:
>
> On Mon, Mar 23, 2026 at 2:24 AM Chao Li <[email protected]> wrote:
> + /* Build timestamp directory path */
> + len = snprintf(logdir, MAXPGPATH, "%s/%s", log_basedir, timestamp);
> +
> + if (len >= MAXPGPATH)
> + pg_fatal("directory path for log files, %s/%s, is too long",
> + logdir, timestamp);
> ```
> > In the pg_fatal call, I believe logdir should be log_basedir.
> We are writing into logdir, and the if will be true only if it is too long. Hence we should be checking logdir.
Not sure if I stated my comment clearly. The “logdir” is build from (“%s/%s, log_basedir, timestamp), however, in the pg_fatal, you are printing (“%s/%s”, logdir, timestamp), here logdir has included a truncated timestamp as it is too long, and so the fatal message will be “log_basedir/truncated-timestamp/timestamp”. So the pg_fatal should be:
```
report_createsub_fatal("directory path for log files, %s/%s, is too long”,
log_basedir, timestamp);
```
>
> > The biggest problem I see with this patch is here. internal_log_file_write doesn’t handle “%m”. I think we can check the implementation of pg_log_generic_v how %m is handled. The key code snippet is:
> ```
> > errno = save_errno;
>
> > va_copy(ap2, ap);
> > required_len = vsnprintf(NULL, 0, fmt, ap2) + 1;
> > va_end(ap2);
> ```
> > Where, vsnprintf points to pg_vsnprintf, and pg_vsnprintf calls dopr to handle %m.
> I have saved and restored errno similar to that. The code snippet you pointed out is, as far as I understand, where they are calculating how much space to allocate (including the \0 at the end). I think it will be handled automatically as long as errno is not overwritten - which it will now be. Thank you!
I verified with v17, %m works now.
>
> >The other problem is, with internal_log_file_write, HINT, DETAIL prefix are no longer printed, is that intentional?
> I could add a switch-case to print it out. Is that important? What do you think?
I personally prefer to keep those prefixes, which helps keep the log messages in a consistent style.
>
> <v17-0001-Add-a-new-argument-l-logdir-to-pg_createsubscrib.patch>
One comment on v17.
```
+ if (internal_log_file_fp != NULL)
+ {
+ if (fclose(internal_log_file_fp) != 0)
+ report_createsub_fatal("could not close %s/%s.log: %m", logdir, INTERNAL_LOG_FILE_NAME);
+ internal_log_file_fp = NULL;
+ }
```
As the error is about internal_log_file_fp, meaning it may no longer able to write to internal_log_file_fp anymore, we should avoid use report_createsub_fatal to log the error again. Maybe just a pg_fatal(), or just log to stderr.
Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-20 11:37 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-20 17:00 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-21 09:57 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-21 23:09 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-23 03:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber shveta malik <[email protected]>
2026-03-23 07:16 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-23 09:54 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-24 01:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-24 03:02 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Chao Li <[email protected]>
@ 2026-03-24 03:31 ` Chao Li <[email protected]>
2026-03-24 09:50 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2 siblings, 1 reply; 55+ messages in thread
From: Chao Li @ 2026-03-24 03:31 UTC (permalink / raw)
To: Gyan Sreejith <[email protected]>; +Cc: "Kuroda, Hayato/黒田 隼人" <[email protected]>; Amit Kapila <[email protected]>; shveta malik <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
> On Mar 24, 2026, at 11:02, Chao Li <[email protected]> wrote:
>
>
>
>> On Mar 24, 2026, at 09:23, Gyan Sreejith <[email protected]> wrote:
>>
>> On Mon, Mar 23, 2026 at 2:24 AM Chao Li <[email protected]> wrote:
>> + /* Build timestamp directory path */
>> + len = snprintf(logdir, MAXPGPATH, "%s/%s", log_basedir, timestamp);
>> +
>> + if (len >= MAXPGPATH)
>> + pg_fatal("directory path for log files, %s/%s, is too long",
>> + logdir, timestamp);
>> ```
>>> In the pg_fatal call, I believe logdir should be log_basedir.
>> We are writing into logdir, and the if will be true only if it is too long. Hence we should be checking logdir.
>
> Not sure if I stated my comment clearly. The “logdir” is build from (“%s/%s, log_basedir, timestamp), however, in the pg_fatal, you are printing (“%s/%s”, logdir, timestamp), here logdir has included a truncated timestamp as it is too long, and so the fatal message will be “log_basedir/truncated-timestamp/timestamp”. So the pg_fatal should be:
> ```
> report_createsub_fatal("directory path for log files, %s/%s, is too long”,
> log_basedir, timestamp);
> ```
>
>>
>>> The biggest problem I see with this patch is here. internal_log_file_write doesn’t handle “%m”. I think we can check the implementation of pg_log_generic_v how %m is handled. The key code snippet is:
>> ```
>>> errno = save_errno;
>>
>>> va_copy(ap2, ap);
>>> required_len = vsnprintf(NULL, 0, fmt, ap2) + 1;
>>> va_end(ap2);
>> ```
>>> Where, vsnprintf points to pg_vsnprintf, and pg_vsnprintf calls dopr to handle %m.
>> I have saved and restored errno similar to that. The code snippet you pointed out is, as far as I understand, where they are calculating how much space to allocate (including the \0 at the end). I think it will be handled automatically as long as errno is not overwritten - which it will now be. Thank you!
>
> I verified with v17, %m works now.
>
>>
>>> The other problem is, with internal_log_file_write, HINT, DETAIL prefix are no longer printed, is that intentional?
>> I could add a switch-case to print it out. Is that important? What do you think?
>
> I personally prefer to keep those prefixes, which helps keep the log messages in a consistent style.
>
>>
>> <v17-0001-Add-a-new-argument-l-logdir-to-pg_createsubscrib.patch>
>
> One comment on v17.
> ```
> + if (internal_log_file_fp != NULL)
> + {
> + if (fclose(internal_log_file_fp) != 0)
> + report_createsub_fatal("could not close %s/%s.log: %m", logdir, INTERNAL_LOG_FILE_NAME);
> + internal_log_file_fp = NULL;
> + }
> ```
>
> As the error is about internal_log_file_fp, meaning it may no longer able to write to internal_log_file_fp anymore, we should avoid use report_createsub_fatal to log the error again. Maybe just a pg_fatal(), or just log to stderr.
>
I forgot to mention one small suggestion.
Since the internal log file path includes a timestamped directory, it is a bit inconvenient to figure out the full path of the file after the command finishes. I think it would be helpful to print the full path of the internal log file at the end, something like:
```
if (internal_log_file_fp != NULL)
{
if (fclose(internal_log_file_fp) != 0)
report_createsub_fatal("could not close %s/%s.log: %m", logdir, INTERNAL_LOG_FILE_NAME);
internal_log_file_fp = NULL;
printf("check the internal log file at \"%s/%s.log\"", logdir, INTERNAL_LOG_FILE_NAME);
}
```
Note that I used printf() here, because based on my test, pg_log_info() does not work in this place.
The other thing I noticed while trying the above code is that cleanup_objects_atexit() has this:
```
if (success)
return;
```
That means on the success path, internal_log_file_fp will not be closed. So this part would need some adjustment as well. Maybe one way is to jump to the log-file cleanup part before returning, for example with a goto, so that internal_log_file_fp is always closed.
Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/
^ permalink raw reply [nested|flat] 55+ messages in thread
* RE: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-20 11:37 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-20 17:00 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-21 09:57 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-21 23:09 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-23 03:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber shveta malik <[email protected]>
2026-03-23 07:16 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-23 09:54 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-24 01:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-24 03:02 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Chao Li <[email protected]>
2026-03-24 03:31 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Chao Li <[email protected]>
@ 2026-03-24 09:50 ` Hayato Kuroda (Fujitsu) <[email protected]>
0 siblings, 0 replies; 55+ messages in thread
From: Hayato Kuroda (Fujitsu) @ 2026-03-24 09:50 UTC (permalink / raw)
To: 'Chao Li' <[email protected]>; Gyan Sreejith <[email protected]>; +Cc: Amit Kapila <[email protected]>; shveta malik <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
Dear Chao,
> I forgot to mention one small suggestion.
>
> Since the internal log file path includes a timestamped directory, it is a bit
> inconvenient to figure out the full path of the file after the command finishes. I
> think it would be helpful to print the full path of the internal log file at the end,
> something like:
> ```
> if (internal_log_file_fp != NULL)
> {
> if (fclose(internal_log_file_fp) != 0)
> report_createsub_fatal("could not
> close %s/%s.log: %m", logdir, INTERNAL_LOG_FILE_NAME);
> internal_log_file_fp = NULL;
> printf("check the internal log file at \"%s/%s.log\"", logdir,
> INTERNAL_LOG_FILE_NAME);
> }
> ```
I don't think it's needed, because pg_upgrade does not print such points.
> Note that I used printf() here, because based on my test, pg_log_info() does not
> work in this place.
It might be becasue you used pg_log_info(). pg_createsubscriber outputs only
warning or error level messages, info-level cannot be printed.
> The other thing I noticed while trying the above code is that
> cleanup_objects_atexit() has this:
> ```
> if (success)
> return;
> ```
>
> That means on the success path, internal_log_file_fp will not be closed. So this
> part would need some adjustment as well. Maybe one way is to jump to the
> log-file cleanup part before returning, for example with a goto, so that
> internal_log_file_fp is always closed.
As the same reason I posted, I think it's not needed.
[1]: https://www.postgresql.org/message-id/OS9PR01MB12149F1AD9C79A2644753A18AF548A%40OS9PR01MB12149.jpnpr...
Best regards,
Hayato Kuroda
FUJITSU LIMITED
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-20 11:37 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-20 17:00 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-21 09:57 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-21 23:09 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-23 03:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber shveta malik <[email protected]>
2026-03-23 07:16 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-23 09:54 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-24 01:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-24 03:02 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Chao Li <[email protected]>
@ 2026-03-24 06:31 ` Amit Kapila <[email protected]>
2 siblings, 0 replies; 55+ messages in thread
From: Amit Kapila @ 2026-03-24 06:31 UTC (permalink / raw)
To: Chao Li <[email protected]>; +Cc: Gyan Sreejith <[email protected]>; Kuroda, Hayato/黒田 隼人 <[email protected]>; shveta malik <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
On Tue, Mar 24, 2026 at 8:32 AM Chao Li <[email protected]> wrote:
>
> > On Mar 24, 2026, at 09:23, Gyan Sreejith <[email protected]> wrote:
> >
> > On Mon, Mar 23, 2026 at 2:24 AM Chao Li <[email protected]> wrote:
> > + /* Build timestamp directory path */
> > + len = snprintf(logdir, MAXPGPATH, "%s/%s", log_basedir, timestamp);
> > +
> > + if (len >= MAXPGPATH)
> > + pg_fatal("directory path for log files, %s/%s, is too long",
> > + logdir, timestamp);
> > ```
> > > In the pg_fatal call, I believe logdir should be log_basedir.
> > We are writing into logdir, and the if will be true only if it is too long. Hence we should be checking logdir.
>
> Not sure if I stated my comment clearly. The “logdir” is build from (“%s/%s, log_basedir, timestamp), however, in the pg_fatal, you are printing (“%s/%s”, logdir, timestamp), here logdir has included a truncated timestamp as it is too long, and so the fatal message will be “log_basedir/truncated-timestamp/timestamp”. So the pg_fatal should be:
> ```
> report_createsub_fatal("directory path for log files, %s/%s, is too long”,
> log_basedir, timestamp);
> ```
>
> >
> > > The biggest problem I see with this patch is here. internal_log_file_write doesn’t handle “%m”. I think we can check the implementation of pg_log_generic_v how %m is handled. The key code snippet is:
> > ```
> > > errno = save_errno;
> >
> > > va_copy(ap2, ap);
> > > required_len = vsnprintf(NULL, 0, fmt, ap2) + 1;
> > > va_end(ap2);
> > ```
> > > Where, vsnprintf points to pg_vsnprintf, and pg_vsnprintf calls dopr to handle %m.
> > I have saved and restored errno similar to that. The code snippet you pointed out is, as far as I understand, where they are calculating how much space to allocate (including the \0 at the end). I think it will be handled automatically as long as errno is not overwritten - which it will now be. Thank you!
>
> I verified with v17, %m works now.
>
> >
> > >The other problem is, with internal_log_file_write, HINT, DETAIL prefix are no longer printed, is that intentional?
> > I could add a switch-case to print it out. Is that important? What do you think?
>
> I personally prefer to keep those prefixes, which helps keep the log messages in a consistent style.
>
+1 to keep HINT, DETAIL kind of prefixes. I have one additional
comment in the latest patch:
+ mode_t oumask;
+
+ oumask = umask((mode_t) ((~(S_IRUSR | S_IWUSR)) & (S_IRWXU | S_IRWXG
| S_IRWXO)));
+ fh = fopen(filename, mode);
+ umask(oumask);
I think we should follow the permissions model for this file same as
what pg_ctl does for log_file given with -l option. Basically, start
with with 077 permission and then switch to data_directory
permissions. This is because to start server we anyway use pg_ctl with
-l option which will use a specific way to assign permissions to
server log file, so we should use same for internal file, otherwise,
we will end up with different file permissions for internal and server
logfile.
--
With Regards,
Amit Kapila.
^ permalink raw reply [nested|flat] 55+ messages in thread
* RE: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-20 11:37 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-20 17:00 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-21 09:57 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-21 23:09 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-23 03:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber shveta malik <[email protected]>
2026-03-23 07:16 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-23 09:54 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-24 01:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-24 03:02 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Chao Li <[email protected]>
@ 2026-03-24 09:47 ` Hayato Kuroda (Fujitsu) <[email protected]>
2 siblings, 0 replies; 55+ messages in thread
From: Hayato Kuroda (Fujitsu) @ 2026-03-24 09:47 UTC (permalink / raw)
To: 'Chao Li' <[email protected]>; Gyan Sreejith <[email protected]>; +Cc: Amit Kapila <[email protected]>; shveta malik <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
Dear Chao,
> One comment on v17.
> ```
> + if (internal_log_file_fp != NULL)
> + {
> + if (fclose(internal_log_file_fp) != 0)
> + report_createsub_fatal("could not
> close %s/%s.log: %m", logdir, INTERNAL_LOG_FILE_NAME);
> + internal_log_file_fp = NULL;
> + }
> ```
>
> As the error is about internal_log_file_fp, meaning it may no longer able to write to
> internal_log_file_fp anymore, we should avoid use report_createsub_fatal to log
> the error again. Maybe just a pg_fatal(), or just log to stderr.
I checked and found [1] in the exit() specification. It means all file descriptors
opened by the process would be closed automatically when the process exits.
So no need to try fclose() and report the failure here. Similarly, pg_upgrade
does not have fclose(log_opts.internal) in case of failures.
[1]:
First, all functions registered by the atexit function are called, in the reverse
order of their registration, except that a function is called after any previously
registered functions that had already been called at the time it was registered.
If, during the call to any such function, a call to the longjmp function is made
that would terminate the call to the registered function, the behavior is
undefined.
Next, all open streams with unwritten buffered data are flushed, all open streams
are closed, and all files created by the tmpfile function are removed.
Best regards,
Hayato Kuroda
FUJITSU LIMITED
^ permalink raw reply [nested|flat] 55+ messages in thread
* RE: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-20 11:37 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-20 17:00 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-21 09:57 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-21 23:09 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-23 03:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber shveta malik <[email protected]>
2026-03-23 07:16 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-23 09:54 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-24 01:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
@ 2026-03-24 10:06 ` Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-24 12:13 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-24 23:14 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
1 sibling, 2 replies; 55+ messages in thread
From: Hayato Kuroda (Fujitsu) @ 2026-03-24 10:06 UTC (permalink / raw)
To: 'Gyan Sreejith' <[email protected]>; +Cc: Amit Kapila <[email protected]>; shveta malik <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
Dear Gyan,
Thanks for updating the patch!
> I haven't changed the output of pg_log_generic_v() yet. Shall I add the prefix to the output?
Since pg_log_generic_v() is the common function used even by others, we should
not fix it. Let's fix internal_log_file_write() instead.
Further comments:
01.
```
+ setvbuf(fh, NULL, PG_IOLBF, 0);
```
I think setvbuf is not needed.
IIUC, setvbuf() controls when messages like printf() are flushed from to the
stdout/stderr/files and so on. In logfile_open(), we are setting that output
messages should be flushed once per new lines. Syslogger process does not call
fflush() frequently thus it seems to be controlled by the library call.
However, pg_createsubscriber always does the fflush() every time, no need to
specify when the buffered strings are flushed on the file.
Also note that pg_upgrade does not call the library function.
02.
```
+ /* use CRLF line endings on Windows */
+ _setmode(_fileno(fh), _O_TEXT);
```
_setmode() is called in win32 system. According to [2] and code comment, it's
used to open the file as TEXT mode for unifying the new-line character to CR-LF.
However, postgres has already been done in fopen(). The function is overwritten
to pgwin32_fopen()->pgwin32_open(), and there we already specify to open with
the _O_TEXT mode. I think no need to do in the pg_createsubscriber.c.
Also note that pg_upgrade does not call the library function.
03.
If we fix above and comments from Amit [1], logfile_open() is not completely
different function from the syslogger.c. We can remove the comment atop the
function.
04.
As I told in [2], no need to do fclose() the file in the cleanup function.
I created a top-up patch set which addressed all comments from me and others.
See attached.
[1]: https://www.postgresql.org/message-id/CAA4eK1KHZraUvb5U5QdMEqiCAWhGPAnStun88ZR0qFunRZu9uQ%40mail.gma...
[2]: https://www.postgresql.org/message-id/OS9PR01MB12149F1AD9C79A2644753A18AF548A%40OS9PR01MB12149.jpnpr...
Best regards,
Hayato Kuroda
FUJITSU LIMITED
Attachments:
[application/octet-stream] v18-0001-Add-a-new-argument-l-logdir-to-pg_createsubscrib.patch (16.5K, 2-v18-0001-Add-a-new-argument-l-logdir-to-pg_createsubscrib.patch)
download | inline diff:
From 47a1a9d5d2f652813cc9f10bb74026ff19d3d17d Mon Sep 17 00:00:00 2001
From: Gyan Sreejith <[email protected]>
Date: Mon, 23 Mar 2026 21:21:56 -0400
Subject: [PATCH v18 1/2] Add a new argument -l <logdir> to
pg_createsubscriber.
Enabling the option to write messages to log files in the specified directory.
A new directory is created if required. A subdirectory is created with timestamp as its name, and it will contain two new logfiles:
1. pg_createsubscriber_server.log - captures messages related to starting and stopping the standby server.
2. pg_createsubscriber_internal.log - captures internal diagnostic output from pg_createsubscriber.
For example, if we specify -l abc as an argument, and if the timestamp on running it is 20260119T204317.204, a directory abc is created if it doesn't exist already, with 20260119T204317.204 as its subdirectory and it will contain the two log files pg_createsubscriber_server.log and pg_createsubscriber_internal.log
---
doc/src/sgml/ref/pg_createsubscriber.sgml | 29 +++
src/bin/pg_basebackup/pg_createsubscriber.c | 174 +++++++++++++++++-
.../t/040_pg_createsubscriber.pl | 48 ++++-
3 files changed, 239 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/pg_createsubscriber.sgml b/doc/src/sgml/ref/pg_createsubscriber.sgml
index 6e17cee18eb..5ac61c7b558 100644
--- a/doc/src/sgml/ref/pg_createsubscriber.sgml
+++ b/doc/src/sgml/ref/pg_createsubscriber.sgml
@@ -136,6 +136,35 @@ PostgreSQL documentation
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>-l <replaceable class="parameter">directory</replaceable></option></term>
+ <term><option>--logdir=<replaceable class="parameter">directory</replaceable></option></term>
+ <listitem>
+ <para>
+ 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 <application>pg_createsubscriber</application>
+ 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.
+ <itemizedlist>
+ <listitem>
+ <para>
+ <filename>pg_createsubscriber_server.log</filename> which captures logs
+ related to stopping and starting the standby server,
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <filename>pg_createsubscriber_internal.log</filename> which captures
+ internal diagnostic output (validations, checks, etc.)
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>-n</option></term>
<term><option>--dry-run</option></term>
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index b2bc9dae0b8..63ffd337613 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -20,6 +20,7 @@
#include "common/connect.h"
#include "common/controldata_utils.h"
+#include "common/file_perm.h"
#include "common/file_utils.h"
#include "common/logging.h"
#include "common/pg_prng.h"
@@ -49,10 +50,14 @@
#define INCLUDED_CONF_FILE "pg_createsubscriber.conf"
#define INCLUDED_CONF_FILE_DISABLED INCLUDED_CONF_FILE ".disabled"
+#define SERVER_LOG_FILE_NAME "pg_createsubscriber_server"
+#define INTERNAL_LOG_FILE_NAME "pg_createsubscriber_internal"
+
/* Command-line options */
struct CreateSubscriberOptions
{
char *config_file; /* configuration file */
+ char *log_dir; /* log directory name */
char *pub_conninfo_str; /* publisher connection string */
char *socket_dir; /* directory for Unix-domain socket, if any */
char *sub_port; /* subscriber port number */
@@ -149,8 +154,14 @@ static void get_publisher_databases(struct CreateSubscriberOptions *opt,
static void report_createsub_log(enum pg_log_level, enum pg_log_part,
const char *pg_restrict fmt,...)
pg_attribute_printf(3, 4);
+static void report_createsub_log_v(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt, va_list args)
+ pg_attribute_printf(3, 0);
pg_noreturn static void report_createsub_fatal(const char *pg_restrict fmt,...)
pg_attribute_printf(1, 2);
+static void internal_log_file_write(enum pg_log_level level,
+ const char *pg_restrict fmt, va_list args)
+ pg_attribute_printf(2, 0);
#define WAIT_INTERVAL 1 /* 1 second */
@@ -172,6 +183,9 @@ static pg_prng_state prng_state;
static char *pg_ctl_path = NULL;
static char *pg_resetwal_path = NULL;
+static FILE *internal_log_file_fp = NULL; /* File ptr to log all messages to */
+static char logdir[MAXPGPATH]; /* Directory log files are put (if specified) */
+
/* standby / subscriber data directory */
static char *subscriber_dir = NULL;
@@ -180,8 +194,29 @@ static bool standby_running = false;
static bool recovery_params_set = false;
/*
- * Report a message with a given log level
+ * Report a message with a given log level to stderr and log file
+ * (if specified).
*/
+static void
+report_createsub_log_v(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt, va_list args)
+{
+ int save_errno = errno;
+
+ if (internal_log_file_fp != NULL)
+ {
+ /* Output to both stderr and the log file */
+ va_list arg_cpy;
+
+ va_copy(arg_cpy, args);
+ internal_log_file_write(level, fmt, arg_cpy);
+ va_end(arg_cpy);
+ /* Restore errno in case internal_log_file_write changed it */
+ errno = save_errno;
+ }
+ pg_log_generic_v(level, part, fmt, args);
+}
+
static void
report_createsub_log(enum pg_log_level level, enum pg_log_part part,
const char *pg_restrict fmt,...)
@@ -190,7 +225,7 @@ report_createsub_log(enum pg_log_level level, enum pg_log_part part,
va_start(args, fmt);
- pg_log_generic_v(level, part, fmt, args);
+ report_createsub_log_v(level, part, fmt, args);
va_end(args);
}
@@ -205,7 +240,7 @@ report_createsub_fatal(const char *pg_restrict fmt,...)
va_start(args, fmt);
- pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, args);
+ report_createsub_log_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, args);
va_end(args);
@@ -313,6 +348,13 @@ cleanup_objects_atexit(void)
if (standby_running)
stop_standby_server(subscriber_dir);
+
+ if (internal_log_file_fp != NULL)
+ {
+ if (fclose(internal_log_file_fp) != 0)
+ report_createsub_fatal("could not close %s/%s.log: %m", logdir, INTERNAL_LOG_FILE_NAME);
+ internal_log_file_fp = NULL;
+ }
}
static void
@@ -327,6 +369,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 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"));
@@ -761,6 +804,7 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
bool crc_ok;
struct timeval tv;
+ char *out_file;
char *cmd_str;
report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
@@ -799,8 +843,20 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
"running pg_resetwal on the subscriber");
- cmd_str = psprintf("\"%s\" -D \"%s\" > \"%s\"", pg_resetwal_path,
- subscriber_dir, DEVNULL);
+ /*
+ * Redirecting the output to the logfile if specified. Since the output
+ * would be very short, around one line, we do not provide a separate file
+ * for it; it's done as a part of the server log.
+ */
+ if (opt->log_dir)
+ out_file = psprintf("%s/%s.log", logdir, SERVER_LOG_FILE_NAME);
+ else
+ out_file = DEVNULL;
+
+ cmd_str = psprintf("\"%s\" -D \"%s\" >> \"%s\"", pg_resetwal_path,
+ subscriber_dir, out_file);
+ if (opt->log_dir)
+ pg_free(out_file);
report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
"pg_resetwal command is: %s", cmd_str);
@@ -817,6 +873,7 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
}
pg_free(cf);
+ pg_free(cmd_str);
}
/*
@@ -1023,6 +1080,89 @@ server_is_in_recovery(PGconn *conn)
return ret == 0;
}
+static void
+internal_log_file_write(enum pg_log_level level, const char *pg_restrict fmt,
+ va_list args)
+{
+ Assert(internal_log_file_fp);
+
+ /* Do nothing if log level is too low. */
+ if (level < __pg_log_level)
+ return;
+
+ vfprintf(internal_log_file_fp, _(fmt), args);
+
+ fprintf(internal_log_file_fp, "\n");
+ fflush(internal_log_file_fp);
+}
+
+/*
+ * Open a new logfile with proper permissions.
+ * From src/backend/postmaster/syslogger.c
+ */
+static FILE *
+logfile_open(const char *filename, const char *mode)
+{
+ FILE *fh;
+ mode_t oumask;
+
+ oumask = umask((mode_t) ((~(S_IRUSR | S_IWUSR)) & (S_IRWXU | S_IRWXG | S_IRWXO)));
+ fh = fopen(filename, mode);
+ umask(oumask);
+
+ if (fh)
+ {
+ setvbuf(fh, NULL, PG_IOLBF, 0);
+
+#ifdef WIN32
+ /* use CRLF line endings on Windows */
+ _setmode(_fileno(fh), _O_TEXT);
+#endif
+ }
+ else
+ report_createsub_fatal("could not open log file \"%s\": %m",
+ filename);
+
+ return fh;
+}
+
+static void
+make_output_dirs(const char *log_basedir)
+{
+ char timestamp[128];
+ struct timeval tval;
+ time_t now;
+ struct tm tmbuf;
+ 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));
+
+ /* Append milliseconds */
+ snprintf(timestamp + strlen(timestamp),
+ sizeof(timestamp) - strlen(timestamp), ".%03u",
+ (unsigned int) (tval.tv_usec / 1000));
+
+ /* Build timestamp directory path */
+ len = snprintf(logdir, MAXPGPATH, "%s/%s", log_basedir, timestamp);
+
+ if (len >= MAXPGPATH)
+ report_createsub_fatal("directory path for log files, %s/%s, is too long",
+ logdir, timestamp);
+
+ /* Create base directory (ignore if exists) */
+ if (mkdir(log_basedir, pg_dir_create_mode) < 0 && errno != EEXIST)
+ report_createsub_fatal("could not create directory \"%s\": %m", log_basedir);
+
+ /* Create a timestamp-named subdirectory under the base directory */
+ if (mkdir(logdir, pg_dir_create_mode) < 0)
+ report_createsub_fatal("could not create directory \"%s\": %m", logdir);
+}
+
/*
* Is the primary server ready for logical replication?
*
@@ -1781,6 +1921,9 @@ 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)
+ appendPQExpBuffer(pg_ctl_cmd, " -l \"%s/%s.log\"", logdir, SERVER_LOG_FILE_NAME);
+
report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
"pg_ctl command is: %s", pg_ctl_cmd->data);
rc = system(pg_ctl_cmd->data);
@@ -2351,6 +2494,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'},
@@ -2409,6 +2553,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;
@@ -2439,7 +2584,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)
@@ -2460,6 +2605,10 @@ 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);
+ break;
case 'n':
dry_run = true;
break;
@@ -2607,6 +2756,19 @@ main(int argc, char **argv)
exit(1);
}
+ if (opt.log_dir != NULL)
+ {
+ char *internal_log_file;
+
+ make_output_dirs(opt.log_dir);
+ internal_log_file = psprintf("%s/%s.log", logdir,
+ INTERNAL_LOG_FILE_NAME);
+
+ /* logfile_open() will exit if there is an error */
+ internal_log_file_fp = logfile_open(internal_log_file, "a");
+ pg_free(internal_log_file);
+ }
+
if (dry_run)
report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
"Executing in dry-run mode.\n"
diff --git a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
index 0c27fca7bb7..239ea58d9a0 100644
--- a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
+++ b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
@@ -5,6 +5,8 @@
use strict;
use warnings FATAL => 'all';
+use File::Basename;
+use File::stat;
use PostgreSQL::Test::Cluster;
use PostgreSQL::Test::Utils;
use Test::More;
@@ -14,6 +16,7 @@ program_version_ok('pg_createsubscriber');
program_options_handling_ok('pg_createsubscriber');
my $datadir = PostgreSQL::Test::Utils::tempdir;
+my $logdir = PostgreSQL::Test::Utils::tempdir;
# Generate a database with a name made of a range of ASCII characters.
# Extracted from 002_pg_upgrade.pl.
@@ -362,9 +365,40 @@ command_ok(
'--subscription' => 'sub2',
'--database' => $db1,
'--database' => $db2,
+ '--logdir' => $logdir,
],
'run pg_createsubscriber --dry-run on node S');
+# Check that the log files were created
+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 $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 $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");
+my $timestamp_dir = dirname($internal_log_files[0]);
+my $timestamp_dir_stat = stat($timestamp_dir);
+my $timestamp_dir_mode = $timestamp_dir_stat->mode & 07777;
+is($timestamp_dir_mode, 0700,
+ "Directory with .log files has permissions S_IRWXU");
+
# Check if node S is still a standby
$node_s->start;
is($node_s->safe_psql('postgres', 'SELECT pg_catalog.pg_is_in_recovery()'),
@@ -444,7 +478,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);
));
@@ -540,8 +575,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');
@@ -555,8 +589,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(
@@ -581,7 +617,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");
--
2.47.3
[application/octet-stream] v18-0002-Address-comments-from-Chao-Amit-and-Kuroda.patch (4.8K, 3-v18-0002-Address-comments-from-Chao-Amit-and-Kuroda.patch)
download | inline diff:
From 2a662b72c6acd469174250aaca6a7d60da856037 Mon Sep 17 00:00:00 2001
From: Hayato Kuroda <[email protected]>
Date: Tue, 24 Mar 2026 15:43:41 +0900
Subject: [PATCH v18 2/2] Address comments from Chao, Amit and Kuroda
---
src/bin/pg_basebackup/pg_createsubscriber.c | 72 ++++++++++++---------
1 file changed, 42 insertions(+), 30 deletions(-)
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index 63ffd337613..1f6247a8cd8 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -160,8 +160,9 @@ static void report_createsub_log_v(enum pg_log_level level, enum pg_log_part par
pg_noreturn static void report_createsub_fatal(const char *pg_restrict fmt,...)
pg_attribute_printf(1, 2);
static void internal_log_file_write(enum pg_log_level level,
+ enum pg_log_part part,
const char *pg_restrict fmt, va_list args)
- pg_attribute_printf(2, 0);
+ pg_attribute_printf(3, 0);
#define WAIT_INTERVAL 1 /* 1 second */
@@ -209,7 +210,7 @@ report_createsub_log_v(enum pg_log_level level, enum pg_log_part part,
va_list arg_cpy;
va_copy(arg_cpy, args);
- internal_log_file_write(level, fmt, arg_cpy);
+ internal_log_file_write(level, part, fmt, arg_cpy);
va_end(arg_cpy);
/* Restore errno in case internal_log_file_write changed it */
errno = save_errno;
@@ -348,13 +349,6 @@ cleanup_objects_atexit(void)
if (standby_running)
stop_standby_server(subscriber_dir);
-
- if (internal_log_file_fp != NULL)
- {
- if (fclose(internal_log_file_fp) != 0)
- report_createsub_fatal("could not close %s/%s.log: %m", logdir, INTERNAL_LOG_FILE_NAME);
- internal_log_file_fp = NULL;
- }
}
static void
@@ -1081,8 +1075,8 @@ server_is_in_recovery(PGconn *conn)
}
static void
-internal_log_file_write(enum pg_log_level level, const char *pg_restrict fmt,
- va_list args)
+internal_log_file_write(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt, va_list args)
{
Assert(internal_log_file_fp);
@@ -1090,6 +1084,30 @@ internal_log_file_write(enum pg_log_level level, const char *pg_restrict fmt,
if (level < __pg_log_level)
return;
+ /* Append prefix based on the log part and log level */
+ switch (part)
+ {
+ case PG_LOG_PRIMARY:
+ switch (level)
+ {
+ case PG_LOG_ERROR:
+ fprintf(internal_log_file_fp, _("error: "));
+ break;
+ case PG_LOG_WARNING:
+ fprintf(internal_log_file_fp, _("warning: "));
+ break;
+ default:
+ break;
+ }
+ break;
+ case PG_LOG_DETAIL:
+ fprintf(internal_log_file_fp, _("detail: "));
+ break;
+ case PG_LOG_HINT:
+ fprintf(internal_log_file_fp, _("hint: "));
+ break;
+ }
+
vfprintf(internal_log_file_fp, _(fmt), args);
fprintf(internal_log_file_fp, "\n");
@@ -1098,28 +1116,15 @@ internal_log_file_write(enum pg_log_level level, const char *pg_restrict fmt,
/*
* Open a new logfile with proper permissions.
- * From src/backend/postmaster/syslogger.c
*/
static FILE *
logfile_open(const char *filename, const char *mode)
{
FILE *fh;
- mode_t oumask;
- oumask = umask((mode_t) ((~(S_IRUSR | S_IWUSR)) & (S_IRWXU | S_IRWXG | S_IRWXO)));
fh = fopen(filename, mode);
- umask(oumask);
- if (fh)
- {
- setvbuf(fh, NULL, PG_IOLBF, 0);
-
-#ifdef WIN32
- /* use CRLF line endings on Windows */
- _setmode(_fileno(fh), _O_TEXT);
-#endif
- }
- else
+ if (!fh)
report_createsub_fatal("could not open log file \"%s\": %m",
filename);
@@ -1151,8 +1156,8 @@ make_output_dirs(const char *log_basedir)
len = snprintf(logdir, MAXPGPATH, "%s/%s", log_basedir, timestamp);
if (len >= MAXPGPATH)
- report_createsub_fatal("directory path for log files, %s/%s, is too long",
- logdir, timestamp);
+ report_createsub_fatal("directory path for log files \"%s/%s\" is too long",
+ log_basedir, timestamp);
/* Create base directory (ignore if exists) */
if (mkdir(log_basedir, pg_dir_create_mode) < 0 && errno != EEXIST)
@@ -2756,10 +2761,20 @@ main(int argc, char **argv)
exit(1);
}
+ /* Rudimentary check for a data directory */
+ check_data_directory(subscriber_dir);
+
if (opt.log_dir != NULL)
{
char *internal_log_file;
+ /* Set mask based on the PGDATA permissions */
+ if (!GetDataDirectoryCreatePerm(subscriber_dir))
+ report_createsub_fatal("could not read permissions of directory \"%s\": %m",
+ subscriber_dir);
+
+ umask(pg_mode_mask);
+
make_output_dirs(opt.log_dir);
internal_log_file = psprintf("%s/%s.log", logdir,
INTERNAL_LOG_FILE_NAME);
@@ -2875,9 +2890,6 @@ main(int argc, char **argv)
pg_ctl_path = get_exec_path(argv[0], "pg_ctl");
pg_resetwal_path = get_exec_path(argv[0], "pg_resetwal");
- /* Rudimentary check for a data directory */
- check_data_directory(subscriber_dir);
-
dbinfos.two_phase = opt.two_phase;
/*
--
2.47.3
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-20 11:37 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-20 17:00 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-21 09:57 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-21 23:09 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-23 03:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber shveta malik <[email protected]>
2026-03-23 07:16 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-23 09:54 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-24 01:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-24 10:06 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
@ 2026-03-24 12:13 ` Amit Kapila <[email protected]>
1 sibling, 0 replies; 55+ messages in thread
From: Amit Kapila @ 2026-03-24 12:13 UTC (permalink / raw)
To: Hayato Kuroda (Fujitsu) <[email protected]>; +Cc: Gyan Sreejith <[email protected]>; shveta malik <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>
On Tue, Mar 24, 2026 at 3:36 PM Hayato Kuroda (Fujitsu)
<[email protected]> wrote:
>
> I created a top-up patch set which addressed all comments from me and others.
> See attached.
>
+ /* Set mask based on the PGDATA permissions */
+ if (!GetDataDirectoryCreatePerm(subscriber_dir))
+ report_createsub_fatal("could not read permissions of directory \"%s\": %m",
+ subscriber_dir);
We can add a bit more detailed comments as to why we use
data_directory permissions.
--
With Regards,
Amit Kapila.
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-20 11:37 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-20 17:00 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-21 09:57 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-21 23:09 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-23 03:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber shveta malik <[email protected]>
2026-03-23 07:16 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-23 09:54 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-24 01:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-24 10:06 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
@ 2026-03-24 23:14 ` Peter Smith <[email protected]>
2026-03-25 00:12 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
1 sibling, 1 reply; 55+ messages in thread
From: Peter Smith @ 2026-03-24 23:14 UTC (permalink / raw)
To: Hayato Kuroda (Fujitsu) <[email protected]>; +Cc: Gyan Sreejith <[email protected]>; Amit Kapila <[email protected]>; shveta malik <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>
Hi. Here are some review comments for v18-0001.
======
1.
+#define SERVER_LOG_FILE_NAME "pg_createsubscriber_server"
+#define INTERNAL_LOG_FILE_NAME "pg_createsubscriber_internal"
Those are not the names of logfiles; they are just the first part of
the names. Why not also put the ".log" part here, instead of burying
it in format strings scattered in the code?
SUGGESTION (e.g., more similar to pg_upgrade.h)
#define SERVER_LOG_FILE_NAME "pg_createsubscriber_server.log"
#define INTERNAL_LOG_FILE_NAME "pg_createsubscriber_internal.log"
~~~
2.
+static char logdir[MAXPGPATH]; /* Directory log files are put (if specified) */
Missing words in that comment?
e.g.
Directory where log files are written ...
Directory for log files ...
It might also be useful to mention that this is more than just the
original user-specified --logdir directory; it also includes the
timestamp sub-dir.
~~~
3.
/*
- * Report a message with a given log level
+ * Report a message with a given log level to stderr and log file
+ * (if specified).
*/
+static void
+report_createsub_log_v(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt, va_list args)
This is a function comment, but there is no way to specify the log
file by using this function, so maybe reword that "if specified" part
for clarity.
SUGGESTION:
* Report a message with a given log level.
* Writes to stderr, and also to the log file (if pg_createsubscriber
--logdir option was specified).
~~~
4.
+ /* Build timestamp directory path */
+ len = snprintf(logdir, MAXPGPATH, "%s/%s", log_basedir, timestamp);
+
+ if (len >= MAXPGPATH)
+ report_createsub_fatal("directory path for log files, %s/%s, is too long",
+ logdir, timestamp);
IIUC, when the snprintf exceeds MAXPGPATH, then 'logdir' is going to
contain a truncated string. And, maybe that already includes a partial
truncated timestamp part:
"myreallylongname/20260"
AFAICT, the subsequent fatal message is going to report that
incorrectly when it appends the timestamp a second time, like:
"... myreallylongname/20260/20260119T204317.204 is too long"
Notice that pg_upgrade doesn't try to report the names of files that
are too long. It just says things like "directory path for new cluster
is too long".
======
Kind Regards,
Peter Smith.
Fujitsu Australia
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-20 11:37 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-20 17:00 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-21 09:57 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-21 23:09 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-23 03:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber shveta malik <[email protected]>
2026-03-23 07:16 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-23 09:54 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-24 01:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-24 10:06 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-24 23:14 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
@ 2026-03-25 00:12 ` Gyan Sreejith <[email protected]>
2026-03-25 08:25 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
0 siblings, 1 reply; 55+ messages in thread
From: Gyan Sreejith @ 2026-03-25 00:12 UTC (permalink / raw)
To: Peter Smith <[email protected]>; +Cc: Hayato Kuroda (Fujitsu) <[email protected]>; Amit Kapila <[email protected]>; shveta malik <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>
Thank you, Kuroda-san and Chao for the suggestions and the patch. I have
merged it with v18-0001, and I have fixed everything that Peter suggested.
Regards,
Gyan
Attachments:
[application/octet-stream] v19-0001-Add-a-new-argument-l-logdir-to-pg_createsubscrib.patch (17.1K, 3-v19-0001-Add-a-new-argument-l-logdir-to-pg_createsubscrib.patch)
download | inline diff:
From 98f4e323cc84003990bddb403134907a27eb96fe Mon Sep 17 00:00:00 2001
From: Gyan Sreejith <[email protected]>
Date: Tue, 24 Mar 2026 20:02:58 -0400
Subject: [PATCH v19] Add a new argument -l <logdir> to pg_createsubscriber.
Enabling the option to write messages to log files in the specified directory.
A new directory is created if required. A subdirectory is created with timestamp as its name, and it will contain two new logfiles:
1. pg_createsubscriber_server.log - captures messages related to starting and stopping the standby server.
2. pg_createsubscriber_internal.log - captures internal diagnostic output from pg_createsubscriber.
For example, if we specify -l abc as an argument, and if the timestamp on running it is 20260119T204317.204, a directory abc is created if it doesn't exist already, with 20260119T204317.204 as its subdirectory and it will contain the two log files pg_createsubscriber_server.log and pg_createsubscriber_internal.log
---
doc/src/sgml/ref/pg_createsubscriber.sgml | 29 +++
src/bin/pg_basebackup/pg_createsubscriber.c | 192 +++++++++++++++++-
.../t/040_pg_createsubscriber.pl | 48 ++++-
3 files changed, 254 insertions(+), 15 deletions(-)
diff --git a/doc/src/sgml/ref/pg_createsubscriber.sgml b/doc/src/sgml/ref/pg_createsubscriber.sgml
index 6e17cee18eb..5ac61c7b558 100644
--- a/doc/src/sgml/ref/pg_createsubscriber.sgml
+++ b/doc/src/sgml/ref/pg_createsubscriber.sgml
@@ -136,6 +136,35 @@ PostgreSQL documentation
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>-l <replaceable class="parameter">directory</replaceable></option></term>
+ <term><option>--logdir=<replaceable class="parameter">directory</replaceable></option></term>
+ <listitem>
+ <para>
+ 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 <application>pg_createsubscriber</application>
+ 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.
+ <itemizedlist>
+ <listitem>
+ <para>
+ <filename>pg_createsubscriber_server.log</filename> which captures logs
+ related to stopping and starting the standby server,
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <filename>pg_createsubscriber_internal.log</filename> which captures
+ internal diagnostic output (validations, checks, etc.)
+ </para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>-n</option></term>
<term><option>--dry-run</option></term>
diff --git a/src/bin/pg_basebackup/pg_createsubscriber.c b/src/bin/pg_basebackup/pg_createsubscriber.c
index b2bc9dae0b8..d795e2cb47e 100644
--- a/src/bin/pg_basebackup/pg_createsubscriber.c
+++ b/src/bin/pg_basebackup/pg_createsubscriber.c
@@ -20,6 +20,7 @@
#include "common/connect.h"
#include "common/controldata_utils.h"
+#include "common/file_perm.h"
#include "common/file_utils.h"
#include "common/logging.h"
#include "common/pg_prng.h"
@@ -49,10 +50,14 @@
#define INCLUDED_CONF_FILE "pg_createsubscriber.conf"
#define INCLUDED_CONF_FILE_DISABLED INCLUDED_CONF_FILE ".disabled"
+#define SERVER_LOG_FILE_NAME "pg_createsubscriber_server.log"
+#define INTERNAL_LOG_FILE_NAME "pg_createsubscriber_internal.log"
+
/* Command-line options */
struct CreateSubscriberOptions
{
char *config_file; /* configuration file */
+ char *log_dir; /* log directory name */
char *pub_conninfo_str; /* publisher connection string */
char *socket_dir; /* directory for Unix-domain socket, if any */
char *sub_port; /* subscriber port number */
@@ -149,8 +154,15 @@ static void get_publisher_databases(struct CreateSubscriberOptions *opt,
static void report_createsub_log(enum pg_log_level, enum pg_log_part,
const char *pg_restrict fmt,...)
pg_attribute_printf(3, 4);
+static void report_createsub_log_v(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt, va_list args)
+ pg_attribute_printf(3, 0);
pg_noreturn static void report_createsub_fatal(const char *pg_restrict fmt,...)
pg_attribute_printf(1, 2);
+static void internal_log_file_write(enum pg_log_level level,
+ enum pg_log_part part,
+ const char *pg_restrict fmt, va_list args)
+ pg_attribute_printf(3, 0);
#define WAIT_INTERVAL 1 /* 1 second */
@@ -172,6 +184,11 @@ static pg_prng_state prng_state;
static char *pg_ctl_path = NULL;
static char *pg_resetwal_path = NULL;
+static FILE *internal_log_file_fp = NULL; /* File ptr to log all messages to */
+static char logdir[MAXPGPATH]; /* Subdirectory of the user specified logdir
+ * where the log files are written (if
+ * specified) */
+
/* standby / subscriber data directory */
static char *subscriber_dir = NULL;
@@ -180,8 +197,29 @@ static bool standby_running = false;
static bool recovery_params_set = false;
/*
- * Report a message with a given log level
+ * Report a message with a given log level.
+ * Writes to stderr, and also to the log file (if pg_createsubscriber --logdir option was specified)
*/
+static void
+report_createsub_log_v(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt, va_list args)
+{
+ int save_errno = errno;
+
+ if (internal_log_file_fp != NULL)
+ {
+ /* Output to both stderr and the log file */
+ va_list arg_cpy;
+
+ va_copy(arg_cpy, args);
+ internal_log_file_write(level, part, fmt, arg_cpy);
+ va_end(arg_cpy);
+ /* Restore errno in case internal_log_file_write changed it */
+ errno = save_errno;
+ }
+ pg_log_generic_v(level, part, fmt, args);
+}
+
static void
report_createsub_log(enum pg_log_level level, enum pg_log_part part,
const char *pg_restrict fmt,...)
@@ -190,7 +228,7 @@ report_createsub_log(enum pg_log_level level, enum pg_log_part part,
va_start(args, fmt);
- pg_log_generic_v(level, part, fmt, args);
+ report_createsub_log_v(level, part, fmt, args);
va_end(args);
}
@@ -205,7 +243,7 @@ report_createsub_fatal(const char *pg_restrict fmt,...)
va_start(args, fmt);
- pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, args);
+ report_createsub_log_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, args);
va_end(args);
@@ -327,6 +365,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 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"));
@@ -761,6 +800,7 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
bool crc_ok;
struct timeval tv;
+ char *out_file;
char *cmd_str;
report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
@@ -799,8 +839,20 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
"running pg_resetwal on the subscriber");
- cmd_str = psprintf("\"%s\" -D \"%s\" > \"%s\"", pg_resetwal_path,
- subscriber_dir, DEVNULL);
+ /*
+ * Redirecting the output to the logfile if specified. Since the output
+ * would be very short, around one line, we do not provide a separate file
+ * for it; it's done as a part of the server log.
+ */
+ if (opt->log_dir)
+ out_file = psprintf("%s/%s", logdir, SERVER_LOG_FILE_NAME);
+ else
+ out_file = DEVNULL;
+
+ cmd_str = psprintf("\"%s\" -D \"%s\" >> \"%s\"", pg_resetwal_path,
+ subscriber_dir, out_file);
+ if (opt->log_dir)
+ pg_free(out_file);
report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
"pg_resetwal command is: %s", cmd_str);
@@ -817,6 +869,7 @@ modify_subscriber_sysid(const struct CreateSubscriberOptions *opt)
}
pg_free(cf);
+ pg_free(cmd_str);
}
/*
@@ -1023,6 +1076,99 @@ server_is_in_recovery(PGconn *conn)
return ret == 0;
}
+static void
+internal_log_file_write(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt, va_list args)
+{
+ Assert(internal_log_file_fp);
+
+ /* Do nothing if log level is too low. */
+ if (level < __pg_log_level)
+ return;
+
+ /* Append prefix based on the log part and log level */
+ switch (part)
+ {
+ case PG_LOG_PRIMARY:
+ switch (level)
+ {
+ case PG_LOG_ERROR:
+ fprintf(internal_log_file_fp, _("error: "));
+ break;
+ case PG_LOG_WARNING:
+ fprintf(internal_log_file_fp, _("warning: "));
+ break;
+ default:
+ break;
+ }
+ break;
+ case PG_LOG_DETAIL:
+ fprintf(internal_log_file_fp, _("detail: "));
+ break;
+ case PG_LOG_HINT:
+ fprintf(internal_log_file_fp, _("hint: "));
+ break;
+ }
+
+ vfprintf(internal_log_file_fp, _(fmt), args);
+
+ fprintf(internal_log_file_fp, "\n");
+ fflush(internal_log_file_fp);
+}
+
+/*
+ * Open a new logfile with proper permissions.
+ */
+static FILE *
+logfile_open(const char *filename, const char *mode)
+{
+ FILE *fh;
+
+ fh = fopen(filename, mode);
+
+ if (!fh)
+ report_createsub_fatal("could not open log file \"%s\": %m",
+ filename);
+
+ return fh;
+}
+
+static void
+make_output_dirs(const char *log_basedir)
+{
+ char timestamp[128];
+ struct timeval tval;
+ time_t now;
+ struct tm tmbuf;
+ 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));
+
+ /* Append milliseconds */
+ snprintf(timestamp + strlen(timestamp),
+ sizeof(timestamp) - strlen(timestamp), ".%03u",
+ (unsigned int) (tval.tv_usec / 1000));
+
+ /* Build timestamp directory path */
+ len = snprintf(logdir, MAXPGPATH, "%s/%s", log_basedir, timestamp);
+
+ if (len >= MAXPGPATH)
+ report_createsub_fatal("directory path for log files (basedir/timestamp) is too long");
+
+ /* Create base directory (ignore if exists) */
+ if (mkdir(log_basedir, pg_dir_create_mode) < 0 && errno != EEXIST)
+ report_createsub_fatal("could not create directory \"%s\": %m", log_basedir);
+
+ /* Create a timestamp-named subdirectory under the base directory */
+ if (mkdir(logdir, pg_dir_create_mode) < 0)
+ report_createsub_fatal("could not create directory \"%s\": %m", logdir);
+}
+
/*
* Is the primary server ready for logical replication?
*
@@ -1781,6 +1927,9 @@ 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)
+ appendPQExpBuffer(pg_ctl_cmd, " -l \"%s/%s\"", logdir, SERVER_LOG_FILE_NAME);
+
report_createsub_log(PG_LOG_DEBUG, PG_LOG_PRIMARY,
"pg_ctl command is: %s", pg_ctl_cmd->data);
rc = system(pg_ctl_cmd->data);
@@ -2351,6 +2500,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'},
@@ -2409,6 +2559,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;
@@ -2439,7 +2590,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)
@@ -2460,6 +2611,10 @@ 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);
+ break;
case 'n':
dry_run = true;
break;
@@ -2607,6 +2762,28 @@ main(int argc, char **argv)
exit(1);
}
+ /* Rudimentary check for a data directory */
+ check_data_directory(subscriber_dir);
+
+ if (opt.log_dir != NULL)
+ {
+ char *internal_log_file;
+
+ /* Set mask based on the PGDATA permissions */
+ if (!GetDataDirectoryCreatePerm(subscriber_dir))
+ report_createsub_fatal("could not read permissions of directory \"%s\": %m",
+ subscriber_dir);
+
+ umask(pg_mode_mask);
+
+ make_output_dirs(opt.log_dir);
+ internal_log_file = psprintf("%s/%s", logdir, INTERNAL_LOG_FILE_NAME);
+
+ /* logfile_open() will exit if there is an error */
+ internal_log_file_fp = logfile_open(internal_log_file, "a");
+ pg_free(internal_log_file);
+ }
+
if (dry_run)
report_createsub_log(PG_LOG_INFO, PG_LOG_PRIMARY,
"Executing in dry-run mode.\n"
@@ -2713,9 +2890,6 @@ main(int argc, char **argv)
pg_ctl_path = get_exec_path(argv[0], "pg_ctl");
pg_resetwal_path = get_exec_path(argv[0], "pg_resetwal");
- /* Rudimentary check for a data directory */
- check_data_directory(subscriber_dir);
-
dbinfos.two_phase = opt.two_phase;
/*
diff --git a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
index 0c27fca7bb7..239ea58d9a0 100644
--- a/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
+++ b/src/bin/pg_basebackup/t/040_pg_createsubscriber.pl
@@ -5,6 +5,8 @@
use strict;
use warnings FATAL => 'all';
+use File::Basename;
+use File::stat;
use PostgreSQL::Test::Cluster;
use PostgreSQL::Test::Utils;
use Test::More;
@@ -14,6 +16,7 @@ program_version_ok('pg_createsubscriber');
program_options_handling_ok('pg_createsubscriber');
my $datadir = PostgreSQL::Test::Utils::tempdir;
+my $logdir = PostgreSQL::Test::Utils::tempdir;
# Generate a database with a name made of a range of ASCII characters.
# Extracted from 002_pg_upgrade.pl.
@@ -362,9 +365,40 @@ command_ok(
'--subscription' => 'sub2',
'--database' => $db1,
'--database' => $db2,
+ '--logdir' => $logdir,
],
'run pg_createsubscriber --dry-run on node S');
+# Check that the log files were created
+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 $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 $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");
+my $timestamp_dir = dirname($internal_log_files[0]);
+my $timestamp_dir_stat = stat($timestamp_dir);
+my $timestamp_dir_mode = $timestamp_dir_stat->mode & 07777;
+is($timestamp_dir_mode, 0700,
+ "Directory with .log files has permissions S_IRWXU");
+
# Check if node S is still a standby
$node_s->start;
is($node_s->safe_psql('postgres', 'SELECT pg_catalog.pg_is_in_recovery()'),
@@ -444,7 +478,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);
));
@@ -540,8 +575,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');
@@ -555,8 +589,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(
@@ -581,7 +617,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");
--
2.43.0
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-20 11:37 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-20 17:00 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-21 09:57 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-21 23:09 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-23 03:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber shveta malik <[email protected]>
2026-03-23 07:16 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-23 09:54 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-24 01:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-24 10:06 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-24 23:14 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2026-03-25 00:12 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
@ 2026-03-25 08:25 ` Amit Kapila <[email protected]>
2026-03-28 16:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-04-01 04:56 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-04-01 05:05 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
0 siblings, 3 replies; 55+ messages in thread
From: Amit Kapila @ 2026-03-25 08:25 UTC (permalink / raw)
To: Gyan Sreejith <[email protected]>; +Cc: Peter Smith <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; shveta malik <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>
On Wed, Mar 25, 2026 at 5:43 AM Gyan Sreejith <[email protected]> wrote:
>
> Thank you, Kuroda-san and Chao for the suggestions and the patch. I have merged it with v18-0001, and I have fixed everything that Peter suggested.
>
Pushed after minor changes.
--
With Regards,
Amit Kapila.
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-20 11:37 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-20 17:00 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-21 09:57 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-21 23:09 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-23 03:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber shveta malik <[email protected]>
2026-03-23 07:16 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-23 09:54 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-24 01:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-24 10:06 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-24 23:14 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2026-03-25 00:12 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-25 08:25 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
@ 2026-03-28 16:47 ` Gyan Sreejith <[email protected]>
2 siblings, 0 replies; 55+ messages in thread
From: Gyan Sreejith @ 2026-03-28 16:47 UTC (permalink / raw)
To: Amit Kapila <[email protected]>; +Cc: Peter Smith <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; shveta malik <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>
Thank you everybody for the help.
Regards,
Gyan
On Wed, Mar 25, 2026 at 4:25 AM Amit Kapila <[email protected]> wrote:
> On Wed, Mar 25, 2026 at 5:43 AM Gyan Sreejith <[email protected]>
> wrote:
> >
> > Thank you, Kuroda-san and Chao for the suggestions and the patch. I have
> merged it with v18-0001, and I have fixed everything that Peter suggested.
> >
>
> Pushed after minor changes.
>
> --
> With Regards,
> Amit Kapila.
>
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-20 11:37 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-20 17:00 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-21 09:57 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-21 23:09 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-23 03:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber shveta malik <[email protected]>
2026-03-23 07:16 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-23 09:54 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-24 01:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-24 10:06 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-24 23:14 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2026-03-25 00:12 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-25 08:25 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
@ 2026-04-01 04:56 ` Amit Kapila <[email protected]>
2026-04-07 13:36 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Eisentraut <[email protected]>
2 siblings, 1 reply; 55+ messages in thread
From: Amit Kapila @ 2026-04-01 04:56 UTC (permalink / raw)
To: Peter Eisentraut <[email protected]>; +Cc: Gyan Sreejith <[email protected]>; Peter Smith <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; shveta malik <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>
On Tue, Mar 31, 2026 at 5:36 PM Peter Eisentraut <[email protected]> wrote:
>
> On 25.03.26 09:25, Amit Kapila wrote:
> > On Wed, Mar 25, 2026 at 5:43 AM Gyan Sreejith <[email protected]> wrote:
> >>
> >> Thank you, Kuroda-san and Chao for the suggestions and the patch. I have merged it with v18-0001, and I have fixed everything that Peter suggested.
> >>
> >
> > Pushed after minor changes.
>
> Instead of creating a new logging API in pg_createsubscriber locally,
> why not extend logging.c to support a log file.
>
Few things we may want to consider if we want to use extended
logging.c: (a) what if we want information to be written in multiple
files (say pub/sub specific info into separate files), something like
what pg_upgrade does, (b) what if we want to write some info to only
one of terminal or log_file, we can probably extend generic API for
this.
> This is much simpler,
> doesn't require caller changes, and could be reused elsewhere. See
> attached patches. (The 0003 and 0004 are just some additional code
> simplifications.)
>
In 0004,
-static char logdir[MAXPGPATH]; /* Subdirectory of the user specified logdir
+static char *logdir = NULL; /* Subdirectory of the user specified logdir
At other places like pg_upgrade, we use MAXPGPATH for a similar case
probably to have standard length on all platforms, see
make_outputdirs() in pg_upgrade.c.
--
With Regards,
Amit Kapila.
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-20 11:37 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-20 17:00 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-21 09:57 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-21 23:09 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-23 03:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber shveta malik <[email protected]>
2026-03-23 07:16 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-23 09:54 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-24 01:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-24 10:06 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-24 23:14 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2026-03-25 00:12 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-25 08:25 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-04-01 04:56 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
@ 2026-04-07 13:36 ` Peter Eisentraut <[email protected]>
0 siblings, 0 replies; 55+ messages in thread
From: Peter Eisentraut @ 2026-04-07 13:36 UTC (permalink / raw)
To: Amit Kapila <[email protected]>; +Cc: Gyan Sreejith <[email protected]>; Peter Smith <[email protected]>; Hayato Kuroda (Fujitsu) <[email protected]>; shveta malik <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>
On 01.04.26 06:56, Amit Kapila wrote:
> On Tue, Mar 31, 2026 at 5:36 PM Peter Eisentraut <[email protected]> wrote:
>>
>> On 25.03.26 09:25, Amit Kapila wrote:
>>> On Wed, Mar 25, 2026 at 5:43 AM Gyan Sreejith <[email protected]> wrote:
>>>>
>>>> Thank you, Kuroda-san and Chao for the suggestions and the patch. I have merged it with v18-0001, and I have fixed everything that Peter suggested.
>>>>
>>>
>>> Pushed after minor changes.
>>
>> Instead of creating a new logging API in pg_createsubscriber locally,
>> why not extend logging.c to support a log file.
>>
>
> Few things we may want to consider if we want to use extended
> logging.c: (a) what if we want information to be written in multiple
> files (say pub/sub specific info into separate files), something like
> what pg_upgrade does, (b) what if we want to write some info to only
> one of terminal or log_file, we can probably extend generic API for
> this.
In the proposed API, you can switch the log file at any time. And there
could be a separate log level for file output versus terminal output.
>> This is much simpler,
>> doesn't require caller changes, and could be reused elsewhere. See
>> attached patches. (The 0003 and 0004 are just some additional code
>> simplifications.)
>>
>
> In 0004,
> -static char logdir[MAXPGPATH]; /* Subdirectory of the user specified logdir
> +static char *logdir = NULL; /* Subdirectory of the user specified logdir
>
> At other places like pg_upgrade, we use MAXPGPATH for a similar case
> probably to have standard length on all platforms, see
> make_outputdirs() in pg_upgrade.c.
I think using MAXPGPATH is kind of deprecated and useless, certainly in
new code. But apart from that, note that in pg_createsubscriber.c
itself, subscriber_dir is dynamically allocated.
^ permalink raw reply [nested|flat] 55+ messages in thread
* RE: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-20 11:37 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-20 17:00 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-21 09:57 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-21 23:09 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-23 03:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber shveta malik <[email protected]>
2026-03-23 07:16 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-23 09:54 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-24 01:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-24 10:06 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-24 23:14 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2026-03-25 00:12 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-25 08:25 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
@ 2026-04-01 05:05 ` Hayato Kuroda (Fujitsu) <[email protected]>
2026-04-07 13:37 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Eisentraut <[email protected]>
2 siblings, 1 reply; 55+ messages in thread
From: Hayato Kuroda (Fujitsu) @ 2026-04-01 05:05 UTC (permalink / raw)
To: 'Peter Eisentraut' <[email protected]>; Amit Kapila <[email protected]>; Gyan Sreejith <[email protected]>; +Cc: Peter Smith <[email protected]>; shveta malik <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>
Dear Peter E.,
> Instead of creating a new logging API in pg_createsubscriber locally,
> why not extend logging.c to support a log file.
I had a concern to extend the common module. Do you have possible idea to use
pg_logging_set_logfile() on others? I cannot, tee command on linux seems enough
for others.
Best regards,
Hayato Kuroda
FUJITSU LIMITED
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-20 11:37 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-20 17:00 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-21 09:57 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-21 23:09 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-23 03:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber shveta malik <[email protected]>
2026-03-23 07:16 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-23 09:54 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-24 01:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-24 10:06 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-24 23:14 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2026-03-25 00:12 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-25 08:25 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-04-01 05:05 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
@ 2026-04-07 13:37 ` Peter Eisentraut <[email protected]>
2026-04-08 05:30 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
0 siblings, 1 reply; 55+ messages in thread
From: Peter Eisentraut @ 2026-04-07 13:37 UTC (permalink / raw)
To: Hayato Kuroda (Fujitsu) <[email protected]>; Amit Kapila <[email protected]>; Gyan Sreejith <[email protected]>; +Cc: Peter Smith <[email protected]>; shveta malik <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>
On 01.04.26 07:05, Hayato Kuroda (Fujitsu) wrote:
> Dear Peter E.,
>
>> Instead of creating a new logging API in pg_createsubscriber locally,
>> why not extend logging.c to support a log file.
>
> I had a concern to extend the common module. Do you have possible idea to use
> pg_logging_set_logfile() on others? I cannot, tee command on linux seems enough
> for others.
pg_upgrade was mentioned.
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-20 11:37 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-20 17:00 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-21 09:57 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-21 23:09 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-23 03:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber shveta malik <[email protected]>
2026-03-23 07:16 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-23 09:54 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-24 01:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-24 10:06 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-24 23:14 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2026-03-25 00:12 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-25 08:25 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-04-01 05:05 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-04-07 13:37 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Eisentraut <[email protected]>
@ 2026-04-08 05:30 ` Amit Kapila <[email protected]>
2026-04-13 09:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Eisentraut <[email protected]>
0 siblings, 1 reply; 55+ messages in thread
From: Amit Kapila @ 2026-04-08 05:30 UTC (permalink / raw)
To: Peter Eisentraut <[email protected]>; +Cc: Hayato Kuroda (Fujitsu) <[email protected]>; Gyan Sreejith <[email protected]>; Peter Smith <[email protected]>; shveta malik <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>
On Tue, Apr 7, 2026 at 7:07 PM Peter Eisentraut <[email protected]> wrote:
>
> On 01.04.26 07:05, Hayato Kuroda (Fujitsu) wrote:
> > Dear Peter E.,
> >
> >> Instead of creating a new logging API in pg_createsubscriber locally,
> >> why not extend logging.c to support a log file.
> >
> > I had a concern to extend the common module. Do you have possible idea to use
> > pg_logging_set_logfile() on others? I cannot, tee command on linux seems enough
> > for others.
>
> pg_upgrade was mentioned.
>
I see that pg_upgrade has some specific requirements related to
verbose/status/report kind of messages which doesn't seem to be
directly handled by the generic API pg_log_generic(). For example,
apart from other things, we need to unset/reset log_file when we want
to send messages only to the terminal and quite some specific handling
for status or other kinds of messages. If you think pg_log_generic
could be extended to handle various upgrade cases then I think it
would be a good use case for it and we should go-ahead with what you
are proposing. OTOH, maybe we can give it a separate try and use the
resulting API for both pg_createsubscriber and pg_upgrade. I am fine
either way you would like to go-ahead with this.
--
With Regards,
Amit Kapila.
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-20 11:37 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-20 17:00 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-21 09:57 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-21 23:09 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-23 03:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber shveta malik <[email protected]>
2026-03-23 07:16 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-23 09:54 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-24 01:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-24 10:06 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-24 23:14 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2026-03-25 00:12 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-25 08:25 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-04-01 05:05 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-04-07 13:37 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Eisentraut <[email protected]>
2026-04-08 05:30 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
@ 2026-04-13 09:24 ` Peter Eisentraut <[email protected]>
2026-04-16 00:20 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
0 siblings, 1 reply; 55+ messages in thread
From: Peter Eisentraut @ 2026-04-13 09:24 UTC (permalink / raw)
To: Amit Kapila <[email protected]>; +Cc: Hayato Kuroda (Fujitsu) <[email protected]>; Gyan Sreejith <[email protected]>; Peter Smith <[email protected]>; shveta malik <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>
On 08.04.26 07:30, Amit Kapila wrote:
> On Tue, Apr 7, 2026 at 7:07 PM Peter Eisentraut <[email protected]> wrote:
>>
>> On 01.04.26 07:05, Hayato Kuroda (Fujitsu) wrote:
>>> Dear Peter E.,
>>>
>>>> Instead of creating a new logging API in pg_createsubscriber locally,
>>>> why not extend logging.c to support a log file.
>>>
>>> I had a concern to extend the common module. Do you have possible idea to use
>>> pg_logging_set_logfile() on others? I cannot, tee command on linux seems enough
>>> for others.
>>
>> pg_upgrade was mentioned.
>>
>
> I see that pg_upgrade has some specific requirements related to
> verbose/status/report kind of messages which doesn't seem to be
> directly handled by the generic API pg_log_generic(). For example,
> apart from other things, we need to unset/reset log_file when we want
> to send messages only to the terminal and quite some specific handling
> for status or other kinds of messages. If you think pg_log_generic
> could be extended to handle various upgrade cases then I think it
> would be a good use case for it and we should go-ahead with what you
> are proposing. OTOH, maybe we can give it a separate try and use the
> resulting API for both pg_createsubscriber and pg_upgrade. I am fine
> either way you would like to go-ahead with this.
I have committed these patches.
I would like to apply the frontend logging API to pg_upgrade as well,
but that's a bigger project for not right now.
^ permalink raw reply [nested|flat] 55+ messages in thread
* RE: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-20 11:37 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-20 17:00 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-21 09:57 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-21 23:09 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-23 03:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber shveta malik <[email protected]>
2026-03-23 07:16 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-23 09:54 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-24 01:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-24 10:06 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-24 23:14 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2026-03-25 00:12 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-25 08:25 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-04-01 05:05 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-04-07 13:37 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Eisentraut <[email protected]>
2026-04-08 05:30 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-04-13 09:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Eisentraut <[email protected]>
@ 2026-04-16 00:20 ` Hayato Kuroda (Fujitsu) <[email protected]>
0 siblings, 0 replies; 55+ messages in thread
From: Hayato Kuroda (Fujitsu) @ 2026-04-16 00:20 UTC (permalink / raw)
To: 'Peter Eisentraut' <[email protected]>; Amit Kapila <[email protected]>; +Cc: Gyan Sreejith <[email protected]>; Peter Smith <[email protected]>; shveta malik <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>
Dear Peter,
> I have committed these patches.
Thanks!
> I would like to apply the frontend logging API to pg_upgrade as well,
> but that's a bigger project for not right now.
Right, it may need huge changes. Not sure it can be done for PG19 because
we have already been in feature freeze.
Best regards,
Hayato Kuroda
FUJITSU LIMITED
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2025-12-18 01:28 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2025-12-18 06:49 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2025-12-23 23:22 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-05 14:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Euler Taveira <[email protected]>
2026-03-06 10:50 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-09 22:55 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber vignesh C <[email protected]>
2026-03-15 23:23 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-17 12:48 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-18 13:15 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-18 13:44 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-20 11:37 ` RE: [Proposal] Adding Log File Capability to pg_createsubscriber Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-20 17:00 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2026-03-21 09:57 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Amit Kapila <[email protected]>
2026-03-21 23:09 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
@ 2026-03-23 09:55 ` shveta malik <[email protected]>
1 sibling, 0 replies; 55+ messages in thread
From: shveta malik @ 2026-03-23 09:55 UTC (permalink / raw)
To: Gyan Sreejith <[email protected]>; +Cc: Amit Kapila <[email protected]>; Kuroda, Hayato/黒田 隼人 <[email protected]>; Shlok Kyal <[email protected]>; vignesh C <[email protected]>; Euler Taveira <[email protected]>; [email protected] <[email protected]>; Peter Smith <[email protected]>; shveta malik <[email protected]>
On Sun, Mar 22, 2026 at 4:39 AM Gyan Sreejith <[email protected]> wrote:
>
>
> On Sat, Mar 21, 2026 at 5:57 AM Amit Kapila <[email protected]> wrote:
>>
>>
>> Based on the above information, can we consider renaming the above
>>
>> functions to report_createsub_log() and report_createsub_fatal()?
>>
>> Other than the above point, 0001 LGTM.
>
>
> I have renamed the functions.
>
Thanks. Few comments on 002:
1)
We can get rid of below alignment related changes in unrelated test parts.
----
-$node_p->safe_psql($db1, qq(
+$node_p->safe_psql(
+ $db1, qq(
-is($result, qq(),
- "table is not replicated in database $db1");
+is($result, qq(), "table is not replicated in database $db1");
-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");
-is($result, qq($db1|{test_pub3}
+is( $result, qq($db1|{test_pub3}
----
2)
Can we simplify the logic of report_createsub_log_v to:
---
if (internal_log_file_fp != NULL)
{
va_list arg_cpy;
va_copy(arg_cpy, args);
internal_log_file_write(level, fmt, arg_cpy);
va_end(arg_cpy);
}
pg_log_generic_v(level, part, fmt, args);
---
We need not to invoke pg_log_generic_v in both if and else.
3)
+ /* Create base directory (ignore if exists) */
+ if (mkdir(log_basedir, S_IRWXU) < 0 && errno != EEXIST)
+ pg_fatal("could not create directory \"%s\": %m", log_basedir);
+
+ /* Create BASE_DIR/$timestamp */
+ if (mkdir(logdir, S_IRWXU) < 0)
+ pg_fatal("could not create directory \"%s\": %m", logdir);
--
Instead of S_IRWXU directly, shall we use pg_dir_create_mode (which
means S_IRWXU) similar to other modules (pg_upgrade, initdb, pg_dump
etc)
See pg_upgrade:
if (mkdir(log_opts.logdir, pg_dir_create_mode) < 0)
pg_fatal("could not create directory \"%s\": %m",
log_opts.logdir);
4)
+ /* Create BASE_DIR/$timestamp */
Above comment refers to BASE_DIR which looks like some variable, but
it is not. Can we please change this comment to:
/* Create a timestamp-named subdirectory under the base directory */
5)
+ /* append milliseconds */
append -->Append
thanks
Shveta
^ permalink raw reply [nested|flat] 55+ messages in thread
* Re: [Proposal] Adding Log File Capability to pg_createsubscriber
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Peter Smith <[email protected]>
2025-12-12 01:33 ` Re: [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
@ 2025-12-16 23:49 ` Peter Smith <[email protected]>
1 sibling, 0 replies; 55+ messages in thread
From: Peter Smith @ 2025-12-16 23:49 UTC (permalink / raw)
To: Gyan Sreejith <[email protected]>; +Cc: [email protected]; vignesh C <[email protected]>
On Fri, Dec 12, 2025 at 12:33 PM Gyan Sreejith <[email protected]> wrote:
>
> Thanks for the feedback, Peter.
>
> I am currently working on the SGML docs update, and will promptly get back with an update.
>
> For your second point, currently, all output goes directly to the console. I thought it made more sense to break it up into multiple files depending on what was being invoked. Do you have another opinion?
>
The point I was trying to make was that if you are going to have a
"--logdir" option, then IMO that option should do nothing other than
change the destination for the logs. In other words, current behaviour
is effectively --logdir=console, so --logdir=some_other_folder should
not have some side-effect causing the log to get split into multiple
files.
If you want to split logs, then I thought Kuroda-San's suggestion [1]
sounded better --- (a) mimic pg_upgrade more closely and (b)
reconsider if -logdir is needed at all.
======
[1] https://www.postgresql.org/message-id/OSCPR01MB14966FD0961F512B29BD46D6BF5AAA%40OSCPR01MB14966.jpnpr...
Kind Regards,
Peter Smith.
Fujitsu Australia
^ permalink raw reply [nested|flat] 55+ messages in thread
end of thread, other threads:[~2026-04-16 00:20 UTC | newest]
Thread overview: 55+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2025-12-09 22:16 [Proposal] Adding Log File Capability to pg_createsubscriber Gyan Sreejith <[email protected]>
2025-12-11 07:29 ` Peter Smith <[email protected]>
2025-12-12 01:33 ` Gyan Sreejith <[email protected]>
2025-12-14 15:47 ` Gyan Sreejith <[email protected]>
2025-12-16 07:01 ` Hayato Kuroda (Fujitsu) <[email protected]>
2025-12-17 10:07 ` vignesh C <[email protected]>
2025-12-18 01:28 ` Euler Taveira <[email protected]>
2025-12-18 06:49 ` Amit Kapila <[email protected]>
2025-12-23 23:22 ` Gyan Sreejith <[email protected]>
2025-12-29 11:10 ` vignesh C <[email protected]>
2026-03-05 14:48 ` Euler Taveira <[email protected]>
2026-03-06 10:50 ` Amit Kapila <[email protected]>
2026-03-09 22:55 ` Gyan Sreejith <[email protected]>
2026-03-11 10:04 ` vignesh C <[email protected]>
2026-03-15 23:23 ` Gyan Sreejith <[email protected]>
2026-03-17 06:05 ` shveta malik <[email protected]>
2026-03-17 10:40 ` Amit Kapila <[email protected]>
2026-03-17 12:18 ` Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-17 12:48 ` Shlok Kyal <[email protected]>
2026-03-17 23:24 ` Gyan Sreejith <[email protected]>
2026-03-18 06:12 ` Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-18 10:03 ` shveta malik <[email protected]>
2026-03-18 13:15 ` Amit Kapila <[email protected]>
2026-03-18 13:44 ` Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-19 11:55 ` Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-19 18:29 ` Gyan Sreejith <[email protected]>
2026-03-20 10:30 ` Nisha Moond <[email protected]>
2026-03-20 11:37 ` Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-20 17:00 ` Gyan Sreejith <[email protected]>
2026-03-21 09:57 ` Amit Kapila <[email protected]>
2026-03-21 23:09 ` Gyan Sreejith <[email protected]>
2026-03-23 03:15 ` shveta malik <[email protected]>
2026-03-23 07:16 ` Amit Kapila <[email protected]>
2026-03-23 09:54 ` Kuroda, Hayato/黒田 隼人 <[email protected]>
2026-03-24 01:23 ` Gyan Sreejith <[email protected]>
2026-03-24 03:02 ` Chao Li <[email protected]>
2026-03-24 03:31 ` Chao Li <[email protected]>
2026-03-24 09:50 ` Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-24 06:31 ` Amit Kapila <[email protected]>
2026-03-24 09:47 ` Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-24 10:06 ` Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-24 12:13 ` Amit Kapila <[email protected]>
2026-03-24 23:14 ` Peter Smith <[email protected]>
2026-03-25 00:12 ` Gyan Sreejith <[email protected]>
2026-03-25 08:25 ` Amit Kapila <[email protected]>
2026-03-28 16:47 ` Gyan Sreejith <[email protected]>
2026-04-01 04:56 ` Amit Kapila <[email protected]>
2026-04-07 13:36 ` Peter Eisentraut <[email protected]>
2026-04-01 05:05 ` Hayato Kuroda (Fujitsu) <[email protected]>
2026-04-07 13:37 ` Peter Eisentraut <[email protected]>
2026-04-08 05:30 ` Amit Kapila <[email protected]>
2026-04-13 09:24 ` Peter Eisentraut <[email protected]>
2026-04-16 00:20 ` Hayato Kuroda (Fujitsu) <[email protected]>
2026-03-23 09:55 ` shveta malik <[email protected]>
2025-12-16 23:49 ` Peter Smith <[email protected]>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox