From 20762f88ad95ea1a3d09352e294993e0ab7d0d19 Mon Sep 17 00:00:00 2001
From: Fujii Masao <fujii@postgresql.org>
Date: Wed, 25 Mar 2026 11:02:17 +0900
Subject: [PATCH v2 2/2] Add trace_logical_decoding_messages GUC to control
 logical decoding logging

This commit introduces a new GUC, trace_logical_decoding_messages, to control
the logging of logical decoding debug messages. This is similar to the former
trace_recovery_messages for recovery debug messages.

This parameter overrides log_min_messages for logical decoding messages only
and is intended for debugging. Valid values are DEBUG5 through DEBUG1, and LOG.
Messages at the configured level or higher are logged as if they were at
LOG level. The default is DEBUG1.

Some logical decoding messages (e.g., "logical decoding found consistent
point") are low-level and were previously considered too verbose for LOG.
However, based on discussion, some users still find it useful to see these
messages at LOG level for debugging. Therefore, with the default setting
(DEBUG1), these messages are logged at LOG level. On the other hand,
setting trace_logical_decoding_messages to LOG can suppress them.
---
 doc/src/sgml/config.sgml                     | 25 ++++++++++++++++++++
 src/backend/replication/logical/logical.c    |  2 +-
 src/backend/replication/logical/logicalctl.c | 24 +++++++++++++++++++
 src/backend/replication/logical/snapbuild.c  |  8 +++----
 src/backend/utils/misc/guc_parameters.dat    | 11 +++++++++
 src/backend/utils/misc/guc_tables.c          |  1 +
 src/include/replication/logicalctl.h         |  3 +++
 7 files changed, 69 insertions(+), 5 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 8cdd826fbd3..b48a80c5508 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -12595,6 +12595,31 @@ dynamic_library_path = '/usr/local/lib/postgresql:$libdir'
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-trace-logical-decoding-messages" xreflabel="trace_logical_decoding_messages">
+      <term><varname>trace_logical_decoding_messages</varname> (<type>enum</type>)
+      <indexterm>
+       <primary><varname>trace_logical_decoding_messages</varname> configuration parameter</primary>
+      </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Enables logging of logical decoding-related debugging output that
+        otherwise would not be logged.  This parameter allows the user to
+        override the normal setting of <xref linkend="guc-log-min-messages"/>,
+        but only for specific messages.  This is intended for use in debugging
+        logical decoding.  Valid values are <literal>DEBUG5</literal>,
+        <literal>DEBUG4</literal>, <literal>DEBUG3</literal>,
+        <literal>DEBUG2</literal>, <literal>DEBUG1</literal>, and
+        <literal>LOG</literal>.  The setting values cause logical
+        decoding-related debug messages of that priority or higher to be logged
+        as though they had <literal>LOG</literal> priority; for common settings
+        of <varname>log_min_messages</varname> this results in unconditionally
+        sending them to the server log.
+        The default is <literal>DEBUG1</literal>.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-trace-notify" xreflabel="trace_notify">
       <term><varname>trace_notify</varname> (<type>boolean</type>)
       <indexterm>
diff --git a/src/backend/replication/logical/logical.c b/src/backend/replication/logical/logical.c
index f20d0c542f3..7d3c0da70f6 100644
--- a/src/backend/replication/logical/logical.c
+++ b/src/backend/replication/logical/logical.c
@@ -598,7 +598,7 @@ CreateDecodingContext(XLogRecPtr start_lsn,
 
 	ctx->reorder->output_rewrites = ctx->options.receive_rewrites;
 
-	ereport(DEBUG1,
+	ereport(trace_logical_decoding(DEBUG1),
 			(errmsg("starting logical decoding for slot \"%s\"",
 					NameStr(slot->data.name)),
 			 errdetail("Streaming transactions committing after %X/%08X, reading WAL from %X/%08X.",
diff --git a/src/backend/replication/logical/logicalctl.c b/src/backend/replication/logical/logicalctl.c
index 4e292951201..d735a63d828 100644
--- a/src/backend/replication/logical/logicalctl.c
+++ b/src/backend/replication/logical/logicalctl.c
@@ -116,6 +116,9 @@ bool		XLogLogicalInfo = false;
  */
 static bool XLogLogicalInfoUpdatePending = false;
 
+/* GUC variables */
+int			trace_logical_decoding_messages = DEBUG1;
+
 static void update_xlog_logical_info(void);
 static void abort_logical_decoding_activation(int code, Datum arg);
 static void write_logical_decoding_status_update_record(bool status);
@@ -638,3 +641,24 @@ UpdateLogicalDecodingStatusEndOfRecovery(void)
 
 	INJECTION_POINT("startup-logical-decoding-status-change-end-of-recovery", NULL);
 }
+
+/*
+ * Adjust the level of a logical decoding-related message per
+ * trace_logical_decoding_messages.
+ *
+ * The argument is the default log level of the message, eg, DEBUG2.  (This
+ * should only be applied to DEBUGn log messages, otherwise it's a no-op.)
+ * If the level is >= trace_logical_decoding_messages, we return LOG, causing
+ * the message to be logged unconditionally (for most settings of
+ * log_min_messages).  Otherwise, we return the argument unchanged.
+ * The message will then be shown based on the setting of log_min_messages.
+ */
+int
+trace_logical_decoding(int trace_level)
+{
+	if (trace_level < LOG &&
+		trace_level >= trace_logical_decoding_messages)
+		return LOG;
+
+	return trace_level;
+}
diff --git a/src/backend/replication/logical/snapbuild.c b/src/backend/replication/logical/snapbuild.c
index b4269a3b102..6c878cbcfd0 100644
--- a/src/backend/replication/logical/snapbuild.c
+++ b/src/backend/replication/logical/snapbuild.c
@@ -1312,7 +1312,7 @@ SnapBuildFindSnapshot(SnapBuild *builder, XLogRecPtr lsn, xl_running_xacts *runn
 		builder->state = SNAPBUILD_CONSISTENT;
 		builder->next_phase_at = InvalidTransactionId;
 
-		ereport(DEBUG1,
+		ereport(trace_logical_decoding(DEBUG1),
 				errmsg("logical decoding found consistent point at %X/%08X",
 					   LSN_FORMAT_ARGS(lsn)),
 				errdetail("There are no running transactions."));
@@ -1385,7 +1385,7 @@ SnapBuildFindSnapshot(SnapBuild *builder, XLogRecPtr lsn, xl_running_xacts *runn
 		builder->state = SNAPBUILD_FULL_SNAPSHOT;
 		builder->next_phase_at = running->nextXid;
 
-		ereport(LOG,
+		ereport(trace_logical_decoding(DEBUG1),
 				errmsg("logical decoding found initial consistent point at %X/%08X",
 					   LSN_FORMAT_ARGS(lsn)),
 				errdetail("Waiting for transactions (approximately %d) older than %u to end.",
@@ -1409,7 +1409,7 @@ SnapBuildFindSnapshot(SnapBuild *builder, XLogRecPtr lsn, xl_running_xacts *runn
 		builder->state = SNAPBUILD_CONSISTENT;
 		builder->next_phase_at = InvalidTransactionId;
 
-		ereport(DEBUG1,
+		ereport(trace_logical_decoding(DEBUG1),
 				errmsg("logical decoding found consistent point at %X/%08X",
 					   LSN_FORMAT_ARGS(lsn)),
 				errdetail("There are no old transactions anymore."));
@@ -1915,7 +1915,7 @@ SnapBuildRestore(SnapBuild *builder, XLogRecPtr lsn)
 
 	Assert(builder->state == SNAPBUILD_CONSISTENT);
 
-	ereport(DEBUG1,
+	ereport(trace_logical_decoding(DEBUG1),
 			errmsg("logical decoding found consistent point at %X/%08X",
 				   LSN_FORMAT_ARGS(lsn)),
 			errdetail("Logical decoding will begin using saved snapshot."));
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index 0c9854ad8fc..04cd0d8c3be 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -3039,6 +3039,17 @@
   ifdef => 'LOCK_DEBUG',
 },
 
+# client_message_level_options allows too many values, really, but
+# it's not worth having a separate options array for this.
+{ name => 'trace_logical_decoding_messages', type => 'enum', context => 'PGC_USERSET', group => 'DEVELOPER_OPTIONS',
+  short_desc => 'Enables logging of logical decoding-related debugging information.',
+  long_desc => 'Each level includes all the levels that follow it. The later the level, the fewer messages are sent.',
+  flags => 'GUC_NOT_IN_SAMPLE',
+  variable => 'trace_logical_decoding_messages',
+  boot_val => 'DEBUG1',
+  options => 'client_message_level_options',
+},
+
 { name => 'trace_lwlocks', type => 'bool', context => 'PGC_SUSET', group => 'DEVELOPER_OPTIONS',
   short_desc => 'Emits information about lightweight lock usage.',
   flags => 'GUC_NOT_IN_SAMPLE',
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 1e14b7b4af0..8e83021afa3 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -72,6 +72,7 @@
 #include "postmaster/syslogger.h"
 #include "postmaster/walsummarizer.h"
 #include "postmaster/walwriter.h"
+#include "replication/logicalctl.h"
 #include "replication/logicallauncher.h"
 #include "replication/slot.h"
 #include "replication/slotsync.h"
diff --git a/src/include/replication/logicalctl.h b/src/include/replication/logicalctl.h
index 495554c532c..6b2db5ba273 100644
--- a/src/include/replication/logicalctl.h
+++ b/src/include/replication/logicalctl.h
@@ -14,6 +14,8 @@
 #ifndef LOGICALCTL_H
 #define LOGICALCTL_H
 
+extern PGDLLIMPORT int trace_logical_decoding_messages;
+
 extern Size LogicalDecodingCtlShmemSize(void);
 extern void LogicalDecodingCtlShmemInit(void);
 extern void StartupLogicalDecodingStatus(bool last_status);
@@ -28,5 +30,6 @@ extern void RequestDisableLogicalDecoding(void);
 extern void DisableLogicalDecodingIfNecessary(void);
 extern void DisableLogicalDecoding(void);
 extern void UpdateLogicalDecodingStatusEndOfRecovery(void);
+extern int	trace_logical_decoding(int trace_level);
 
 #endif
-- 
2.51.2

