On Wed, 16 Jul 2025 at 21:30, Fujii Masao <masao.fu...@oss.nttdata.com> wrote:
>
>
>
> On 2025/07/16 19:45, vignesh C wrote:
> > If we don't trim the trailing newline, an extra blank line will appear
> > after the message is printed, like this:
> > 2025-07-16 12:44:20.076 IST [534376] LOG:  logical replication table
> > synchronization worker for subscription "sub1", table "t2" has started
> > 2025-07-16 12:44:20.294 IST [534374] LOG:  received message via
> > replication: WARNING:  could not remove directory
> > "pg_replslot/pg_16396_sync_16384_7527574647116269356.tmp"
> >
> > 2025-07-16 12:44:20.294 IST [534374] LOG:  logical replication table
> > synchronization worker for subscription "sub1", table "t1" has
> > finished
> >
> > This occurs because a newline is appended to the message in
> > pqBuildErrorMessage3, which is called when the message is received in
> > pqGetErrorNotice3:
> > ...
> > }
> > appendPQExpBufferChar(msg, '\n');
> > if (verbosity != PQERRORS_TERSE)
> > ...
>
> You're right, the message text passed to the notice processor includes
> a trailing newline. I confirmed this is documented in the PQsetNoticeProcessor
> docs [1], so I agree it's reasonable to trim the newline for cleaner log 
> output.
>
>
> Regarding the current implementation:
> +       /* Trim trailing newline for cleaner logs */
> +       size_t          len = strlen(message);
> +
> +       if (len > 0 && message[len - 1] == '\n')
> +               ereport(LOG,
> +                               errmsg("received message via replication: 
> %.*s",
> +                                          (int) (len - 1), message));
> +       else
> +               ereport(LOG,
> +                               errmsg("received message via replication: 
> %s", message));
>
> To avoid repeating ereport(), how about refactoring it like this?
> I think it's simpler and easier to read:
>
> ---------------------------------------
> /*
>   * Custom notice processor for replication connection.
>   */
> static void
> notice_processor(void *arg, const char *message)
> {
>         int                     len;
>
>         /*
>          * Trim the trailing newline from the message text passed to the 
> notice
>          * processor, as it always includes one, to produce cleaner log 
> output.
>          */
>         len = strlen(message);
>         if (len > 0 && message[len - 1] == '\n')
>                 len--;
>
>         ereport(LOG,
>                         errmsg("received message via replication: %.*s",
>                                    len, message));
> }
> ---------------------------------------

Looks good, modified

> Also, I suggest adding the prototype for notice_processor() in 
> libpqwalreceiver.c:
>
> ---------------------------------------
> /* Prototypes for private functions */
> static void notice_processor(void *arg, const char *message);
> static char *stringlist_to_identifierstr(PGconn *conn, List *strings);
> ---------------------------------------
>
>
> Currently, notice_processor() is placed just before libpqrcv_connect(),
> but I think it would be better to move it before 
> stringlist_to_identifierstr(),
> since the libpqwalreceiver.c seems to follow a structure where private
> functions come after the libpqrcv_* functions.

Modified

>
> +       PQsetNoticeProcessor(conn->streamConn, notice_processor, NULL);
>
> It might be helpful to add a comment explaining the purpose, such as:
>
>      Set a custom notice processor so that notice, warning, and similar 
> messages
>      received via the replication connection are logged in our preferred 
> style,
>      instead of just being printed to stderr.

Modified

The attached v4 version patch has the changes for the same.

Regards,
Vignesh
From 2460fbd0a5c6a1be425cdaf92daa2800407e4fcb Mon Sep 17 00:00:00 2001
From: Vignesh C <vignes...@gmail.com>
Date: Wed, 16 Jul 2025 15:26:12 +0530
Subject: [PATCH v4 1/3] Add custom PQsetNoticeProcessor handlers for
 replication connection

This patch introduces a custom notice processor for libpq-based
connections in replication connection. The notice processor captures
messages and routes them through ereport(), making them visible in
local logs with a prefix making it easy for diagnosis.
---
 .../libpqwalreceiver/libpqwalreceiver.c       | 28 +++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c b/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
index f7b5d093681..689333d7c52 100644
--- a/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
+++ b/src/backend/replication/libpqwalreceiver/libpqwalreceiver.c
@@ -114,6 +114,7 @@ static WalReceiverFunctionsType PQWalReceiverFunctions = {
 };
 
 /* Prototypes for private functions */
+static void notice_processor(void *arg, const char *message);
 static char *stringlist_to_identifierstr(PGconn *conn, List *strings);
 
 /*
@@ -232,6 +233,13 @@ libpqrcv_connect(const char *conninfo, bool replication, bool logical,
 				 errhint("Target server's authentication method must be changed, or set password_required=false in the subscription parameters.")));
 	}
 
+	/*
+	 * Set a custom notice processor so that NOTICEs, WARNINGs, and similar
+	 * messages from the replication connection are reported via ereport(),
+	 * instead of being printed to stderr.
+	 */
+	PQsetNoticeProcessor(conn->streamConn, notice_processor, NULL);
+
 	/*
 	 * Set always-secure search path for the cases where the connection is
 	 * used to run SQL queries, so malicious users can't get control.
@@ -1195,6 +1203,26 @@ libpqrcv_exec(WalReceiverConn *conn, const char *query,
 	return walres;
 }
 
+/*
+ * Custom notice processor for replication connection.
+ */
+static void
+notice_processor(void *arg, const char *message)
+{
+	int			len;
+
+	/*
+	 * Trim the trailing newline from the message text passed to the notice
+	 * processor, as it always includes one, to produce cleaner log output.
+	 */
+	len = strlen(message);
+	if (len > 0 && message[len - 1] == '\n')
+		len--;
+
+	ereport(LOG,
+			errmsg("received message via replication: %.*s", len, message));
+}
+
 /*
  * Given a List of strings, return it as single comma separated
  * string, quoting identifiers as needed.
-- 
2.43.0

From 9a0c0ce671d3860f1611c4ca1ec7cf55ab43e6e4 Mon Sep 17 00:00:00 2001
From: Vignesh C <vignes...@gmail.com>
Date: Wed, 16 Jul 2025 15:31:54 +0530
Subject: [PATCH v4 2/3] Add custom PQsetNoticeProcessor handlers for dblink
 connection

This patch introduces a custom notice processor for libpq-based
connections in dblink connection.  The notice processor captures
messages and routes them through ereport(), making them visible
in local logs with a prefix making it easy for diagnosis.
---
 contrib/dblink/dblink.c | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index 8a0b112a7ff..30509f9ff32 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -137,6 +137,7 @@ static void appendSCRAMKeysInfo(StringInfo buf);
 static bool is_valid_dblink_fdw_option(const PQconninfoOption *options, const char *option,
 									   Oid context);
 static bool dblink_connstr_has_required_scram_options(const char *connstr);
+static void notice_processor(void *arg, const char *message);
 
 /* Global */
 static remoteConn *pconn = NULL;
@@ -240,6 +241,14 @@ dblink_get_conn(char *conname_or_str,
 					 errmsg("could not establish connection"),
 					 errdetail_internal("%s", msg)));
 		}
+
+		/*
+		 * Set a custom notice processor so that NOTICEs, WARNINGs, and
+		 * similar messages from the remote server connection are reported via
+		 * ereport(), instead of being printed to stderr.
+		 */
+		PQsetNoticeProcessor(conn, notice_processor, NULL);
+
 		dblink_security_check(conn, NULL, connstr);
 		if (PQclientEncoding(conn) != GetDatabaseEncoding())
 			PQsetClientEncoding(conn, GetDatabaseEncodingName());
@@ -338,6 +347,8 @@ dblink_connect(PG_FUNCTION_ARGS)
 				 errdetail_internal("%s", msg)));
 	}
 
+	PQsetNoticeProcessor(conn, notice_processor, NULL);
+
 	/* check password actually used if not superuser */
 	dblink_security_check(conn, connname, connstr);
 
@@ -2670,6 +2681,26 @@ dblink_connstr_has_required_scram_options(const char *connstr)
 	return (has_scram_keys && has_require_auth);
 }
 
+/*
+ * Custom notice processor for remote server connection.
+ */
+static void
+notice_processor(void *arg, const char *message)
+{
+	int			len;
+
+	/*
+	 * Trim the trailing newline from the message text passed to the notice
+	 * processor, as it always includes one, to produce cleaner log output.
+	 */
+	len = strlen(message);
+	if (len > 0 && message[len - 1] == '\n')
+		len--;
+
+	ereport(LOG,
+			errmsg("received message from remote server: %.*s", len, message));
+}
+
 /*
  * We need to make sure that the connection made used credentials
  * which were provided by the user, so check what credentials were
-- 
2.43.0

From 880d19658fdc3f9d4e31b1e6f1be670e1cb3dd46 Mon Sep 17 00:00:00 2001
From: Vignesh C <vignes...@gmail.com>
Date: Wed, 16 Jul 2025 15:32:41 +0530
Subject: [PATCH v4 3/3] Add custom PQsetNoticeProcessor handlers for fdw
 connection

This patch introduces a custom notice processor for libpq-based
connections in fdw connection. The notice processor captures
messages and routes them through ereport(), making them visible
in local logs with a prefix making it easy for diagnosis.
---
 contrib/postgres_fdw/connection.c | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 304f3c20f83..53264ecbf93 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -472,6 +472,26 @@ pgfdw_security_check(const char **keywords, const char **values, UserMapping *us
 			 errhint("Target server's authentication method must be changed or password_required=false set in the user mapping attributes.")));
 }
 
+/*
+ * Custom notice processor for remote server connection.
+ */
+static void
+notice_processor(void *arg, const char *message)
+{
+	int			len;
+
+	/*
+	 * Trim the trailing newline from the message text passed to the notice
+	 * processor, as it always includes one, to produce cleaner log output.
+	 */
+	len = strlen(message);
+	if (len > 0 && message[len - 1] == '\n')
+		len--;
+
+	ereport(LOG,
+			errmsg("received message from remote server: %.*s", len, message));
+}
+
 /*
  * Connect to remote server using specified server and user mapping properties.
  */
@@ -625,6 +645,13 @@ connect_pg_server(ForeignServer *server, UserMapping *user)
 							server->servername),
 					 errdetail_internal("%s", pchomp(PQerrorMessage(conn)))));
 
+		/*
+		 * Set a custom notice processor so that NOTICEs, WARNINGs, and
+		 * similar messages from the remote server connection are reported via
+		 * ereport(), instead of being printed to stderr.
+		 */
+		PQsetNoticeProcessor(conn, notice_processor, NULL);
+
 		/* Perform post-connection security checks. */
 		pgfdw_security_check(keywords, values, user, conn);
 
-- 
2.43.0

Reply via email to