agora inbox for pgsql-hackers@postgresql.org  
help / color / mirror / Atom feed
log_min_messages per backend type
58+ messages / 15 participants
[nested] [flat]

* log_min_messages per backend type
@ 2024-12-17 19:14  Euler Taveira <euler@eulerto.com>
  0 siblings, 3 replies; 58+ messages in thread

From: Euler Taveira @ 2024-12-17 19:14 UTC (permalink / raw)
  To: pgsql-hackers@lists.postgresql.org

Hi,

Sometimes you need to inspect some debug messages from autovacuum worker but
you cannot apply the same setting for backends (that could rapidly fill the log
file). This proposal aims to change log_min_messages to have different log
levels depending on which backend type the message comes from.

The syntax was changed from enum to string and it accepts a list of elements.

Instead of enum, it now accepts a comma-separated list of elements (string).
Each element is LOGLEVEL:BACKENDTYPE. It still accepts the old syntax (single
log level for backward compatibility. In this case, it sets all backend types
to the informed log level. For the list, the default log level (WARNING) is
used for the backend types that are not specified.

SET log_min_messages TO 'debug2:checkpointer, debug1:autovacuum';
SHOW log_min_messages;
            log_min_messages
----------------------------------------
debug2:checkpointer, debug1:autovacuum
(1 row)

In the above example, it sets log level DEBUG2 to checkpointer process and
DEBUG1 to autovacuum launcher and autovacuum worker processes. The other
processes keep WARNING (default) as log level.


--
Euler Taveira
EDB   https://www.enterprisedb.com/

Attachments:

  [text/x-patch] v1-0001-log_min_messages-per-backend-type.patch (18.8K, ../../e85c6671-1600-4112-8887-f97a8a5d07b2@app.fastmail.com/3-v1-0001-log_min_messages-per-backend-type.patch)
  download | inline diff:
From 8a63cdc0eaa402c722fcdbf33ce75999f8f52332 Mon Sep 17 00:00:00 2001
From: Euler Taveira <euler@eulerto.com>
Date: Mon, 11 Dec 2023 17:24:50 -0300
Subject: [PATCH v1] log_min_messages per backend type

Change log_min_messages from a single element to a comma-separated list
of elements. Each element is LOGLEVEL:BACKENDTYPE. The BACKENDTYPE are
archiver, autovacuum (includes launcher and workers), backend, bgworker,
bgwriter, checkpointer, logger, slotsyncworker, walreceiver, walsender,
walsummarizer and walwriter.

The old syntax (a single log level) is still accepted for backward
compatibility. In this case, it means the informed log level for all
backend types. The default log level is the same (WARNING) for all
backend types.
---
 doc/src/sgml/config.sgml                      |  30 ++-
 src/backend/commands/extension.c              |   2 +-
 src/backend/commands/variable.c               | 194 ++++++++++++++++++
 src/backend/utils/error/elog.c                |   2 +-
 src/backend/utils/misc/guc_tables.c           |  28 +--
 src/backend/utils/misc/postgresql.conf.sample |   1 +
 src/include/miscadmin.h                       |   2 +
 src/include/utils/guc.h                       |   2 +-
 src/include/utils/guc_hooks.h                 |   2 +
 src/include/utils/guc_tables.h                |   5 +
 src/test/regress/expected/guc.out             |  38 ++++
 src/test/regress/sql/guc.sql                  |  13 ++
 12 files changed, 294 insertions(+), 25 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 24bd504c21..005a7a5d4a 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -6913,7 +6913,7 @@ local0.*    /var/log/postgresql
      <variablelist>
 
      <varlistentry id="guc-log-min-messages" xreflabel="log_min_messages">
-      <term><varname>log_min_messages</varname> (<type>enum</type>)
+      <term><varname>log_min_messages</varname> (<type>string</type>)
       <indexterm>
        <primary><varname>log_min_messages</varname> configuration parameter</primary>
       </indexterm>
@@ -6922,14 +6922,26 @@ local0.*    /var/log/postgresql
        <para>
         Controls which <link linkend="runtime-config-severity-levels">message
         levels</link> are written to the server log.
-        Valid values are <literal>DEBUG5</literal>, <literal>DEBUG4</literal>,
-        <literal>DEBUG3</literal>, <literal>DEBUG2</literal>, <literal>DEBUG1</literal>,
-        <literal>INFO</literal>, <literal>NOTICE</literal>, <literal>WARNING</literal>,
-        <literal>ERROR</literal>, <literal>LOG</literal>, <literal>FATAL</literal>, and
-        <literal>PANIC</literal>.  Each level includes all the levels that
-        follow it.  The later the level, the fewer messages are sent
-        to the log.  The default is <literal>WARNING</literal>.  Note that
-        <literal>LOG</literal> has a different rank here than in
+        Valid values are a comma-separated list of <literal>LEVEL:BACKENDTYPE</literal>
+        or a single <literal>LEVEL</literal> (for backward compatibility). The list
+        allows it to use different levels per backend type.
+        Valid <literal>BACKENDTYPE</literal> values are <literal>ARCHIVER</literal>,
+        <literal>AUTOVACUUM</literal>, <literal>BACKEND</literal>,
+        <literal>BGWORKER</literal>, <literal>BGWRITER</literal>,
+        <literal>CHECKPOINTER</literal>, <literal>LOGGER</literal>,
+        <literal>SLOTSYNCWORKER</literal>, <literal>WALRECEIVER</literal>,
+        <literal>WALSENDER</literal>, <literal>WALSUMMARIZER</literal>, and
+        <literal>WALWRITER</literal>.
+        Valid <literal>LEVEL</literal> values are <literal>DEBUG5</literal>,
+        <literal>DEBUG4</literal>, <literal>DEBUG3</literal>, <literal>DEBUG2</literal>,
+        <literal>DEBUG1</literal>, <literal>INFO</literal>, <literal>NOTICE</literal>,
+        <literal>WARNING</literal>, <literal>ERROR</literal>, <literal>LOG</literal>,
+        <literal>FATAL</literal>, and <literal>PANIC</literal>.  Each level includes
+        all the levels that follow it.  The later the level, the fewer messages are sent
+        to the log. If a single <literal>LEVEL</literal> is informed, this level is
+        assigned to all backend types.  The default is <literal>WARNING</literal> for
+        all backend types.
+        Note that <literal>LOG</literal> has a different rank here than in
         <xref linkend="guc-client-min-messages"/>.
         Only superusers and users with the appropriate <literal>SET</literal>
         privilege can change this setting.
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index e683c520a8..dacd721d65 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1057,7 +1057,7 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
 		(void) set_config_option("client_min_messages", "warning",
 								 PGC_USERSET, PGC_S_SESSION,
 								 GUC_ACTION_SAVE, true, 0, false);
-	if (log_min_messages < WARNING)
+	if (log_min_messages[MyBackendType] < WARNING)
 		(void) set_config_option_ext("log_min_messages", "warning",
 									 PGC_SUSET, PGC_S_SESSION,
 									 BOOTSTRAP_SUPERUSERID,
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index d2db0c45b2..1784818375 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -35,12 +35,46 @@
 #include "utils/datetime.h"
 #include "utils/fmgrprotos.h"
 #include "utils/guc_hooks.h"
+#include "utils/guc_tables.h"
 #include "utils/snapmgr.h"
 #include "utils/syscache.h"
 #include "utils/timestamp.h"
 #include "utils/tzparser.h"
 #include "utils/varlena.h"
 
+struct log_min_messages_entry
+{
+	const char *name;
+	int			val;
+};
+
+/*
+ * The element index is the BackendType.
+ */
+static struct log_min_messages_entry log_min_messages_options[] = {
+	{"BACKEND", WARNING},		/* B_INVALID. XXX same as BACKEND? */
+	{"BACKEND", WARNING},		/* B_BACKEND */
+	{"BACKEND", WARNING},		/* B_DEAD_END_BACKEND. XXX same as BACKEND? */
+	{"AUTOVACUUM", WARNING},	/* B_AUTOVAC_LAUNCHER */
+	{"AUTOVACUUM", WARNING},	/* B_AUTOVAC_WORKER */
+	{"BGWORKER", WARNING},		/* B_BG_WORKER */
+	{"WALSENDER", WARNING},		/* B_WAL_SENDER */
+	{"SLOTSYNCWORKER", WARNING},	/* B_SLOTSYNC_WORKER */
+	{"BACKEND", WARNING},		/* B_STANDALONE_BACKEND. XXX same as BACKEND? */
+	{"ARCHIVER", WARNING},		/* B_ARCHIVER */
+	{"BGWRITER", WARNING},		/* B_BG_WRITER */
+	{"CHECKPOINTER", WARNING},	/* B_CHECKPOINTER */
+	{"BACKEND", WARNING},		/* B_STARTUP. XXX same as BACKEND? */
+	{"WALRECEIVER", WARNING},	/* B_WAL_RECEIVER */
+	{"WALSUMMARIZER", WARNING}, /* B_WAL_SUMMARIZER */
+	{"WALWRITER", WARNING},		/* B_WAL_WRITER */
+	{"LOGGER", WARNING},		/* B_LOGGER */
+	{NULL, 0},
+};
+
+StaticAssertDecl(lengthof(log_min_messages_options) == BACKEND_NUM_TYPES + 1,
+				 "array length mismatch");
+
 /*
  * DATESTYLE
  */
@@ -1269,3 +1303,163 @@ check_ssl(bool *newval, void **extra, GucSource source)
 #endif
 	return true;
 }
+
+/*
+ * GUC check_hook for log_min_messages
+ *
+ * The parsing consists of a comma-separated list of LEVEL:BACKENDTYPENAME
+ * elements. LEVEL is server_message_level_options. BACKENDTYPENAME is the
+ * first member of log_min_messages_options. A single LEVEL element is accepted
+ * for backward compatibility.
+ */
+bool
+check_log_min_messages(char **newval, void **extra, GucSource source)
+{
+	char	   *rawstring;
+	List	   *elemlist;
+	ListCell   *l;
+	int			newlogminmsgs[BACKEND_NUM_TYPES];
+
+	/* Initialize the array. */
+	memset(newlogminmsgs, WARNING, BACKEND_NUM_TYPES * sizeof(int));
+
+	/* Need a modifiable copy of string. */
+	rawstring = pstrdup(*newval);
+
+	/* Parse string into list of identifiers. */
+	if (!SplitGUCList(rawstring, ',', &elemlist))
+	{
+		/* syntax error in list */
+		GUC_check_errdetail("List syntax is invalid.");
+		pfree(rawstring);
+		list_free(elemlist);
+		return false;
+	}
+
+	/* Validate and assign log level and backend type. */
+	foreach(l, elemlist)
+	{
+		char	   *tok = (char *) lfirst(l);
+		int			btid;
+		char	   *sep;
+		const struct config_enum_entry *entry;
+
+		/*
+		 * Check whether there is a backend type following the log level. If
+		 * there is no separator, it means this log level should be applied
+		 * for all backend types (backward compatibility).
+		 */
+		sep = strchr(tok, ':');
+		if (sep == NULL)
+		{
+			bool		found = false;
+
+			/*
+			 * It only accepts value without backend type if the list has only
+			 * one element.
+			 */
+			if (list_length(elemlist) > 1)
+			{
+				GUC_check_errdetail("Log level must provide a backend type.");
+				pfree(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			/* Is the log level valid? */
+			for (entry = server_message_level_options; entry && entry->name; entry++)
+			{
+				if (pg_strcasecmp(entry->name, tok) == 0)
+				{
+					for (btid = 0; btid < BACKEND_NUM_TYPES; btid++)
+						newlogminmsgs[btid] = entry->val;
+					found = true;
+					break;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized log level: \"%s\".", tok);
+				pfree(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+		}
+		else
+		{
+			char	   *loglevel;
+			char	   *btype;
+			bool		found = false;
+			const struct log_min_messages_entry *bentry;
+
+			loglevel = palloc((sep - tok) + 1);
+			memcpy(loglevel, tok, sep - tok);
+			loglevel[sep - tok] = '\0';
+			btype = pstrdup(sep + 1);
+
+			/* Is the log level valid? */
+			for (entry = server_message_level_options; entry && entry->name; entry++)
+			{
+				if (pg_strcasecmp(entry->name, loglevel) == 0)
+				{
+					found = true;
+					break;
+				}
+			}
+
+			/* FIXME provide a list of log levels in the errdetail. */
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel);
+				pfree(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			/* Is the backend type name valid? */
+			found = false;
+			btid = 0;
+			for (bentry = log_min_messages_options; bentry && bentry->name; bentry++)
+			{
+				if (pg_strcasecmp(bentry->name, btype) == 0)
+				{
+					newlogminmsgs[btid] = entry->val;
+					found = true;
+				}
+				btid++;
+			}
+
+			/* FIXME provide a list of backend types in the errdetail. */
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized backend type: \"%s\".", btype);
+				pfree(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+		}
+	}
+
+	pfree(rawstring);
+	list_free(elemlist);
+
+	/*
+	 * Pass back data for assign_log_min_messages to use.
+	 */
+	*extra = guc_malloc(LOG, BACKEND_NUM_TYPES * sizeof(int));
+	if (!*extra)
+		return false;
+	memcpy(*extra, newlogminmsgs, BACKEND_NUM_TYPES * sizeof(int));
+
+	return true;
+}
+
+/*
+ * GUC assign_hook for log_min_messages
+ */
+void
+assign_log_min_messages(const char *newval, void *extra)
+{
+	log_min_messages = (int *) extra;
+}
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 289059435a..eab42d4a1e 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -234,7 +234,7 @@ is_log_level_output(int elevel, int log_min_level)
 static inline bool
 should_output_to_server(int elevel)
 {
-	return is_log_level_output(elevel, log_min_messages);
+	return is_log_level_output(elevel, log_min_messages[MyBackendType]);
 }
 
 /*
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 8cf1afbad2..dfaef0abcc 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -137,7 +137,7 @@ static const struct config_enum_entry client_message_level_options[] = {
 	{NULL, 0, false}
 };
 
-static const struct config_enum_entry server_message_level_options[] = {
+const struct config_enum_entry server_message_level_options[] = {
 	{"debug5", DEBUG5, false},
 	{"debug4", DEBUG4, false},
 	{"debug3", DEBUG3, false},
@@ -519,7 +519,8 @@ static bool default_with_oids = false;
 bool		current_role_is_superuser;
 
 int			log_min_error_statement = ERROR;
-int			log_min_messages = WARNING;
+char	   *log_min_messages_string = NULL;
+int		   *log_min_messages = NULL;
 int			client_min_messages = NOTICE;
 int			log_min_duration_sample = -1;
 int			log_min_duration_statement = -1;
@@ -4161,6 +4162,18 @@ struct config_string ConfigureNamesString[] =
 		check_client_encoding, assign_client_encoding, NULL
 	},
 
+	{
+		{"log_min_messages", PGC_SUSET, LOGGING_WHEN,
+			gettext_noop("Sets the message levels that are logged."),
+			gettext_noop("Each level includes all the levels that follow it. The later"
+						 " the level, the fewer messages are sent."),
+			GUC_LIST_INPUT
+		},
+		&log_min_messages_string,
+		"WARNING",
+		check_log_min_messages, assign_log_min_messages, NULL
+	},
+
 	{
 		{"log_line_prefix", PGC_SIGHUP, LOGGING_WHAT,
 			gettext_noop("Controls information prefixed to each log line."),
@@ -4938,17 +4951,6 @@ struct config_enum ConfigureNamesEnum[] =
 		NULL, NULL, NULL
 	},
 
-	{
-		{"log_min_messages", PGC_SUSET, LOGGING_WHEN,
-			gettext_noop("Sets the message levels that are logged."),
-			gettext_noop("Each level includes all the levels that follow it. The later"
-						 " the level, the fewer messages are sent.")
-		},
-		&log_min_messages,
-		WARNING, server_message_level_options,
-		NULL, NULL, NULL
-	},
-
 	{
 		{"log_min_error_statement", PGC_SUSET, LOGGING_WHEN,
 			gettext_noop("Causes all statements generating error at or above this level to be logged."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index a2ac7575ca..5f0f7f4a8f 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -527,6 +527,7 @@
 					#   log
 					#   fatal
 					#   panic
+					#   or a comma-separated list of log level:backend type
 
 #log_min_error_statement = error	# values in order of decreasing detail:
 					#   debug5
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 3f97fcef80..6d3dd96f95 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -332,6 +332,8 @@ extern void SwitchBackToLocalLatch(void);
  *
  * If you add entries, please also update the child_process_kinds array in
  * launch_backend.c.
+ * XXX If you add a new backend type or change the order, update
+ * log_min_messages_options because it relies on this order to work correctly.
  */
 typedef enum BackendType
 {
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index 840b0fe57f..adfcfd8ca3 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -267,7 +267,7 @@ extern PGDLLIMPORT bool log_duration;
 extern PGDLLIMPORT int log_parameter_max_length;
 extern PGDLLIMPORT int log_parameter_max_length_on_error;
 extern PGDLLIMPORT int log_min_error_statement;
-extern PGDLLIMPORT int log_min_messages;
+extern PGDLLIMPORT int *log_min_messages;
 extern PGDLLIMPORT int client_min_messages;
 extern PGDLLIMPORT int log_min_duration_sample;
 extern PGDLLIMPORT int log_min_duration_statement;
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index 5813dba0a2..4b59034261 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -174,5 +174,7 @@ extern void assign_wal_sync_method(int new_wal_sync_method, void *extra);
 extern bool check_synchronized_standby_slots(char **newval, void **extra,
 											 GucSource source);
 extern void assign_synchronized_standby_slots(const char *newval, void *extra);
+extern bool check_log_min_messages(char **newval, void **extra, GucSource source);
+extern void assign_log_min_messages(const char *newval, void *extra);
 
 #endif							/* GUC_HOOKS_H */
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index 0c0277c423..2dc0359ecb 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -280,6 +280,11 @@ struct config_enum
 	void	   *reset_extra;
 };
 
+/*
+ * XXX Is there a better place for it?
+ */
+extern PGDLLIMPORT const struct config_enum_entry server_message_level_options[];
+
 /* constant tables corresponding to enums above and in guc.h */
 extern PGDLLIMPORT const char *const config_group_names[];
 extern PGDLLIMPORT const char *const config_type_names[];
diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out
index 7f9e29c765..2c1f377205 100644
--- a/src/test/regress/expected/guc.out
+++ b/src/test/regress/expected/guc.out
@@ -913,3 +913,41 @@ SELECT name FROM tab_settings_flags
 (0 rows)
 
 DROP TABLE tab_settings_flags;
+-- Test log_min_messages
+SET log_min_messages TO fatal;
+SHOW log_min_messages;
+ log_min_messages 
+------------------
+ fatal
+(1 row)
+
+SET log_min_messages TO 'fatal';
+SHOW log_min_messages;
+ log_min_messages 
+------------------
+ fatal
+(1 row)
+
+SET log_min_messages TO 'error:backend, fatal, debug1:archiver';	-- fail
+ERROR:  invalid value for parameter "log_min_messages": "error:backend, fatal, debug1:archiver"
+DETAIL:  Log level must provide a backend type.
+SET log_min_messages TO 'error:backend, fatal:foo, debug1:archiver';	-- fail
+ERROR:  invalid value for parameter "log_min_messages": "error:backend, fatal:foo, debug1:archiver"
+DETAIL:  Unrecognized backend type: "foo".
+SET log_min_messages TO 'error:backend, bar:checkpointer, debug1:archiver';	-- fail
+ERROR:  invalid value for parameter "log_min_messages": "error:backend, bar:checkpointer, debug1:archiver"
+DETAIL:  Unrecognized log level: "bar".
+SET log_min_messages TO 'error:backend, debug3:checkpointer, debug2:archiver, debug1:autovacuum, debug3:walsender';
+SHOW log_min_messages;
+                                     log_min_messages                                     
+------------------------------------------------------------------------------------------
+ error:backend, debug3:checkpointer, debug2:archiver, debug1:autovacuum, debug3:walsender
+(1 row)
+
+SET log_min_messages TO 'debug2:checkpointer, debug1:autovacuum';
+SHOW log_min_messages;
+            log_min_messages            
+----------------------------------------
+ debug2:checkpointer, debug1:autovacuum
+(1 row)
+
diff --git a/src/test/regress/sql/guc.sql b/src/test/regress/sql/guc.sql
index f65f84a263..c7d7b6551d 100644
--- a/src/test/regress/sql/guc.sql
+++ b/src/test/regress/sql/guc.sql
@@ -368,3 +368,16 @@ SELECT name FROM tab_settings_flags
   WHERE no_reset AND NOT no_reset_all
   ORDER BY 1;
 DROP TABLE tab_settings_flags;
+
+-- Test log_min_messages
+SET log_min_messages TO fatal;
+SHOW log_min_messages;
+SET log_min_messages TO 'fatal';
+SHOW log_min_messages;
+SET log_min_messages TO 'error:backend, fatal, debug1:archiver';	-- fail
+SET log_min_messages TO 'error:backend, fatal:foo, debug1:archiver';	-- fail
+SET log_min_messages TO 'error:backend, bar:checkpointer, debug1:archiver';	-- fail
+SET log_min_messages TO 'error:backend, debug3:checkpointer, debug2:archiver, debug1:autovacuum, debug3:walsender';
+SHOW log_min_messages;
+SET log_min_messages TO 'debug2:checkpointer, debug1:autovacuum';
+SHOW log_min_messages;
-- 
2.39.5



^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2024-12-19 04:13  Kirill Reshke <reshkekirill@gmail.com>
  parent: Euler Taveira <euler@eulerto.com>
  2 siblings, 1 reply; 58+ messages in thread

From: Kirill Reshke @ 2024-12-19 04:13 UTC (permalink / raw)
  To: Euler Taveira <euler@eulerto.com>; +Cc: pgsql-hackers@lists.postgresql.org

On Wed, 18 Dec 2024 at 00:15, Euler Taveira <euler@eulerto.com> wrote:
>
> Hi,
>
> Sometimes you need to inspect some debug messages from autovacuum worker but
> you cannot apply the same setting for backends (that could rapidly fill the log
> file). This proposal aims to change log_min_messages to have different log
> levels depending on which backend type the message comes from.

Hi! Looks like a sane proposal. Correct me if I'm wrong, but with your
patch it is not possible to configure something like "everybody ERROR,
but autovac DEBUG5"? I mean,
set log_min_messages to 'ERROR:default, DEBUG5:autovacuum' (not
specifying all possible cases?)





^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2024-12-19 13:34  Euler Taveira <euler@eulerto.com>
  parent: Kirill Reshke <reshkekirill@gmail.com>
  0 siblings, 0 replies; 58+ messages in thread

From: Euler Taveira @ 2024-12-19 13:34 UTC (permalink / raw)
  To: Kirill Reshke <reshkekirill@gmail.com>; +Cc: pgsql-hackers@lists.postgresql.org

On Thu, Dec 19, 2024, at 1:13 AM, Kirill Reshke wrote:
> Hi! Looks like a sane proposal. Correct me if I'm wrong, but with your
> patch it is not possible to configure something like "everybody ERROR,
> but autovac DEBUG5"? I mean,
> set log_min_messages to 'ERROR:default, DEBUG5:autovacuum' (not
> specifying all possible cases?)
> 
No. Good point. I forgot to mention that ALL as backend type could be added.


--
Euler Taveira
EDB   https://www.enterprisedb.com/

^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-02-05 18:51  Álvaro Herrera <alvherre@alvh.no-ip.org>
  parent: Euler Taveira <euler@eulerto.com>
  2 siblings, 1 reply; 58+ messages in thread

From: Álvaro Herrera @ 2025-02-05 18:51 UTC (permalink / raw)
  To: Euler Taveira <euler@eulerto.com>; +Cc: pgsql-hackers@lists.postgresql.org

Hello Euler,

On 2024-Dec-17, Euler Taveira wrote:

> Sometimes you need to inspect some debug messages from autovacuum worker but
> you cannot apply the same setting for backends (that could rapidly fill the log
> file). This proposal aims to change log_min_messages to have different log
> levels depending on which backend type the message comes from.
> 
> The syntax was changed from enum to string and it accepts a list of elements.
> 
> Instead of enum, it now accepts a comma-separated list of elements (string).
> Each element is LOGLEVEL:BACKENDTYPE.

This format seems unintuitive.  I would have thought you do it the other
way around, "backendtype:loglevel" ... that seems more natural because
it's like assigning the 'loglevel' value to the 'backendtype' element.
  SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1';


I dislike the array of names in variable.c.  We already have an array in
launch_backend.c (child_process_kinds), plus GetBackendTypeDesc in
miscinit.c.  Maybe not for this patch to clean up though.


I think it should be acceptable to configure one global setting with
exceptions for particular backend types:

log_min_messages = WARNING, autovacuum:DEBUG1

Right now I think the code only accepts the unadorned log level if there
are no other items in the list.  I think the proposal downthread is to
use the keyword ALL for this,

  log_min_messages = all:WARNING, autovacuum:DEBUG1   # I don't like this

but I think it's inferior, because then "all" is not really "all", and I
think it would be different if I had said

  log_min_messages = autovacuum:DEBUG1, all:WARNING   # I don't like this

because it looks like the "all" entry should override the one I set for
autovacuum before, which frankly would not make sense to me.

So I think these two lines,

log_min_messages = WARNING, autovacuum:DEBUG1
log_min_messages = autovacuum:DEBUG1, WARNING

should behave identically and mean "set the level for autovacuum to
DEBUG1, and to any other backend type to WARNING.


Also, I think it'd be better to reject duplicates in the list.  Right
now it looks like the last entry for one backend type overrides prior
ones.  I mean

log_min_messages = autovacuum:DEBUG1, autovacuum:ERROR

would set autovacuum to error, but that might be mistake prone if your
string is long.  So implementation-wise I suggest to initialize the
whole newlogminmsgs array to -1, then scan the list of entries (saving
an entry without backend type as the one to use later and) setting every
backend type to the number specified; if we see trying to set a value
that's already different from -1, throw error.  After scanning the whole
log_min_messages array, we scan the newlogminmsgs and set any entries
that are still -1 to the value that we saved before.


The new code in variable.c should be before the /* DATESTYLE */ comment
rather than at the end of the file.


You still have many XXX comments.  Also, postgresql.conf should list the
valid values for backendtype, as well as show an example of a valid
setting.  Please don't use ALLCAPS backend types in the docs, this looks
ugly:

> +        Valid <literal>BACKENDTYPE</literal> values are <literal>ARCHIVER</literal>,
> +        <literal>AUTOVACUUM</literal>, <literal>BACKEND</literal>,
> +        <literal>BGWORKER</literal>, <literal>BGWRITER</literal>,
> +        <literal>CHECKPOINTER</literal>, <literal>LOGGER</literal>,
> +        <literal>SLOTSYNCWORKER</literal>, <literal>WALRECEIVER</literal>,
> +        <literal>WALSENDER</literal>, <literal>WALSUMMARIZER</literal>, and
> +        <literal>WALWRITER</literal>.

-- 
Álvaro Herrera        Breisgau, Deutschland  —  https://www.EnterpriseDB.com/
"No necesitamos banderas
 No reconocemos fronteras"                  (Jorge González)





^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-03-05 00:33  Euler Taveira <euler@eulerto.com>
  parent: Álvaro Herrera <alvherre@alvh.no-ip.org>
  0 siblings, 3 replies; 58+ messages in thread

From: Euler Taveira @ 2025-03-05 00:33 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: pgsql-hackers@lists.postgresql.org

On Wed, Feb 5, 2025, at 3:51 PM, Álvaro Herrera wrote:
> On 2024-Dec-17, Euler Taveira wrote:
> 
> > Sometimes you need to inspect some debug messages from autovacuum worker but
> > you cannot apply the same setting for backends (that could rapidly fill the log
> > file). This proposal aims to change log_min_messages to have different log
> > levels depending on which backend type the message comes from.
> > 
> > The syntax was changed from enum to string and it accepts a list of elements.
> > 
> > Instead of enum, it now accepts a comma-separated list of elements (string).
> > Each element is LOGLEVEL:BACKENDTYPE.
> 
> This format seems unintuitive.  I would have thought you do it the other
> way around, "backendtype:loglevel" ... that seems more natural because
> it's like assigning the 'loglevel' value to the 'backendtype' element.
>   SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1';

Alvaro, thanks for your feedback. Your suggestion makes sense to me.

> I dislike the array of names in variable.c.  We already have an array in
> launch_backend.c (child_process_kinds), plus GetBackendTypeDesc in
> miscinit.c.  Maybe not for this patch to clean up though.

I thought about using child_process_kinds but two details made me give
up on the idea: (a) multiple names per backend type (for example, 
B_BACKEND, B_DEAD_END_BACKEND, B_STANDALONE_BACKEND) and (b) spaces into
names. Maybe we should have a group into child_process_kinds but as you
said it seems material for another patch.

> I think it should be acceptable to configure one global setting with
> exceptions for particular backend types:
> 
> log_min_messages = WARNING, autovacuum:DEBUG1
> 
> Right now I think the code only accepts the unadorned log level if there
> are no other items in the list.  I think the proposal downthread is to
> use the keyword ALL for this,
> 
>   log_min_messages = all:WARNING, autovacuum:DEBUG1   # I don't like this
> 
> but I think it's inferior, because then "all" is not really "all", and I
> think it would be different if I had said
> 
>   log_min_messages = autovacuum:DEBUG1, all:WARNING   # I don't like this
> 
> because it looks like the "all" entry should override the one I set for
> autovacuum before, which frankly would not make sense to me.

Good point. After reflection, I agree that "all" is not a good keyword.
This patch turns backend type as optional so WARNING means apply this
log level as a final step to the backend types that are not specified in
the list.

> So I think these two lines,
> 
> log_min_messages = WARNING, autovacuum:DEBUG1
> log_min_messages = autovacuum:DEBUG1, WARNING
> 
> should behave identically and mean "set the level for autovacuum to
> DEBUG1, and to any other backend type to WARNING.

Done.

> Also, I think it'd be better to reject duplicates in the list.  Right
> now it looks like the last entry for one backend type overrides prior
> ones.  I mean
> 
> log_min_messages = autovacuum:DEBUG1, autovacuum:ERROR
> 
> would set autovacuum to error, but that might be mistake prone if your
> string is long.  So implementation-wise I suggest to initialize the
> whole newlogminmsgs array to -1, then scan the list of entries (saving
> an entry without backend type as the one to use later and) setting every
> backend type to the number specified; if we see trying to set a value
> that's already different from -1, throw error.  After scanning the whole
> log_min_messages array, we scan the newlogminmsgs and set any entries
> that are still -1 to the value that we saved before.
> 
> 
> The new code in variable.c should be before the /* DATESTYLE */ comment
> rather than at the end of the file.

It was added into the MISCELLANEOUS section.

> 
> You still have many XXX comments.  Also, postgresql.conf should list the
> valid values for backendtype, as well as show an example of a valid
> setting.  Please don't use ALLCAPS backend types in the docs, this looks
> ugly:
> 
> > +        Valid <literal>BACKENDTYPE</literal> values are <literal>ARCHIVER</literal>,
> > +        <literal>AUTOVACUUM</literal>, <literal>BACKEND</literal>,
> > +        <literal>BGWORKER</literal>, <literal>BGWRITER</literal>,
> > +        <literal>CHECKPOINTER</literal>, <literal>LOGGER</literal>,
> > +        <literal>SLOTSYNCWORKER</literal>, <literal>WALRECEIVER</literal>,
> > +        <literal>WALSENDER</literal>, <literal>WALSUMMARIZER</literal>, and
> > +        <literal>WALWRITER</literal>.

Done.

Just to recap what was changed:

- patch was rebased
- backend type is optional and means all unspecified backend types
- generic log level (without backend type) is mandatory and the order it 
  ppears is not important (it is applied for the remaining backend types)
- fix Windows build
- new tests to cover the changes


--
Euler Taveira
EDB   https://www.enterprisedb.com/

Attachments:

  [text/x-patch] v2-0001-log_min_messages-per-backend-type.patch (21.2K, ../../313aa202-b5fa-4e3f-95d0-83425575c66d@app.fastmail.com/3-v2-0001-log_min_messages-per-backend-type.patch)
  download | inline diff:
From 0640ddb6e5510f1b600982331490188d20bfa7e9 Mon Sep 17 00:00:00 2001
From: Euler Taveira <euler@eulerto.com>
Date: Mon, 11 Dec 2023 17:24:50 -0300
Subject: [PATCH v2] log_min_messages per backend type

Change log_min_messages from a single element to a comma-separated list
of elements. Each element is backendtype:loglevel. The backendtype are
archiver, autovacuum (includes launcher and workers), backend, bgworker,
bgwriter, checkpointer, logger, slotsyncworker, walreceiver, walsender,
walsummarizer and walwriter. It should be part of this list a single log
level that is applied as a final step to the backend types that are not
informed.

The old syntax (a single log level) is still accepted for backward
compatibility. In this case, it means the informed log level is applied
for the backend types that are not informed. The default log level is
the same (WARNING) for all backend types.
---
 doc/src/sgml/config.sgml                      |  30 ++-
 src/backend/commands/extension.c              |   2 +-
 src/backend/commands/variable.c               | 187 ++++++++++++++++++
 src/backend/utils/error/elog.c                |   2 +-
 src/backend/utils/misc/guc_tables.c           |  81 ++++++--
 src/backend/utils/misc/postgresql.conf.sample |   1 +
 src/include/miscadmin.h                       |   2 +
 src/include/utils/guc.h                       |   2 +-
 src/include/utils/guc_hooks.h                 |   2 +
 src/include/utils/guc_tables.h                |   4 +
 src/test/regress/expected/guc.out             |  48 +++++
 src/test/regress/sql/guc.sql                  |  16 ++
 12 files changed, 352 insertions(+), 25 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index d2fa5f7d1a9..3e8df85556d 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -6861,7 +6861,7 @@ local0.*    /var/log/postgresql
      <variablelist>
 
      <varlistentry id="guc-log-min-messages" xreflabel="log_min_messages">
-      <term><varname>log_min_messages</varname> (<type>enum</type>)
+      <term><varname>log_min_messages</varname> (<type>string</type>)
       <indexterm>
        <primary><varname>log_min_messages</varname> configuration parameter</primary>
       </indexterm>
@@ -6870,14 +6870,26 @@ local0.*    /var/log/postgresql
        <para>
         Controls which <link linkend="runtime-config-severity-levels">message
         levels</link> are written to the server log.
-        Valid values are <literal>DEBUG5</literal>, <literal>DEBUG4</literal>,
-        <literal>DEBUG3</literal>, <literal>DEBUG2</literal>, <literal>DEBUG1</literal>,
-        <literal>INFO</literal>, <literal>NOTICE</literal>, <literal>WARNING</literal>,
-        <literal>ERROR</literal>, <literal>LOG</literal>, <literal>FATAL</literal>, and
-        <literal>PANIC</literal>.  Each level includes all the levels that
-        follow it.  The later the level, the fewer messages are sent
-        to the log.  The default is <literal>WARNING</literal>.  Note that
-        <literal>LOG</literal> has a different rank here than in
+        Valid values are a comma-separated list of <literal>backendtype:level</literal>
+        and a single <literal>level</literal>. The list allows it to use
+        different levels per backend type.
+        Valid <literal>backendtype</literal> values are <literal>archiver</literal>,
+        <literal>autovacuum</literal>, <literal>backend</literal>,
+        <literal>bgworker</literal>, <literal>bgwriter</literal>,
+        <literal>checkpointer</literal>, <literal>logger</literal>,
+        <literal>slotsyncworker</literal>, <literal>walreceiver</literal>,
+        <literal>walsender</literal>, <literal>walsummarizer</literal>, and
+        <literal>walwriter</literal>.
+        Valid <literal>LEVEL</literal> values are <literal>DEBUG5</literal>,
+        <literal>DEBUG4</literal>, <literal>DEBUG3</literal>, <literal>DEBUG2</literal>,
+        <literal>DEBUG1</literal>, <literal>INFO</literal>, <literal>NOTICE</literal>,
+        <literal>WARNING</literal>, <literal>ERROR</literal>, <literal>LOG</literal>,
+        <literal>FATAL</literal>, and <literal>PANIC</literal>.  Each level includes
+        all the levels that follow it.  The later the level, the fewer messages are sent
+        to the log. The single <literal>LEVEL</literal> is mandatory and it is
+        assigned to the backend types that are not specified in the list.  The
+        default is <literal>WARNING</literal> for all backend types.
+        Note that <literal>LOG</literal> has a different rank here than in
         <xref linkend="guc-client-min-messages"/>.
         Only superusers and users with the appropriate <literal>SET</literal>
         privilege can change this setting.
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index d9bb4ce5f1e..93923c26a7e 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1059,7 +1059,7 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
 		(void) set_config_option("client_min_messages", "warning",
 								 PGC_USERSET, PGC_S_SESSION,
 								 GUC_ACTION_SAVE, true, 0, false);
-	if (log_min_messages < WARNING)
+	if (log_min_messages[MyBackendType] < WARNING)
 		(void) set_config_option_ext("log_min_messages", "warning",
 									 PGC_SUSET, PGC_S_SESSION,
 									 BOOTSTRAP_SUPERUSERID,
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index 4ad6e236d69..0f646298bf1 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -35,6 +35,7 @@
 #include "utils/datetime.h"
 #include "utils/fmgrprotos.h"
 #include "utils/guc_hooks.h"
+#include "utils/guc_tables.h"
 #include "utils/snapmgr.h"
 #include "utils/syscache.h"
 #include "utils/timestamp.h"
@@ -1271,3 +1272,189 @@ check_ssl(bool *newval, void **extra, GucSource source)
 #endif
 	return true;
 }
+
+/*
+ * GUC check_hook for log_min_messages
+ *
+ * The parsing consists of a comma-separated list of BACKENDTYPENAME:LEVEL
+ * elements. BACKENDTYPENAME is log_min_messages_backend_types.  LEVEL is
+ * server_message_level_options. A single LEVEL element should be part of this
+ * list and it is applied as a final step to the backend types that are not
+ * specified. For backward compatibility, the old syntax is still accepted and
+ * it means to apply this level for all backend types.
+ */
+bool
+check_log_min_messages(char **newval, void **extra, GucSource source)
+{
+	char	   *rawstring;
+	List	   *elemlist;
+	ListCell   *l;
+	int			newlevels[BACKEND_NUM_TYPES];
+	bool		assigned[BACKEND_NUM_TYPES];
+	int			genericlevel = -1;	/* -1 means not assigned */
+
+	/* Initialize the array. */
+	memset(newlevels, WARNING, BACKEND_NUM_TYPES * sizeof(int));
+	memset(assigned, false, BACKEND_NUM_TYPES * sizeof(bool));
+
+	/* Need a modifiable copy of string. */
+	rawstring = pstrdup(*newval);
+
+	/* Parse string into list of identifiers. */
+	if (!SplitGUCList(rawstring, ',', &elemlist))
+	{
+		/* syntax error in list */
+		GUC_check_errdetail("List syntax is invalid.");
+		pfree(rawstring);
+		list_free(elemlist);
+		return false;
+	}
+
+	/* Validate and assign log level and backend type. */
+	foreach(l, elemlist)
+	{
+		char	   *tok = (char *) lfirst(l);
+		char	   *sep;
+		const struct config_enum_entry *entry;
+
+		/*
+		 * Check whether there is a backend type following the log level. If
+		 * there is no separator, it means this log level should be applied
+		 * for all backend types (backward compatibility).
+		 */
+		sep = strchr(tok, ':');
+		if (sep == NULL)
+		{
+			bool		found = false;
+
+			/* Reject duplicates for generic log level. */
+			if (genericlevel != -1)
+			{
+				GUC_check_errdetail("Generic log level was already assigned.");
+				pfree(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			/* Is the log level valid? */
+			for (entry = server_message_level_options; entry && entry->name; entry++)
+			{
+				if (pg_strcasecmp(entry->name, tok) == 0)
+				{
+					genericlevel = entry->val;
+					found = true;
+					break;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized log level: \"%s\".", tok);
+				pfree(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+		}
+		else
+		{
+			char	   *loglevel;
+			char	   *btype;
+			bool		found = false;
+
+			btype = palloc((sep - tok) + 1);
+			memcpy(btype, tok, sep - tok);
+			btype[sep - tok] = '\0';
+			loglevel = pstrdup(sep + 1);
+
+			/* Is the log level valid? */
+			for (entry = server_message_level_options; entry && entry->name; entry++)
+			{
+				if (pg_strcasecmp(entry->name, loglevel) == 0)
+				{
+					found = true;
+					break;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel);
+				pfree(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			/*
+			 * Is the backend type name valid? There might be multiple entries
+			 * per backend type, don't bail out when find first occurrence.
+			 */
+			found = false;
+			for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+			{
+				if (pg_strcasecmp(log_min_messages_backend_types[i], btype) == 0)
+				{
+					newlevels[i] = entry->val;
+					assigned[i] = true;
+					found = true;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized backend type: \"%s\".", btype);
+				pfree(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+		}
+	}
+
+	/*
+	 * Generic log level must be specified. It is a good idea to specify a
+	 * generic log level to make it clear that it is the fallback value.
+	 * Although, we can document it, it might confuse users that used to
+	 * specify a single log level in prior releases.
+	 */
+	if (genericlevel == -1)
+	{
+		GUC_check_errdetail("Generic log level was not defined.");
+		pfree(rawstring);
+		list_free(elemlist);
+		return false;
+	}
+
+	/*
+	 * Apply the generic log level (the one without a backend type) after all
+	 * of the specific backend type have been assigned. Hence, it doesn't
+	 * matter the order you specify the generic log level, the final result
+	 * will be the same.
+	 */
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+	{
+		if (!assigned[i])
+			newlevels[i] = genericlevel;
+	}
+
+	pfree(rawstring);
+	list_free(elemlist);
+
+	/*
+	 * Pass back data for assign_log_min_messages to use.
+	 */
+	*extra = guc_malloc(LOG, BACKEND_NUM_TYPES * sizeof(int));
+	if (!*extra)
+		return false;
+	memcpy(*extra, newlevels, BACKEND_NUM_TYPES * sizeof(int));
+
+	return true;
+}
+
+/*
+ * GUC assign_hook for log_min_messages
+ */
+void
+assign_log_min_messages(const char *newval, void *extra)
+{
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+		log_min_messages[i] = ((int *) extra)[i];
+}
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 860bbd40d42..fc5aedab0c8 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -234,7 +234,7 @@ is_log_level_output(int elevel, int log_min_level)
 static inline bool
 should_output_to_server(int elevel)
 {
-	return is_log_level_output(elevel, log_min_messages);
+	return is_log_level_output(elevel, log_min_messages[MyBackendType]);
 }
 
 /*
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index ad25cbb39c5..9d38474fc79 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -139,7 +139,7 @@ static const struct config_enum_entry client_message_level_options[] = {
 	{NULL, 0, false}
 };
 
-static const struct config_enum_entry server_message_level_options[] = {
+const struct config_enum_entry server_message_level_options[] = {
 	{"debug5", DEBUG5, false},
 	{"debug4", DEBUG4, false},
 	{"debug3", DEBUG3, false},
@@ -521,7 +521,6 @@ static bool default_with_oids = false;
 bool		current_role_is_superuser;
 
 int			log_min_error_statement = ERROR;
-int			log_min_messages = WARNING;
 int			client_min_messages = NOTICE;
 int			log_min_duration_sample = -1;
 int			log_min_duration_statement = -1;
@@ -579,6 +578,7 @@ static char *server_version_string;
 static int	server_version_num;
 static char *debug_io_direct_string;
 static char *restrict_nonsystem_relation_kind_string;
+static char *log_min_messages_string;
 
 #ifdef HAVE_SYSLOG
 #define	DEFAULT_SYSLOG_FACILITY LOG_LOCAL0
@@ -623,6 +623,60 @@ char	   *role_string;
 /* should be static, but guc.c needs to get at this */
 bool		in_hot_standby_guc;
 
+/*
+ * This must match enum BackendType! It should be static, but
+ * commands/variable.c needs to get at this.
+ */
+int			log_min_messages[] = {
+	[B_INVALID] = WARNING,
+	[B_BACKEND] = WARNING,
+	[B_DEAD_END_BACKEND] = WARNING,
+	[B_AUTOVAC_LAUNCHER] = WARNING,
+	[B_AUTOVAC_WORKER] = WARNING,
+	[B_BG_WORKER] = WARNING,
+	[B_WAL_SENDER] = WARNING,
+	[B_SLOTSYNC_WORKER] = WARNING,
+	[B_STANDALONE_BACKEND] = WARNING,
+	[B_ARCHIVER] = WARNING,
+	[B_BG_WRITER] = WARNING,
+	[B_CHECKPOINTER] = WARNING,
+	[B_STARTUP] = WARNING,
+	[B_WAL_RECEIVER] = WARNING,
+	[B_WAL_SUMMARIZER] = WARNING,
+	[B_WAL_WRITER] = WARNING,
+	[B_LOGGER] = WARNING,
+};
+
+StaticAssertDecl(lengthof(log_min_messages) == BACKEND_NUM_TYPES,
+				 "array length mismatch");
+
+/*
+ * This must match enum BackendType! It might be in commands/variable.c but for
+ * convenience it is near log_min_messages.
+ */
+const char *const log_min_messages_backend_types[] = {
+	[B_INVALID] = "backend",	/* XXX same as backend? */
+	[B_BACKEND] = "backend",
+	[B_DEAD_END_BACKEND] = "backend",	/* XXX same as backend? */
+	[B_AUTOVAC_LAUNCHER] = "autovacuum",
+	[B_AUTOVAC_WORKER] = "autovacuum",
+	[B_BG_WORKER] = "bgworker",
+	[B_WAL_SENDER] = "walsender",
+	[B_SLOTSYNC_WORKER] = "slotsyncworker",
+	[B_STANDALONE_BACKEND] = "backend", /* XXX same as backend? */
+	[B_ARCHIVER] = "archiver",
+	[B_BG_WRITER] = "bgwriter",
+	[B_CHECKPOINTER] = "checkpointer",
+	[B_STARTUP] = "backend",	/* XXX same as backend? */
+	[B_WAL_RECEIVER] = "walreceiver",
+	[B_WAL_SUMMARIZER] = "walsummarizer",
+	[B_WAL_WRITER] = "walwriter",
+	[B_LOGGER] = "logger",
+};
+
+StaticAssertDecl(lengthof(log_min_messages_backend_types) == BACKEND_NUM_TYPES,
+				 "array length mismatch");
+
 
 /*
  * Displayable names for context types (enum GucContext)
@@ -4223,6 +4277,18 @@ struct config_string ConfigureNamesString[] =
 		check_client_encoding, assign_client_encoding, NULL
 	},
 
+	{
+		{"log_min_messages", PGC_SUSET, LOGGING_WHEN,
+			gettext_noop("Sets the message levels that are logged."),
+			gettext_noop("Each level includes all the levels that follow it. The later"
+						 " the level, the fewer messages are sent."),
+			GUC_LIST_INPUT
+		},
+		&log_min_messages_string,
+		"WARNING",
+		check_log_min_messages, assign_log_min_messages, NULL
+	},
+
 	{
 		{"log_line_prefix", PGC_SIGHUP, LOGGING_WHAT,
 			gettext_noop("Controls information prefixed to each log line."),
@@ -5011,17 +5077,6 @@ struct config_enum ConfigureNamesEnum[] =
 		NULL, NULL, NULL
 	},
 
-	{
-		{"log_min_messages", PGC_SUSET, LOGGING_WHEN,
-			gettext_noop("Sets the message levels that are logged."),
-			gettext_noop("Each level includes all the levels that follow it. The later"
-						 " the level, the fewer messages are sent.")
-		},
-		&log_min_messages,
-		WARNING, server_message_level_options,
-		NULL, NULL, NULL
-	},
-
 	{
 		{"log_min_error_statement", PGC_SUSET, LOGGING_WHEN,
 			gettext_noop("Causes all statements generating error at or above this level to be logged."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 2d1de9c37bd..c99e1fb90ae 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -526,6 +526,7 @@
 					#   log
 					#   fatal
 					#   panic
+					#   or a comma-separated list of backend_type:level
 
 #log_min_error_statement = error	# values in order of decreasing detail:
 					#   debug5
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index a2b63495eec..36b0b82cf3e 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -332,6 +332,8 @@ extern void SwitchBackToLocalLatch(void);
  *
  * If you add entries, please also update the child_process_kinds array in
  * launch_backend.c.
+ * XXX If you add a new backend type or change the order, update
+ * log_min_messages because it relies on this order to work correctly.
  */
 typedef enum BackendType
 {
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index 1233e07d7da..bb23ca65449 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -271,7 +271,7 @@ extern PGDLLIMPORT bool log_duration;
 extern PGDLLIMPORT int log_parameter_max_length;
 extern PGDLLIMPORT int log_parameter_max_length_on_error;
 extern PGDLLIMPORT int log_min_error_statement;
-extern PGDLLIMPORT int log_min_messages;
+extern PGDLLIMPORT int log_min_messages[];
 extern PGDLLIMPORT int client_min_messages;
 extern PGDLLIMPORT int log_min_duration_sample;
 extern PGDLLIMPORT int log_min_duration_statement;
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index 951451a9765..cf27960e239 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -176,5 +176,7 @@ extern bool check_synchronized_standby_slots(char **newval, void **extra,
 extern void assign_synchronized_standby_slots(const char *newval, void *extra);
 extern bool check_idle_replication_slot_timeout(int *newval, void **extra,
 												GucSource source);
+extern bool check_log_min_messages(char **newval, void **extra, GucSource source);
+extern void assign_log_min_messages(const char *newval, void *extra);
 
 #endif							/* GUC_HOOKS_H */
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index ab47145ec36..8a60ec5997a 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -297,6 +297,10 @@ struct config_enum
 	void	   *reset_extra;
 };
 
+/* log_min_messages */
+extern PGDLLIMPORT const char *const log_min_messages_backend_types[];
+extern PGDLLIMPORT const struct config_enum_entry server_message_level_options[];
+
 /* constant tables corresponding to enums above and in guc.h */
 extern PGDLLIMPORT const char *const config_group_names[];
 extern PGDLLIMPORT const char *const config_type_names[];
diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out
index 7f9e29c765c..7572e0addbc 100644
--- a/src/test/regress/expected/guc.out
+++ b/src/test/regress/expected/guc.out
@@ -913,3 +913,51 @@ SELECT name FROM tab_settings_flags
 (0 rows)
 
 DROP TABLE tab_settings_flags;
+-- Test log_min_messages
+SET log_min_messages TO fatal;
+SHOW log_min_messages;
+ log_min_messages 
+------------------
+ fatal
+(1 row)
+
+SET log_min_messages TO 'fatal';
+SHOW log_min_messages;
+ log_min_messages 
+------------------
+ fatal
+(1 row)
+
+SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1';  --fail
+ERROR:  invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1"
+DETAIL:  Generic log level was not defined.
+SET log_min_messages TO 'debug1, backend:error, fatal';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "debug1, backend:error, fatal"
+DETAIL:  Generic log level was already assigned.
+SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1"
+DETAIL:  Unrecognized backend type: "foo".
+SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1"
+DETAIL:  Unrecognized log level: "bar".
+SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3';
+SHOW log_min_messages;
+                                        log_min_messages                                         
+-------------------------------------------------------------------------------------------------
+ backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3
+(1 row)
+
+SET log_min_messages TO 'warning, autovacuum:debug1';
+SHOW log_min_messages;
+      log_min_messages      
+----------------------------
+ warning, autovacuum:debug1
+(1 row)
+
+SET log_min_messages TO 'autovacuum:debug1, warning';
+SHOW log_min_messages;
+      log_min_messages      
+----------------------------
+ autovacuum:debug1, warning
+(1 row)
+
diff --git a/src/test/regress/sql/guc.sql b/src/test/regress/sql/guc.sql
index f65f84a2632..e9dddb4e72d 100644
--- a/src/test/regress/sql/guc.sql
+++ b/src/test/regress/sql/guc.sql
@@ -368,3 +368,19 @@ SELECT name FROM tab_settings_flags
   WHERE no_reset AND NOT no_reset_all
   ORDER BY 1;
 DROP TABLE tab_settings_flags;
+
+-- Test log_min_messages
+SET log_min_messages TO fatal;
+SHOW log_min_messages;
+SET log_min_messages TO 'fatal';
+SHOW log_min_messages;
+SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1';  --fail
+SET log_min_messages TO 'debug1, backend:error, fatal';  -- fail
+SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1';  -- fail
+SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1';  -- fail
+SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3';
+SHOW log_min_messages;
+SET log_min_messages TO 'warning, autovacuum:debug1';
+SHOW log_min_messages;
+SET log_min_messages TO 'autovacuum:debug1, warning';
+SHOW log_min_messages;
-- 
2.39.5



^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-03-05 16:40  Andrew Dunstan <andrew@dunslane.net>
  parent: Euler Taveira <euler@eulerto.com>
  2 siblings, 2 replies; 58+ messages in thread

From: Andrew Dunstan @ 2025-03-05 16:40 UTC (permalink / raw)
  To: Euler Taveira <euler@eulerto.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: pgsql-hackers@lists.postgresql.org


On 2025-03-04 Tu 7:33 PM, Euler Taveira wrote:
>> I think it should be acceptable to configure one global setting with
>> exceptions for particular backend types:
>>
>> log_min_messages = WARNING, autovacuum:DEBUG1
>>
>> Right now I think the code only accepts the unadorned log level if there
>> are no other items in the list.  I think the proposal downthread is to
>> use the keyword ALL for this,
>>
>>   log_min_messages = all:WARNING, autovacuum:DEBUG1   # I don't like this
>>
>> but I think it's inferior, because then "all" is not really "all", and I
>> think it would be different if I had said
>>
>>   log_min_messages = autovacuum:DEBUG1, all:WARNING   # I don't like this
>>
>> because it looks like the "all" entry should override the one I set for
>> autovacuum before, which frankly would not make sense to me.
>
> Good point. After reflection, I agree that "all" is not a good keyword.
> This patch turns backend type as optional so WARNING means apply this
> log level as a final step to the backend types that are not specified in
> the list.
>
>> So I think these two lines,
>>
>> log_min_messages = WARNING, autovacuum:DEBUG1
>> log_min_messages = autovacuum:DEBUG1, WARNING
>>
>> should behave identically and mean "set the level for autovacuum to
>> DEBUG1, and to any other backend type to WARNING.
>
> Done.


Just bikeshedding a bit ...

I'm not mad keen on this design. I think the value should be either a 
single setting like "WARNING" or a set of type:setting pairs. I agree 
that "all" is a bad name, but I think "default" would make sense.

I can live with it but I think this just looks a bit odd.


cheers


andrew



--
Andrew Dunstan
EDB:https://www.enterprisedb.com

^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-03-06 02:53  Fujii Masao <masao.fujii@oss.nttdata.com>
  parent: Euler Taveira <euler@eulerto.com>
  2 siblings, 1 reply; 58+ messages in thread

From: Fujii Masao @ 2025-03-06 02:53 UTC (permalink / raw)
  To: Euler Taveira <euler@eulerto.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: pgsql-hackers@lists.postgresql.org



On 2025/03/05 9:33, Euler Taveira wrote:
>> > +        Valid <literal>BACKENDTYPE</literal> values are <literal>ARCHIVER</literal>,
>> > +        <literal>AUTOVACUUM</literal>, <literal>BACKEND</literal>,
>> > +        <literal>BGWORKER</literal>, <literal>BGWRITER</literal>,
>> > +        <literal>CHECKPOINTER</literal>, <literal>LOGGER</literal>,
>> > +        <literal>SLOTSYNCWORKER</literal>, <literal>WALRECEIVER</literal>,
>> > +        <literal>WALSENDER</literal>, <literal>WALSUMMARIZER</literal>, and
>> > +        <literal>WALWRITER</literal>.

What about postmaster?

For parallel workers launched for parallel queries, should they follow
the backend's log level or the background worker's? Since they operate
as part of a parallel query executed by a backend, it seems more logical
for them to follow the backend's setting.

+	[B_CHECKPOINTER] = "checkpointer",
+	[B_STARTUP] = "backend",	/* XXX same as backend? */

I like the idea of allowing log levels to be set per process.
There were times I wanted to use debug5 specifically for
the startup process when troubleshooting WAL replay. It would be
helpful to distinguish the startup process from a regular backend,
so we can set its log level independently.

Regards,

-- 
Fujii Masao
Advanced Computing Technology Center
Research and Development Headquarters
NTT DATA CORPORATION






^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-03-06 13:20  Matheus Alcantara <matheusssilv97@gmail.com>
  parent: Andrew Dunstan <andrew@dunslane.net>
  1 sibling, 0 replies; 58+ messages in thread

From: Matheus Alcantara @ 2025-03-06 13:20 UTC (permalink / raw)
  To: Andrew Dunstan <andrew@dunslane.net>; Euler Taveira <euler@eulerto.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: pgsql-hackers@lists.postgresql.org

Hi,

>
> On 2025-03-04 Tu 7:33 PM, Euler Taveira wrote:
>>> I think it should be acceptable to configure one global setting with
>>> exceptions for particular backend types:
>>>
>>> log_min_messages = WARNING, autovacuum:DEBUG1
>>>
>>> Right now I think the code only accepts the unadorned log level if
>>> there
>>> are no other items in the list.  I think the proposal downthread is to
>>> use the keyword ALL for this,
>>>
>>>   log_min_messages = all:WARNING, autovacuum:DEBUG1   # I don't
>>> like this
>>>
>>> but I think it's inferior, because then "all" is not really "all",
>>> and I
>>> think it would be different if I had said
>>>
>>>   log_min_messages = autovacuum:DEBUG1, all:WARNING   # I don't
>>> like this
>>>
>>> because it looks like the "all" entry should override the one I set
>>> for
>>> autovacuum before, which frankly would not make sense to me.
>>
>> Good point. After reflection, I agree that "all" is not a good keyword.
>> This patch turns backend type as optional so WARNING means apply this
>> log level as a final step to the backend types that are not
>> specified in
>> the list.
>>
>>> So I think these two lines,
>>>
>>> log_min_messages = WARNING, autovacuum:DEBUG1
>>> log_min_messages = autovacuum:DEBUG1, WARNING
>>>
>>> should behave identically and mean "set the level for autovacuum to
>>> DEBUG1, and to any other backend type to WARNING.
>>
>> Done.
>
>
> Just bikeshedding a bit ...
>
> I'm not mad keen on this design. I think the value should be either a
> single setting like "WARNING" or a set of type:setting pairs. I agree
> that "all" is a bad name, but I think "default" would make sense.
>
> I can live with it but I think this just looks a bit odd.
>

Just bringing some thoughts about it...

How about using something like *:WARNING? I'm not sure if it could also be
confusing as the "all" keyword, but I think it could also be interpreted as
"anything else use WARNING".

--
Matheus Alcantara





^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-03-06 13:33  Andres Freund <andres@anarazel.de>
  parent: Euler Taveira <euler@eulerto.com>
  2 siblings, 1 reply; 58+ messages in thread

From: Andres Freund @ 2025-03-06 13:33 UTC (permalink / raw)
  To: Euler Taveira <euler@eulerto.com>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers@lists.postgresql.org

Hi,

On 2025-03-04 21:33:39 -0300, Euler Taveira wrote:
> +/*
> + * This must match enum BackendType! It should be static, but
> + * commands/variable.c needs to get at this.
> + */
> +int			log_min_messages[] = {
> +	[B_INVALID] = WARNING,
> +	[B_BACKEND] = WARNING,
> +	[B_DEAD_END_BACKEND] = WARNING,
> +	[B_AUTOVAC_LAUNCHER] = WARNING,
> +	[B_AUTOVAC_WORKER] = WARNING,
> +	[B_BG_WORKER] = WARNING,
> +	[B_WAL_SENDER] = WARNING,
> +	[B_SLOTSYNC_WORKER] = WARNING,
> +	[B_STANDALONE_BACKEND] = WARNING,
> +	[B_ARCHIVER] = WARNING,
> +	[B_BG_WRITER] = WARNING,
> +	[B_CHECKPOINTER] = WARNING,
> +	[B_STARTUP] = WARNING,
> +	[B_WAL_RECEIVER] = WARNING,
> +	[B_WAL_SUMMARIZER] = WARNING,
> +	[B_WAL_WRITER] = WARNING,
> +	[B_LOGGER] = WARNING,
> +};

> +StaticAssertDecl(lengthof(log_min_messages) == BACKEND_NUM_TYPES,
> +				 "array length mismatch");
> +
> +/*
> + * This must match enum BackendType! It might be in commands/variable.c but for
> + * convenience it is near log_min_messages.
> + */
> +const char *const log_min_messages_backend_types[] = {
> +	[B_INVALID] = "backend",	/* XXX same as backend? */
> +	[B_BACKEND] = "backend",
> +	[B_DEAD_END_BACKEND] = "backend",	/* XXX same as backend? */
> +	[B_AUTOVAC_LAUNCHER] = "autovacuum",
> +	[B_AUTOVAC_WORKER] = "autovacuum",
> +	[B_BG_WORKER] = "bgworker",
> +	[B_WAL_SENDER] = "walsender",
> +	[B_SLOTSYNC_WORKER] = "slotsyncworker",
> +	[B_STANDALONE_BACKEND] = "backend", /* XXX same as backend? */
> +	[B_ARCHIVER] = "archiver",
> +	[B_BG_WRITER] = "bgwriter",
> +	[B_CHECKPOINTER] = "checkpointer",
> +	[B_STARTUP] = "backend",	/* XXX same as backend? */

Huh, the startup process is among the most crucial things to monitor?

> +	[B_WAL_RECEIVER] = "walreceiver",
> +	[B_WAL_SUMMARIZER] = "walsummarizer",
> +	[B_WAL_WRITER] = "walwriter",
> +	[B_LOGGER] = "logger",
> +};
> +
> +StaticAssertDecl(lengthof(log_min_messages_backend_types) == BACKEND_NUM_TYPES,
> +				 "array length mismatch");
> +

I don't know what I think about the whole patch, but I do want to voice
*strong* opposition to duplicating a list of all backend types into multiple
places. It's already painfull enough to add a new backend type, without having
to pointlessly go around and manually add a new backend type to mulltiple
arrays that have completely predictable content.

Greetings,

Andres Freund





^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-03-06 14:33  Euler Taveira <euler@eulerto.com>
  parent: Fujii Masao <masao.fujii@oss.nttdata.com>
  0 siblings, 0 replies; 58+ messages in thread

From: Euler Taveira @ 2025-03-06 14:33 UTC (permalink / raw)
  To: Fujii Masao <masao.fujii@oss.nttdata.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: pgsql-hackers@lists.postgresql.org

On Wed, Mar 5, 2025, at 11:53 PM, Fujii Masao wrote:
> On 2025/03/05 9:33, Euler Taveira wrote:
> >> > +        Valid <literal>BACKENDTYPE</literal> values are <literal>ARCHIVER</literal>,
> >> > +        <literal>AUTOVACUUM</literal>, <literal>BACKEND</literal>,
> >> > +        <literal>BGWORKER</literal>, <literal>BGWRITER</literal>,
> >> > +        <literal>CHECKPOINTER</literal>, <literal>LOGGER</literal>,
> >> > +        <literal>SLOTSYNCWORKER</literal>, <literal>WALRECEIVER</literal>,
> >> > +        <literal>WALSENDER</literal>, <literal>WALSUMMARIZER</literal>, and
> >> > +        <literal>WALWRITER</literal>.
> 
> What about postmaster?

It is B_INVALID that is currently mapped to "backend". This state is used as an
initial phase for the child process. There might be messages before
MyBackendType is assigned (see ProcessStartupPacket, for example). Hence, I
refrain to create a special backend type for postmaster. Should we map it to
generic log level instead of backend?

> For parallel workers launched for parallel queries, should they follow
> the backend's log level or the background worker's? Since they operate
> as part of a parallel query executed by a backend, it seems more logical
> for them to follow the backend's setting.

Since we are using enum BackendType and there is no special entry for parallel
query processes. I'm afraid that introducing conditional logic for checking
special cases like the bgw_type for parallel queries or MyProcPid ==
PostmasterPid might slowdown that code path. (See that should_output_to_server
is an inline function.) I will run some tests to see if there is a considerable
impact.

> + [B_CHECKPOINTER] = "checkpointer",
> + [B_STARTUP] = "backend", /* XXX same as backend? */
> 
> I like the idea of allowing log levels to be set per process.
> There were times I wanted to use debug5 specifically for
> the startup process when troubleshooting WAL replay. It would be
> helpful to distinguish the startup process from a regular backend,
> so we can set its log level independently.

Although startup process is mapped to backend (hence, the XXX), we can
certainly create a separate backend type for it.


--
Euler Taveira
EDB   https://www.enterprisedb.com/

^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-03-06 21:33  Euler Taveira <euler@eulerto.com>
  parent: Andrew Dunstan <andrew@dunslane.net>
  1 sibling, 0 replies; 58+ messages in thread

From: Euler Taveira @ 2025-03-06 21:33 UTC (permalink / raw)
  To: Andrew Dunstan <andrew@dunslane.net>; Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: pgsql-hackers@lists.postgresql.org

On Wed, Mar 5, 2025, at 1:40 PM, Andrew Dunstan wrote:
> Just bikeshedding a bit ...
> 
> I'm not mad keen on this design. I think the value should be either a single setting like "WARNING" or a set of type:setting pairs. I agree that "all" is a bad name, but I think "default" would make sense.
> 

One of my main concerns was a clear interface. I think "default" is less
confusing than "all". Your suggestion about single or list is aligned with what
Alvaro suggested. IIUC you are suggesting default:loglevel only if it is part
of the list; the single loglevel shouldn't contain the backend type to keep the
backward compatibility. The advantage of your proposal is that it make it clear
what the fallback log level is. However, someone could be confused asking if
the "default" is a valid backend type and if there is a difference between
WARNING and default:WARNING (both is a fallback for non-specified backend type
elements).


--
Euler Taveira
EDB   https://www.enterprisedb.com/

^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-07-31 14:19  Euler Taveira <euler@eulerto.com>
  parent: Andres Freund <andres@anarazel.de>
  0 siblings, 2 replies; 58+ messages in thread

From: Euler Taveira @ 2025-07-31 14:19 UTC (permalink / raw)
  To: Andres Freund <andres@anarazel.de>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers@lists.postgresql.org

On Thu, Mar 6, 2025, at 10:33 AM, Andres Freund wrote:
> Huh, the startup process is among the most crucial things to monitor?
>

Good point. Fixed.

After collecting some suggestions, I'm attaching a new patch contains the
following changes:

- patch was rebased
- include Alvaro's patch (v2-0001) [1] as a basis for this patch
- add ioworker as new backend type
- add startup as new backend type per Andres suggestion
- small changes into documentation

> I don't know what I think about the whole patch, but I do want to voice
> *strong* opposition to duplicating a list of all backend types into multiple
> places. It's already painfull enough to add a new backend type, without having
> to pointlessly go around and manually add a new backend type to mulltiple
> arrays that have completely predictable content.
>

I'm including Alvaro's patch as is just to make the CF bot happy and to
illustrate how it would be if we adopt his solution to centralize the list of
backend types. I think Alvaro's proposal overcomes the objection [2], right?


[1] https://www.postgresql.org/message-id/202507282113.vdp4axehoppi@alvherre.pgsql
[2] https://www.postgresql.org/message-id/y5tgui75jrcj6mm5nmoq4yqwage2432akx4kp2ogtcnim3wskx@2ipmtfi4qvp...


-- 
Euler Taveira
EDB   https://www.enterprisedb.com/

Attachments:

  [text/x-patch] v3-0001-Create-a-separate-file-listing-backend-types.patch (7.0K, ../../c0135750-48d8-4a13-9b7c-426f27888e9b@app.fastmail.com/2-v3-0001-Create-a-separate-file-listing-backend-types.patch)
  download | inline diff:
From ec409dada269cf54b9bce16cae473a46b2400598 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Herrera?= <alvherre@kurilemu.de>
Date: Tue, 15 Jul 2025 18:19:27 +0200
Subject: [PATCH v3 1/2] Create a separate file listing backend types

Use our established coding pattern to reduce maintenance pain when
adding other per-process-type characteristics.

Like PG_KEYWORD, PG_CMDTAG, PG_RMGR.
---
 src/backend/postmaster/launch_backend.c | 32 ++------------
 src/backend/utils/init/miscinit.c       | 59 ++-----------------------
 src/include/postmaster/proctypelist.h   | 50 +++++++++++++++++++++
 3 files changed, 58 insertions(+), 83 deletions(-)
 create mode 100644 src/include/postmaster/proctypelist.h

diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index bf6b55ee830..8b2f1a0cf41 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -177,34 +177,10 @@ typedef struct
 } child_process_kind;
 
 static child_process_kind child_process_kinds[] = {
-	[B_INVALID] = {"invalid", NULL, false},
-
-	[B_BACKEND] = {"backend", BackendMain, true},
-	[B_DEAD_END_BACKEND] = {"dead-end backend", BackendMain, true},
-	[B_AUTOVAC_LAUNCHER] = {"autovacuum launcher", AutoVacLauncherMain, true},
-	[B_AUTOVAC_WORKER] = {"autovacuum worker", AutoVacWorkerMain, true},
-	[B_BG_WORKER] = {"bgworker", BackgroundWorkerMain, true},
-
-	/*
-	 * WAL senders start their life as regular backend processes, and change
-	 * their type after authenticating the client for replication.  We list it
-	 * here for PostmasterChildName() but cannot launch them directly.
-	 */
-	[B_WAL_SENDER] = {"wal sender", NULL, true},
-	[B_SLOTSYNC_WORKER] = {"slot sync worker", ReplSlotSyncWorkerMain, true},
-
-	[B_STANDALONE_BACKEND] = {"standalone backend", NULL, false},
-
-	[B_ARCHIVER] = {"archiver", PgArchiverMain, true},
-	[B_BG_WRITER] = {"bgwriter", BackgroundWriterMain, true},
-	[B_CHECKPOINTER] = {"checkpointer", CheckpointerMain, true},
-	[B_IO_WORKER] = {"io_worker", IoWorkerMain, true},
-	[B_STARTUP] = {"startup", StartupProcessMain, true},
-	[B_WAL_RECEIVER] = {"wal_receiver", WalReceiverMain, true},
-	[B_WAL_SUMMARIZER] = {"wal_summarizer", WalSummarizerMain, true},
-	[B_WAL_WRITER] = {"wal_writer", WalWriterMain, true},
-
-	[B_LOGGER] = {"syslogger", SysLoggerMain, false},
+#define PG_PROCTYPE(bktype, description, main_func, shmem_attach) \
+	[bktype] = {description, main_func, shmem_attach},
+#include "postmaster/proctypelist.h"
+#undef PG_PROCTYPE
 };
 
 const char *
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index 43b4dbccc3d..dec220a61f5 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -266,62 +266,11 @@ GetBackendTypeDesc(BackendType backendType)
 
 	switch (backendType)
 	{
-		case B_INVALID:
-			backendDesc = gettext_noop("not initialized");
-			break;
-		case B_ARCHIVER:
-			backendDesc = gettext_noop("archiver");
-			break;
-		case B_AUTOVAC_LAUNCHER:
-			backendDesc = gettext_noop("autovacuum launcher");
-			break;
-		case B_AUTOVAC_WORKER:
-			backendDesc = gettext_noop("autovacuum worker");
-			break;
-		case B_BACKEND:
-			backendDesc = gettext_noop("client backend");
-			break;
-		case B_DEAD_END_BACKEND:
-			backendDesc = gettext_noop("dead-end client backend");
-			break;
-		case B_BG_WORKER:
-			backendDesc = gettext_noop("background worker");
-			break;
-		case B_BG_WRITER:
-			backendDesc = gettext_noop("background writer");
-			break;
-		case B_CHECKPOINTER:
-			backendDesc = gettext_noop("checkpointer");
-			break;
-		case B_IO_WORKER:
-			backendDesc = gettext_noop("io worker");
-			break;
-		case B_LOGGER:
-			backendDesc = gettext_noop("logger");
-			break;
-		case B_SLOTSYNC_WORKER:
-			backendDesc = gettext_noop("slotsync worker");
-			break;
-		case B_STANDALONE_BACKEND:
-			backendDesc = gettext_noop("standalone backend");
-			break;
-		case B_STARTUP:
-			backendDesc = gettext_noop("startup");
-			break;
-		case B_WAL_RECEIVER:
-			backendDesc = gettext_noop("walreceiver");
-			break;
-		case B_WAL_SENDER:
-			backendDesc = gettext_noop("walsender");
-			break;
-		case B_WAL_SUMMARIZER:
-			backendDesc = gettext_noop("walsummarizer");
-			break;
-		case B_WAL_WRITER:
-			backendDesc = gettext_noop("walwriter");
-			break;
+#define PG_PROCTYPE(bktype, description, main_func, shmem_attach) \
+		case bktype: backendDesc = gettext_noop(description); break;
+#include "postmaster/proctypelist.h"
+#undef PG_PROCTYPE
 	}
-
 	return backendDesc;
 }
 
diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h
new file mode 100644
index 00000000000..919f00bb3a7
--- /dev/null
+++ b/src/include/postmaster/proctypelist.h
@@ -0,0 +1,50 @@
+/*-------------------------------------------------------------------------
+ *
+ * proctypelist.h
+ *
+ * The list of process types is kept on its own source file for use by
+ * automatic tools.  The exact representation of a process type is
+ * determined by the PG_PROCTYPE macro, which is not defined in this
+ * file; it can be defined by the caller for special purposes.
+ *
+ * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/include/postmaster/proctypelist.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/* there is deliberately not an #ifndef PROCTYPELIST_H here */
+
+/*
+ * WAL senders start their life as regular backend processes, and change their
+ * type after authenticating the client for replication.  We list it here for
+ * PostmasterChildName() but cannot launch them directly.
+ */
+
+/*
+ * List of process types (symbol, description, Main function, shmem_attach)
+ * entries.
+ */
+
+/* bktype, description, main_func, shmem_attach */
+PG_PROCTYPE(B_ARCHIVER, "archiver", PgArchiverMain, true)
+PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum launcher", AutoVacLauncherMain, true)
+PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum worker", AutoVacWorkerMain, true)
+PG_PROCTYPE(B_BACKEND, "client backend", BackendMain, true)
+PG_PROCTYPE(B_BG_WORKER, "background worker", BackgroundWorkerMain, true)
+PG_PROCTYPE(B_BG_WRITER, "background writer", BackgroundWriterMain, true)
+PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", CheckpointerMain, true)
+PG_PROCTYPE(B_DEAD_END_BACKEND, "dead-end client backend", BackendMain, true)
+PG_PROCTYPE(B_INVALID, "unrecognized", NULL, false)
+PG_PROCTYPE(B_IO_WORKER, "io worker", IoWorkerMain, true)
+PG_PROCTYPE(B_LOGGER, "syslogger", SysLoggerMain, false)
+PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsync worker", ReplSlotSyncWorkerMain, true)
+PG_PROCTYPE(B_STANDALONE_BACKEND, "standalone backend", NULL, false)
+PG_PROCTYPE(B_STARTUP, "startup", StartupProcessMain, true)
+PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", WalReceiverMain, true)
+PG_PROCTYPE(B_WAL_SENDER, "walsender", NULL, true)
+PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", WalSummarizerMain, true)
+PG_PROCTYPE(B_WAL_WRITER, "walwriter", WalWriterMain, true)
-- 
2.39.5



  [text/x-patch] v3-0002-log_min_messages-per-backend-type.patch (24.7K, ../../c0135750-48d8-4a13-9b7c-426f27888e9b@app.fastmail.com/3-v3-0002-log_min_messages-per-backend-type.patch)
  download | inline diff:
From a36fb59413fc6cca46a87accebebd08e515429a8 Mon Sep 17 00:00:00 2001
From: Euler Taveira <euler@eulerto.com>
Date: Mon, 11 Dec 2023 17:24:50 -0300
Subject: [PATCH v3 2/2] log_min_messages per backend type

Change log_min_messages from a single element to a comma-separated list
of elements. Each element is backendtype:loglevel. The backendtype are
archiver, autovacuum (includes launcher and workers), backend, bgworker,
bgwriter, checkpointer, ioworker, logger, slotsyncworker, walreceiver,
walsender, walsummarizer and walwriter. A single log level should be
part of this list that is applied as a final step to the backend types
that are not informed.

The old syntax (a single log level) is still accepted for backward
compatibility. In this case, it means the informed log level is applied
for the backend types that are not informed. The default log level is
the same (WARNING) for all backend types.
---
 doc/src/sgml/config.sgml                      |  32 ++-
 src/backend/commands/extension.c              |   2 +-
 src/backend/commands/variable.c               | 196 ++++++++++++++++++
 src/backend/postmaster/launch_backend.c       |   2 +-
 src/backend/utils/error/elog.c                |   2 +-
 src/backend/utils/init/miscinit.c             |   2 +-
 src/backend/utils/misc/guc_tables.c           |  48 +++--
 src/backend/utils/misc/postgresql.conf.sample |   1 +
 src/include/postmaster/proctypelist.h         |  38 ++--
 src/include/utils/guc.h                       |   2 +-
 src/include/utils/guc_hooks.h                 |   2 +
 src/include/utils/guc_tables.h                |   4 +
 src/test/regress/expected/guc.out             |  51 +++++
 src/test/regress/sql/guc.sql                  |  17 ++
 14 files changed, 353 insertions(+), 46 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 20ccb2d6b54..7c4002f2266 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -7021,7 +7021,7 @@ local0.*    /var/log/postgresql
      <variablelist>
 
      <varlistentry id="guc-log-min-messages" xreflabel="log_min_messages">
-      <term><varname>log_min_messages</varname> (<type>enum</type>)
+      <term><varname>log_min_messages</varname> (<type>string</type>)
       <indexterm>
        <primary><varname>log_min_messages</varname> configuration parameter</primary>
       </indexterm>
@@ -7030,14 +7030,28 @@ local0.*    /var/log/postgresql
        <para>
         Controls which <link linkend="runtime-config-severity-levels">message
         levels</link> are written to the server log.
-        Valid values are <literal>DEBUG5</literal>, <literal>DEBUG4</literal>,
-        <literal>DEBUG3</literal>, <literal>DEBUG2</literal>, <literal>DEBUG1</literal>,
-        <literal>INFO</literal>, <literal>NOTICE</literal>, <literal>WARNING</literal>,
-        <literal>ERROR</literal>, <literal>LOG</literal>, <literal>FATAL</literal>, and
-        <literal>PANIC</literal>.  Each level includes all the levels that
-        follow it.  The later the level, the fewer messages are sent
-        to the log.  The default is <literal>WARNING</literal>.  Note that
-        <literal>LOG</literal> has a different rank here than in
+        Valid values are a comma-separated list of <literal>backendtype:level</literal>
+        and a single <literal>level</literal>. The list allows it to use
+        different levels per backend type.
+        Valid <literal>backendtype</literal> values are <literal>archiver</literal>,
+        <literal>autovacuum</literal>, <literal>backend</literal>,
+        <literal>bgworker</literal>, <literal>bgwriter</literal>,
+        <literal>checkpointer</literal>, <literal>ioworker</literal>,
+        <literal>logger</literal>, <literal>slotsyncworker</literal>,
+        <literal>startup</literal>, <literal>walreceiver</literal>,
+        <literal>walsender</literal>, <literal>walsummarizer</literal>, and
+        <literal>walwriter</literal>.
+        Valid <literal>LEVEL</literal> values are <literal>DEBUG5</literal>,
+        <literal>DEBUG4</literal>, <literal>DEBUG3</literal>, <literal>DEBUG2</literal>,
+        <literal>DEBUG1</literal>, <literal>INFO</literal>, <literal>NOTICE</literal>,
+        <literal>WARNING</literal>, <literal>ERROR</literal>, <literal>LOG</literal>,
+        <literal>FATAL</literal>, and <literal>PANIC</literal>.  Each level includes
+        all the levels that follow it.  The later the level, the fewer messages are sent
+        to the log. A single <literal>LEVEL</literal> is mandatory (order does
+        not matter) and it is assigned to the backend types that are not
+        specified in the list.  The default is <literal>WARNING</literal> for
+        all backend types.
+        Note that <literal>LOG</literal> has a different rank here than in
         <xref linkend="guc-client-min-messages"/>.
         Only superusers and users with the appropriate <literal>SET</literal>
         privilege can change this setting.
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index e6f9ab6dfd6..b6055431d8f 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1143,7 +1143,7 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
 		(void) set_config_option("client_min_messages", "warning",
 								 PGC_USERSET, PGC_S_SESSION,
 								 GUC_ACTION_SAVE, true, 0, false);
-	if (log_min_messages < WARNING)
+	if (log_min_messages[MyBackendType] < WARNING)
 		(void) set_config_option_ext("log_min_messages", "warning",
 									 PGC_SUSET, PGC_S_SESSION,
 									 BOOTSTRAP_SUPERUSERID,
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index 608f10d9412..5c769dd7bcc 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -35,6 +35,7 @@
 #include "utils/datetime.h"
 #include "utils/fmgrprotos.h"
 #include "utils/guc_hooks.h"
+#include "utils/guc_tables.h"
 #include "utils/snapmgr.h"
 #include "utils/syscache.h"
 #include "utils/timestamp.h"
@@ -1257,3 +1258,198 @@ check_ssl(bool *newval, void **extra, GucSource source)
 #endif
 	return true;
 }
+
+/*
+ * GUC check_hook for log_min_messages
+ *
+ * The parsing consists of a comma-separated list of BACKENDTYPENAME:LEVEL
+ * elements. BACKENDTYPENAME is log_min_messages_backend_types.  LEVEL is
+ * server_message_level_options. A single LEVEL element should be part of this
+ * list and it is applied as a final step to the backend types that are not
+ * specified. For backward compatibility, the old syntax is still accepted and
+ * it means to apply this level for all backend types.
+ */
+bool
+check_log_min_messages(char **newval, void **extra, GucSource source)
+{
+	char	   *rawstring;
+	List	   *elemlist;
+	ListCell   *l;
+	int			newlevels[BACKEND_NUM_TYPES];
+	bool		assigned[BACKEND_NUM_TYPES];
+	int			genericlevel = -1;	/* -1 means not assigned */
+
+	/* Initialize the array. */
+	memset(newlevels, WARNING, BACKEND_NUM_TYPES * sizeof(int));
+	memset(assigned, false, BACKEND_NUM_TYPES * sizeof(bool));
+
+	/* Need a modifiable copy of string. */
+	rawstring = pstrdup(*newval);
+
+	/* Parse string into list of identifiers. */
+	if (!SplitGUCList(rawstring, ',', &elemlist))
+	{
+		/* syntax error in list */
+		GUC_check_errdetail("List syntax is invalid.");
+		pfree(rawstring);
+		list_free(elemlist);
+		return false;
+	}
+
+	/* Validate and assign log level and backend type. */
+	foreach(l, elemlist)
+	{
+		char	   *tok = (char *) lfirst(l);
+		char	   *sep;
+		const struct config_enum_entry *entry;
+
+		/*
+		 * Check whether there is a backend type following the log level. If
+		 * there is no separator, it means this log level should be applied
+		 * for all backend types (backward compatibility).
+		 */
+		sep = strchr(tok, ':');
+		if (sep == NULL)
+		{
+			bool		found = false;
+
+			/* Reject duplicates for generic log level. */
+			if (genericlevel != -1)
+			{
+				GUC_check_errdetail("Generic log level was already assigned.");
+				pfree(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			/* Is the log level valid? */
+			for (entry = server_message_level_options; entry && entry->name; entry++)
+			{
+				if (pg_strcasecmp(entry->name, tok) == 0)
+				{
+					genericlevel = entry->val;
+					found = true;
+					break;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized log level: \"%s\".", tok);
+				pfree(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+		}
+		else
+		{
+			char	   *loglevel;
+			char	   *btype;
+			bool		found = false;
+
+			btype = palloc((sep - tok) + 1);
+			memcpy(btype, tok, sep - tok);
+			btype[sep - tok] = '\0';
+			loglevel = pstrdup(sep + 1);
+
+			/* Is the log level valid? */
+			for (entry = server_message_level_options; entry && entry->name; entry++)
+			{
+				if (pg_strcasecmp(entry->name, loglevel) == 0)
+				{
+					found = true;
+					break;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel);
+				pfree(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			/*
+			 * Is the backend type name valid? There might be multiple entries
+			 * per backend type, don't bail out when find first occurrence.
+			 */
+			found = false;
+			for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+			{
+				if (pg_strcasecmp(log_min_messages_backend_types[i], btype) == 0)
+				{
+					/* Reject duplicates for a backend type. */
+					if (assigned[i])
+					{
+						GUC_check_errdetail("Backend type \"%s\" was already assigned.", btype);
+						pfree(rawstring);
+						list_free(elemlist);
+						return false;
+					}
+
+					newlevels[i] = entry->val;
+					assigned[i] = true;
+					found = true;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized backend type: \"%s\".", btype);
+				pfree(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+		}
+	}
+
+	/*
+	 * Generic log level must be specified. It is a good idea to specify a
+	 * generic log level to make it clear that it is the fallback value.
+	 * Although, we can document it, it might confuse users that used to
+	 * specify a single log level in prior releases.
+	 */
+	if (genericlevel == -1)
+	{
+		GUC_check_errdetail("Generic log level was not defined.");
+		pfree(rawstring);
+		list_free(elemlist);
+		return false;
+	}
+
+	/*
+	 * Apply the generic log level (the one without a backend type) after all
+	 * of the specific backend type have been assigned. Hence, it doesn't
+	 * matter the order you specify the generic log level, the final result
+	 * will be the same.
+	 */
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+	{
+		if (!assigned[i])
+			newlevels[i] = genericlevel;
+	}
+
+	pfree(rawstring);
+	list_free(elemlist);
+
+	/*
+	 * Pass back data for assign_log_min_messages to use.
+	 */
+	*extra = guc_malloc(LOG, BACKEND_NUM_TYPES * sizeof(int));
+	if (!*extra)
+		return false;
+	memcpy(*extra, newlevels, BACKEND_NUM_TYPES * sizeof(int));
+
+	return true;
+}
+
+/*
+ * GUC assign_hook for log_min_messages
+ */
+void
+assign_log_min_messages(const char *newval, void *extra)
+{
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+		log_min_messages[i] = ((int *) extra)[i];
+}
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 8b2f1a0cf41..08395dafe04 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -177,7 +177,7 @@ typedef struct
 } child_process_kind;
 
 static child_process_kind child_process_kinds[] = {
-#define PG_PROCTYPE(bktype, description, main_func, shmem_attach) \
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
 	[bktype] = {description, main_func, shmem_attach},
 #include "postmaster/proctypelist.h"
 #undef PG_PROCTYPE
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 47af743990f..77cafe6e7b5 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -235,7 +235,7 @@ is_log_level_output(int elevel, int log_min_level)
 static inline bool
 should_output_to_server(int elevel)
 {
-	return is_log_level_output(elevel, log_min_messages);
+	return is_log_level_output(elevel, log_min_messages[MyBackendType]);
 }
 
 /*
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index dec220a61f5..8a919148fd7 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType)
 
 	switch (backendType)
 	{
-#define PG_PROCTYPE(bktype, description, main_func, shmem_attach) \
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
 		case bktype: backendDesc = gettext_noop(description); break;
 #include "postmaster/proctypelist.h"
 #undef PG_PROCTYPE
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index d14b1678e7f..300e172aa82 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -146,7 +146,7 @@ static const struct config_enum_entry client_message_level_options[] = {
 	{NULL, 0, false}
 };
 
-static const struct config_enum_entry server_message_level_options[] = {
+const struct config_enum_entry server_message_level_options[] = {
 	{"debug5", DEBUG5, false},
 	{"debug4", DEBUG4, false},
 	{"debug3", DEBUG3, false},
@@ -536,7 +536,6 @@ static bool default_with_oids = false;
 bool		current_role_is_superuser;
 
 int			log_min_error_statement = ERROR;
-int			log_min_messages = WARNING;
 int			client_min_messages = NOTICE;
 int			log_min_duration_sample = -1;
 int			log_min_duration_statement = -1;
@@ -594,6 +593,7 @@ static char *server_version_string;
 static int	server_version_num;
 static char *debug_io_direct_string;
 static char *restrict_nonsystem_relation_kind_string;
+static char *log_min_messages_string;
 
 #ifdef HAVE_SYSLOG
 #define	DEFAULT_SYSLOG_FACILITY LOG_LOCAL0
@@ -638,6 +638,27 @@ char	   *role_string;
 /* should be static, but guc.c needs to get at this */
 bool		in_hot_standby_guc;
 
+/*
+ * It should be static, but commands/variable.c needs to get at this.
+ */
+int			log_min_messages[] = {
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
+	[bktype] = log_min_messages,
+#include "postmaster/proctypelist.h"
+#undef PG_PROCTYPE
+};
+
+/*
+ * It might be in commands/variable.c but for convenience it is near
+ * log_min_messages.
+ */
+const char *const log_min_messages_backend_types[] = {
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
+	[bktype] = bkcategory,
+#include "postmaster/proctypelist.h"
+#undef PG_PROCTYPE
+};
+
 
 /*
  * Displayable names for context types (enum GucContext)
@@ -4298,6 +4319,18 @@ struct config_string ConfigureNamesString[] =
 		check_client_encoding, assign_client_encoding, NULL
 	},
 
+	{
+		{"log_min_messages", PGC_SUSET, LOGGING_WHEN,
+			gettext_noop("Sets the message levels that are logged."),
+			gettext_noop("Each level includes all the levels that follow it. The later"
+						 " the level, the fewer messages are sent."),
+			GUC_LIST_INPUT
+		},
+		&log_min_messages_string,
+		"WARNING",
+		check_log_min_messages, assign_log_min_messages, NULL
+	},
+
 	{
 		{"log_line_prefix", PGC_SIGHUP, LOGGING_WHAT,
 			gettext_noop("Controls information prefixed to each log line."),
@@ -5110,17 +5143,6 @@ struct config_enum ConfigureNamesEnum[] =
 		NULL, NULL, NULL
 	},
 
-	{
-		{"log_min_messages", PGC_SUSET, LOGGING_WHEN,
-			gettext_noop("Sets the message levels that are logged."),
-			gettext_noop("Each level includes all the levels that follow it. The later"
-						 " the level, the fewer messages are sent.")
-		},
-		&log_min_messages,
-		WARNING, server_message_level_options,
-		NULL, NULL, NULL
-	},
-
 	{
 		{"log_min_error_statement", PGC_SUSET, LOGGING_WHEN,
 			gettext_noop("Causes all statements generating error at or above this level to be logged."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index a9d8293474a..c266effc597 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -539,6 +539,7 @@
 					#   log
 					#   fatal
 					#   panic
+					#   or a comma-separated list of backend_type:level
 
 #log_min_error_statement = error	# values in order of decreasing detail:
 					#   debug5
diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h
index 919f00bb3a7..356cd45815a 100644
--- a/src/include/postmaster/proctypelist.h
+++ b/src/include/postmaster/proctypelist.h
@@ -29,22 +29,22 @@
  * entries.
  */
 
-/* bktype, description, main_func, shmem_attach */
-PG_PROCTYPE(B_ARCHIVER, "archiver", PgArchiverMain, true)
-PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum launcher", AutoVacLauncherMain, true)
-PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum worker", AutoVacWorkerMain, true)
-PG_PROCTYPE(B_BACKEND, "client backend", BackendMain, true)
-PG_PROCTYPE(B_BG_WORKER, "background worker", BackgroundWorkerMain, true)
-PG_PROCTYPE(B_BG_WRITER, "background writer", BackgroundWriterMain, true)
-PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", CheckpointerMain, true)
-PG_PROCTYPE(B_DEAD_END_BACKEND, "dead-end client backend", BackendMain, true)
-PG_PROCTYPE(B_INVALID, "unrecognized", NULL, false)
-PG_PROCTYPE(B_IO_WORKER, "io worker", IoWorkerMain, true)
-PG_PROCTYPE(B_LOGGER, "syslogger", SysLoggerMain, false)
-PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsync worker", ReplSlotSyncWorkerMain, true)
-PG_PROCTYPE(B_STANDALONE_BACKEND, "standalone backend", NULL, false)
-PG_PROCTYPE(B_STARTUP, "startup", StartupProcessMain, true)
-PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", WalReceiverMain, true)
-PG_PROCTYPE(B_WAL_SENDER, "walsender", NULL, true)
-PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", WalSummarizerMain, true)
-PG_PROCTYPE(B_WAL_WRITER, "walwriter", WalWriterMain, true)
+/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */
+PG_PROCTYPE(B_ARCHIVER, "archiver", "archiver", PgArchiverMain, true, WARNING)
+PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", "autovacuum launcher", AutoVacLauncherMain, true, WARNING)
+PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", "autovacuum worker", AutoVacWorkerMain, true, WARNING)
+PG_PROCTYPE(B_BACKEND, "backend", "client backend", BackendMain, true, WARNING)
+PG_PROCTYPE(B_BG_WORKER, "bgworker", "background worker", BackgroundWorkerMain, true, WARNING)
+PG_PROCTYPE(B_BG_WRITER, "bgwriter", "background writer", BackgroundWriterMain, true, WARNING)
+PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", "checkpointer", CheckpointerMain, true, WARNING)
+PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", "dead-end client backend", BackendMain, true, WARNING)
+PG_PROCTYPE(B_INVALID, "backend", "invalid", NULL, false, WARNING)
+PG_PROCTYPE(B_IO_WORKER, "ioworker", "io worker", IoWorkerMain, true, WARNING)
+PG_PROCTYPE(B_LOGGER, "logger", "syslogger", SysLoggerMain, false, WARNING)
+PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", "slotsync worker", ReplSlotSyncWorkerMain, true, WARNING)
+PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", "standalone backend", NULL, false, WARNING)
+PG_PROCTYPE(B_STARTUP, "startup", "startup", StartupProcessMain, true, WARNING)
+PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", "walreceiver", WalReceiverMain, true, WARNING)
+PG_PROCTYPE(B_WAL_SENDER, "walsender", "walsender", NULL, true, WARNING)
+PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", "walsummarizer", WalSummarizerMain, true, WARNING)
+PG_PROCTYPE(B_WAL_WRITER, "walwriter", "walwriter", WalWriterMain, true, WARNING)
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index f619100467d..e10e24940a7 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -271,7 +271,7 @@ extern PGDLLIMPORT bool log_duration;
 extern PGDLLIMPORT int log_parameter_max_length;
 extern PGDLLIMPORT int log_parameter_max_length_on_error;
 extern PGDLLIMPORT int log_min_error_statement;
-extern PGDLLIMPORT int log_min_messages;
+extern PGDLLIMPORT int log_min_messages[];
 extern PGDLLIMPORT int client_min_messages;
 extern PGDLLIMPORT int log_min_duration_sample;
 extern PGDLLIMPORT int log_min_duration_statement;
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index 82ac8646a8d..e6ffc96468f 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -174,5 +174,7 @@ extern void assign_wal_sync_method(int new_wal_sync_method, void *extra);
 extern bool check_synchronized_standby_slots(char **newval, void **extra,
 											 GucSource source);
 extern void assign_synchronized_standby_slots(const char *newval, void *extra);
+extern bool check_log_min_messages(char **newval, void **extra, GucSource source);
+extern void assign_log_min_messages(const char *newval, void *extra);
 
 #endif							/* GUC_HOOKS_H */
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index f72ce944d7f..2aefc653f20 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -298,6 +298,10 @@ struct config_enum
 	void	   *reset_extra;
 };
 
+/* log_min_messages */
+extern PGDLLIMPORT const char *const log_min_messages_backend_types[];
+extern PGDLLIMPORT const struct config_enum_entry server_message_level_options[];
+
 /* constant tables corresponding to enums above and in guc.h */
 extern PGDLLIMPORT const char *const config_group_names[];
 extern PGDLLIMPORT const char *const config_type_names[];
diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out
index 7f9e29c765c..741ad6bf062 100644
--- a/src/test/regress/expected/guc.out
+++ b/src/test/regress/expected/guc.out
@@ -913,3 +913,54 @@ SELECT name FROM tab_settings_flags
 (0 rows)
 
 DROP TABLE tab_settings_flags;
+-- Test log_min_messages
+SET log_min_messages TO fatal;
+SHOW log_min_messages;
+ log_min_messages 
+------------------
+ fatal
+(1 row)
+
+SET log_min_messages TO 'fatal';
+SHOW log_min_messages;
+ log_min_messages 
+------------------
+ fatal
+(1 row)
+
+SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1';  --fail
+ERROR:  invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1"
+DETAIL:  Generic log level was not defined.
+SET log_min_messages TO 'debug1, backend:error, fatal';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "debug1, backend:error, fatal"
+DETAIL:  Generic log level was already assigned.
+SET log_min_messages TO 'backend:error, debug1, backend:warning';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning"
+DETAIL:  Backend type "backend" was already assigned.
+SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1"
+DETAIL:  Unrecognized backend type: "foo".
+SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1"
+DETAIL:  Unrecognized log level: "bar".
+SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3';
+SHOW log_min_messages;
+                                        log_min_messages                                         
+-------------------------------------------------------------------------------------------------
+ backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3
+(1 row)
+
+SET log_min_messages TO 'warning, autovacuum:debug1';
+SHOW log_min_messages;
+      log_min_messages      
+----------------------------
+ warning, autovacuum:debug1
+(1 row)
+
+SET log_min_messages TO 'autovacuum:debug1, warning';
+SHOW log_min_messages;
+      log_min_messages      
+----------------------------
+ autovacuum:debug1, warning
+(1 row)
+
diff --git a/src/test/regress/sql/guc.sql b/src/test/regress/sql/guc.sql
index f65f84a2632..12fa1812f35 100644
--- a/src/test/regress/sql/guc.sql
+++ b/src/test/regress/sql/guc.sql
@@ -368,3 +368,20 @@ SELECT name FROM tab_settings_flags
   WHERE no_reset AND NOT no_reset_all
   ORDER BY 1;
 DROP TABLE tab_settings_flags;
+
+-- Test log_min_messages
+SET log_min_messages TO fatal;
+SHOW log_min_messages;
+SET log_min_messages TO 'fatal';
+SHOW log_min_messages;
+SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1';  --fail
+SET log_min_messages TO 'debug1, backend:error, fatal';  -- fail
+SET log_min_messages TO 'backend:error, debug1, backend:warning';  -- fail
+SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1';  -- fail
+SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1';  -- fail
+SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3';
+SHOW log_min_messages;
+SET log_min_messages TO 'warning, autovacuum:debug1';
+SHOW log_min_messages;
+SET log_min_messages TO 'autovacuum:debug1, warning';
+SHOW log_min_messages;
-- 
2.39.5



^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-07-31 16:22  Japin Li <japinli@hotmail.com>
  parent: Euler Taveira <euler@eulerto.com>
  1 sibling, 1 reply; 58+ messages in thread

From: Japin Li @ 2025-07-31 16:22 UTC (permalink / raw)
  To: Euler Taveira <euler@eulerto.com>; +Cc: Andres Freund <andres@anarazel.de>; Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers@lists.postgresql.org

On Thu, 31 Jul 2025 at 11:19, "Euler Taveira" <euler@eulerto.com> wrote:
> On Thu, Mar 6, 2025, at 10:33 AM, Andres Freund wrote:
>> Huh, the startup process is among the most crucial things to monitor?
>>
>
> Good point. Fixed.
>
> After collecting some suggestions, I'm attaching a new patch contains the
> following changes:
>
> - patch was rebased
> - include Alvaro's patch (v2-0001) [1] as a basis for this patch
> - add ioworker as new backend type
> - add startup as new backend type per Andres suggestion
> - small changes into documentation
>
>> I don't know what I think about the whole patch, but I do want to voice
>> *strong* opposition to duplicating a list of all backend types into multiple
>> places. It's already painfull enough to add a new backend type, without having
>> to pointlessly go around and manually add a new backend type to mulltiple
>> arrays that have completely predictable content.
>>
>
> I'm including Alvaro's patch as is just to make the CF bot happy and to
> illustrate how it would be if we adopt his solution to centralize the list of
> backend types. I think Alvaro's proposal overcomes the objection [2], right?
>

If we set the log level for all backend types, I don't think a generic log
level is necessary.

-- 
Regards,
Japin Li
ChengDu WenWu Information Technology Co., Ltd.





^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-07-31 16:51  Alvaro Herrera <alvherre@alvh.no-ip.org>
  parent: Japin Li <japinli@hotmail.com>
  0 siblings, 1 reply; 58+ messages in thread

From: Alvaro Herrera @ 2025-07-31 16:51 UTC (permalink / raw)
  To: Japin Li <japinli@hotmail.com>; +Cc: Euler Taveira <euler@eulerto.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org

On 2025-Aug-01, Japin Li wrote:

> If we set the log level for all backend types, I don't think a generic log
> level is necessary.

I don't understand what you mean by this.  Can you elaborate?

-- 
Álvaro Herrera               48°01'N 7°57'E  —  https://www.EnterpriseDB.com/
"If it is not right, do not do it.
If it is not true, do not say it." (Marcus Aurelius, Meditations)





^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-07-31 23:34  Japin Li <japinli@hotmail.com>
  parent: Alvaro Herrera <alvherre@alvh.no-ip.org>
  0 siblings, 1 reply; 58+ messages in thread

From: Japin Li @ 2025-07-31 23:34 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: Euler Taveira <euler@eulerto.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org

On Thu, 31 Jul 2025 at 18:51, Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:
> On 2025-Aug-01, Japin Li wrote:
>
>> If we set the log level for all backend types, I don't think a generic log
>> level is necessary.
>
> I don't understand what you mean by this.  Can you elaborate?
>
For example:

ALTER SYSTEM SET log_min_messages TO
'archiver:info, autovacuum:info, backend:info, bgworker:info, bgwriter:info, checkpointer:info, ioworker:info, logger:info, slotsyncworker:info, startup:info, walreceiver:info, walsender:info, walsummarizer:info, walwriter:info';

Given that we've set a log level for every backend type and
assigned[BACKEND_NUM_TYPES] is true, a generic level seems redundant.

-- 
Regards,
Japin Li
ChengDu WenWu Information Technology Co., Ltd.





^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-08-01 05:53  Japin Li <japinli@hotmail.com>
  parent: Euler Taveira <euler@eulerto.com>
  1 sibling, 0 replies; 58+ messages in thread

From: Japin Li @ 2025-08-01 05:53 UTC (permalink / raw)
  To: Euler Taveira <euler@eulerto.com>; +Cc: Andres Freund <andres@anarazel.de>; Alvaro Herrera <alvherre@alvh.no-ip.org>; pgsql-hackers@lists.postgresql.org

On Thu, Jul 31, 2025 at 11:19:48AM -0300, Euler Taveira wrote:
> On Thu, Mar 6, 2025, at 10:33 AM, Andres Freund wrote:
> > Huh, the startup process is among the most crucial things to monitor?
> >
> 
> Good point. Fixed.
> 
> After collecting some suggestions, I'm attaching a new patch contains the
> following changes:
> 
> - patch was rebased
> - include Alvaro's patch (v2-0001) [1] as a basis for this patch
> - add ioworker as new backend type
> - add startup as new backend type per Andres suggestion
> - small changes into documentation
> 
> > I don't know what I think about the whole patch, but I do want to voice
> > *strong* opposition to duplicating a list of all backend types into multiple
> > places. It's already painfull enough to add a new backend type, without having
> > to pointlessly go around and manually add a new backend type to mulltiple
> > arrays that have completely predictable content.
> >
> 
> I'm including Alvaro's patch as is just to make the CF bot happy and to
> illustrate how it would be if we adopt his solution to centralize the list of
> backend types. I think Alvaro's proposal overcomes the objection [2], right?
> 

I think we can avoid memory allocation by using pg_strncasecmp().

diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index 5c769dd7bcc..f854b2fac93 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -1343,14 +1343,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 		}
 		else
 		{
-			char	   *loglevel;
-			char	   *btype;
-			bool		found = false;

-			btype = palloc((sep - tok) + 1);
-			memcpy(btype, tok, sep - tok);
-			btype[sep - tok] = '\0';
-			loglevel = pstrdup(sep + 1);
+			char	   *btype = tok;
+			char	   *loglevel = sep + 1;
+			bool		found = false;

 			/* Is the log level valid? */
 			for (entry = server_message_level_options; entry && entry->name; entry++)
@@ -1377,7 +1373,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 			found = false;
 			for (int i = 0; i < BACKEND_NUM_TYPES; i++)
 			{
-				if (pg_strcasecmp(log_min_messages_backend_types[i], btype) == 0)
+				if (pg_strncasecmp(log_min_messages_backend_types[i], btype, sep - tok) == 0)
 				{
 					/* Reject duplicates for a backend type. */
 					if (assigned[i])

-- 
Best regards,
Japin Li
ChengDu WenWu Information Technology Co., LTD.





^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-10-05 14:18  Euler Taveira <euler@eulerto.com>
  parent: Japin Li <japinli@hotmail.com>
  0 siblings, 1 reply; 58+ messages in thread

From: Euler Taveira @ 2025-10-05 14:18 UTC (permalink / raw)
  To: japin <japinli@hotmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org

On Thu, Jul 31, 2025, at 8:34 PM, Japin Li wrote:
> On Thu, 31 Jul 2025 at 18:51, Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:
>> On 2025-Aug-01, Japin Li wrote:
>>
>>> If we set the log level for all backend types, I don't think a generic log
>>> level is necessary.
>>
>> I don't understand what you mean by this.  Can you elaborate?
>>
> For example:
>
> ALTER SYSTEM SET log_min_messages TO
> 'archiver:info, autovacuum:info, backend:info, bgworker:info, 
> bgwriter:info, checkpointer:info, ioworker:info, logger:info, 
> slotsyncworker:info, startup:info, walreceiver:info, walsender:info, 
> walsummarizer:info, walwriter:info';
>
> Given that we've set a log level for every backend type and
> assigned[BACKEND_NUM_TYPES] is true, a generic level seems redundant.
>

The main reason I didn't consider this idea was that this GUC assignment will
fail as soon as a new backend type is added. Restore a dump should work
across major versions.

> I think we can avoid memory allocation by using pg_strncasecmp().
>

loglevel does not required a memory allocation but I didn't include the btype
part because it complicates the error message that uses btype a few lines
above.

This new patch contains the following changes:

- patch was rebased
- use commit dbf8cfb4f02
- use some GUC memory allocation functions
- avoid one memory allocation (suggested by Japin Li)
- rename backend type: logger -> syslogger
- adjust tests to increase test coverage
- improve documentation and comments to reflect the current state


-- 
Euler Taveira
EDB   https://www.enterprisedb.com/

Attachments:

  [text/x-patch] v4-0001-log_min_messages-per-backend-type.patch (26.0K, ../../24b9bfd6-7fba-4946-9b73-244934b89464@app.fastmail.com/2-v4-0001-log_min_messages-per-backend-type.patch)
  download | inline diff:
From 37a99059c8eaed5bece5d56530892edf348d2e67 Mon Sep 17 00:00:00 2001
From: Euler Taveira <euler@eulerto.com>
Date: Tue, 30 Sep 2025 10:59:25 -0300
Subject: [PATCH v4] log_min_messages per backend type

Change log_min_messages from a single element to a comma-separated list
of elements. Each element is backendtype:loglevel. The backendtype are
archiver, autovacuum (includes launcher and workers), backend, bgworker,
bgwriter, checkpointer, ioworker, syslogger, slotsyncworker, startup,
walreceiver, walsender, walsummarizer and walwriter. A single log level
should be part of this list and it is applied as a final step to the
backend types that were not informed.

The old syntax (a single log level) is still accepted for backward
compatibility. In this case, it means the informed log level is applied
for all backend types. The default log level is the same (WARNING) for
all backend types.
---
 doc/src/sgml/config.sgml                      |  31 ++-
 src/backend/commands/extension.c              |   2 +-
 src/backend/commands/variable.c               | 205 ++++++++++++++++++
 src/backend/postmaster/launch_backend.c       |   2 +-
 src/backend/utils/error/elog.c                |   2 +-
 src/backend/utils/init/miscinit.c             |   2 +-
 src/backend/utils/misc/guc_parameters.dat     |  18 +-
 src/backend/utils/misc/guc_tables.c           |  25 ++-
 src/backend/utils/misc/postgresql.conf.sample |   2 +
 src/include/postmaster/proctypelist.h         |  38 ++--
 src/include/utils/guc.h                       |   2 +-
 src/include/utils/guc_hooks.h                 |   2 +
 src/include/utils/guc_tables.h                |   4 +
 src/test/regress/expected/guc.out             |  54 +++++
 src/test/regress/sql/guc.sql                  |  18 ++
 15 files changed, 364 insertions(+), 43 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index e9b420f3ddb..b31d5875838 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -7022,7 +7022,7 @@ local0.*    /var/log/postgresql
      <variablelist>
 
      <varlistentry id="guc-log-min-messages" xreflabel="log_min_messages">
-      <term><varname>log_min_messages</varname> (<type>enum</type>)
+      <term><varname>log_min_messages</varname> (<type>string</type>)
       <indexterm>
        <primary><varname>log_min_messages</varname> configuration parameter</primary>
       </indexterm>
@@ -7031,14 +7031,27 @@ local0.*    /var/log/postgresql
        <para>
         Controls which <link linkend="runtime-config-severity-levels">message
         levels</link> are written to the server log.
-        Valid values are <literal>DEBUG5</literal>, <literal>DEBUG4</literal>,
-        <literal>DEBUG3</literal>, <literal>DEBUG2</literal>, <literal>DEBUG1</literal>,
-        <literal>INFO</literal>, <literal>NOTICE</literal>, <literal>WARNING</literal>,
-        <literal>ERROR</literal>, <literal>LOG</literal>, <literal>FATAL</literal>, and
-        <literal>PANIC</literal>.  Each level includes all the levels that
-        follow it.  The later the level, the fewer messages are sent
-        to the log.  The default is <literal>WARNING</literal>.  Note that
-        <literal>LOG</literal> has a different rank here than in
+        Valid values are a comma-separated list of <literal>backendtype:level</literal>
+        and a single <literal>level</literal>. The list allows it to use
+        different levels per backend type. Only the single <literal>level</literal>
+        is mandatory (order does not matter) and it is assigned to the backend
+        types that are not specified in the list.
+        Valid <literal>backendtype</literal> values are <literal>archiver</literal>,
+        <literal>autovacuum</literal>, <literal>backend</literal>,
+        <literal>bgworker</literal>, <literal>bgwriter</literal>,
+        <literal>checkpointer</literal>, <literal>ioworker</literal>,
+        <literal>syslogger</literal>, <literal>slotsyncworker</literal>,
+        <literal>startup</literal>, <literal>walreceiver</literal>,
+        <literal>walsender</literal>, <literal>walsummarizer</literal>, and
+        <literal>walwriter</literal>.
+        Valid <literal>level</literal> values are <literal>DEBUG5</literal>,
+        <literal>DEBUG4</literal>, <literal>DEBUG3</literal>, <literal>DEBUG2</literal>,
+        <literal>DEBUG1</literal>, <literal>INFO</literal>, <literal>NOTICE</literal>,
+        <literal>WARNING</literal>, <literal>ERROR</literal>, <literal>LOG</literal>,
+        <literal>FATAL</literal>, and <literal>PANIC</literal>.  Each level includes
+        all the levels that follow it.  The later the level, the fewer messages are sent
+        to the log.  The default is <literal>WARNING</literal> for all backend types.
+        Note that <literal>LOG</literal> has a different rank here than in
         <xref linkend="guc-client-min-messages"/>.
         Only superusers and users with the appropriate <literal>SET</literal>
         privilege can change this setting.
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 93ef1ad106f..81f7bc5e567 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1143,7 +1143,7 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
 		(void) set_config_option("client_min_messages", "warning",
 								 PGC_USERSET, PGC_S_SESSION,
 								 GUC_ACTION_SAVE, true, 0, false);
-	if (log_min_messages < WARNING)
+	if (log_min_messages[MyBackendType] < WARNING)
 		(void) set_config_option_ext("log_min_messages", "warning",
 									 PGC_SUSET, PGC_S_SESSION,
 									 BOOTSTRAP_SUPERUSERID,
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index 608f10d9412..93336fee33b 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -35,6 +35,7 @@
 #include "utils/datetime.h"
 #include "utils/fmgrprotos.h"
 #include "utils/guc_hooks.h"
+#include "utils/guc_tables.h"
 #include "utils/snapmgr.h"
 #include "utils/syscache.h"
 #include "utils/timestamp.h"
@@ -1257,3 +1258,207 @@ check_ssl(bool *newval, void **extra, GucSource source)
 #endif
 	return true;
 }
+
+/*
+ * GUC check_hook for log_min_messages
+ *
+ * The parsing consists of a comma-separated list of BACKENDTYPENAME:LEVEL
+ * elements. BACKENDTYPENAME is log_min_messages_backend_types.  LEVEL is
+ * server_message_level_options. A single LEVEL element should be part of this
+ * list and it is applied as a final step to the backend types that are not
+ * specified. For backward compatibility, the old syntax is still accepted and
+ * it means to apply the LEVEL for all backend types.
+ */
+bool
+check_log_min_messages(char **newval, void **extra, GucSource source)
+{
+	char	   *rawstring;
+	List	   *elemlist;
+	ListCell   *l;
+	int			newlevel[BACKEND_NUM_TYPES];
+	bool		assigned[BACKEND_NUM_TYPES];
+	int			genericlevel = -1;	/* -1 means not assigned */
+
+	/* Initialize the array. */
+	memset(newlevel, WARNING, BACKEND_NUM_TYPES * sizeof(int));
+	memset(assigned, false, BACKEND_NUM_TYPES * sizeof(bool));
+
+	/* Need a modifiable copy of string. */
+	rawstring = guc_strdup(LOG, *newval);
+
+	/* Parse string into list of identifiers. */
+	if (!SplitGUCList(rawstring, ',', &elemlist))
+	{
+		/* syntax error in list */
+		GUC_check_errdetail("List syntax is invalid.");
+		guc_free(rawstring);
+		list_free(elemlist);
+		return false;
+	}
+
+	/* Validate and assign log level and backend type. */
+	foreach(l, elemlist)
+	{
+		char	   *tok = (char *) lfirst(l);
+		char	   *sep;
+		const struct config_enum_entry *entry;
+
+		/*
+		 * Check whether there is a backend type following the log level. If
+		 * there is no separator, it means this is the generic log level. The
+		 * generic log level will be assigned to the backend types that were
+		 * not informed.
+		 */
+		sep = strchr(tok, ':');
+		if (sep == NULL)
+		{
+			bool		found = false;
+
+			/* Reject duplicates for generic log level. */
+			if (genericlevel != -1)
+			{
+				GUC_check_errdetail("Generic log level was already assigned.");
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			/* Is the log level valid? */
+			for (entry = server_message_level_options; entry && entry->name; entry++)
+			{
+				if (pg_strcasecmp(entry->name, tok) == 0)
+				{
+					genericlevel = entry->val;
+					found = true;
+					break;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized log level: \"%s\".", tok);
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+		}
+		else
+		{
+			char	   *loglevel;
+			char	   *btype;
+			bool		found = false;
+
+			btype = guc_malloc(LOG, (sep - tok) + 1);
+			if (!btype)
+			{
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+			memcpy(btype, tok, sep - tok);
+			btype[sep - tok] = '\0';
+			loglevel = sep + 1;
+
+			/* Is the log level valid? */
+			for (entry = server_message_level_options; entry && entry->name; entry++)
+			{
+				if (pg_strcasecmp(entry->name, loglevel) == 0)
+				{
+					found = true;
+					break;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel);
+				guc_free(btype);
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			/*
+			 * Is the backend type name valid? There might be multiple entries
+			 * per backend type, don't bail out because it can assign the
+			 * value for multiple entries.
+			 */
+			found = false;
+			for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+			{
+				if (pg_strcasecmp(log_min_messages_backend_types[i], btype) == 0)
+				{
+					/* Reject duplicates for a backend type. */
+					if (assigned[i])
+					{
+						GUC_check_errdetail("Backend type \"%s\" was already assigned.", btype);
+						guc_free(btype);
+						guc_free(rawstring);
+						list_free(elemlist);
+						return false;
+					}
+
+					newlevel[i] = entry->val;
+					assigned[i] = true;
+					found = true;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized backend type: \"%s\".", btype);
+				guc_free(btype);
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			guc_free(btype);
+		}
+	}
+
+	/*
+	 * The generic log level must be specified. It is the fallback value.
+	 */
+	if (genericlevel == -1)
+	{
+		GUC_check_errdetail("Generic log level was not defined.");
+		guc_free(rawstring);
+		list_free(elemlist);
+		return false;
+	}
+
+	/*
+	 * Apply the generic log level after all of the specific backend type have
+	 * been assigned. Hence, it doesn't matter the order you specify the
+	 * generic log level, the final result will be the same.
+	 */
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+	{
+		if (!assigned[i])
+			newlevel[i] = genericlevel;
+	}
+
+	guc_free(rawstring);
+	list_free(elemlist);
+
+	/*
+	 * Pass back data for assign_log_min_messages to use.
+	 */
+	*extra = guc_malloc(LOG, BACKEND_NUM_TYPES * sizeof(int));
+	if (!*extra)
+		return false;
+	memcpy(*extra, newlevel, BACKEND_NUM_TYPES * sizeof(int));
+
+	return true;
+}
+
+/*
+ * GUC assign_hook for log_min_messages
+ */
+void
+assign_log_min_messages(const char *newval, void *extra)
+{
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+		log_min_messages[i] = ((int *) extra)[i];
+}
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 976638a58ac..c2a044321d1 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -179,7 +179,7 @@ typedef struct
 } child_process_kind;
 
 static child_process_kind child_process_kinds[] = {
-#define PG_PROCTYPE(bktype, description, main_func, shmem_attach) \
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
 	[bktype] = {description, main_func, shmem_attach},
 #include "postmaster/proctypelist.h"
 #undef PG_PROCTYPE
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index b7b9692f8c8..616dd0b63d7 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -235,7 +235,7 @@ is_log_level_output(int elevel, int log_min_level)
 static inline bool
 should_output_to_server(int elevel)
 {
-	return is_log_level_output(elevel, log_min_messages);
+	return is_log_level_output(elevel, log_min_messages[MyBackendType]);
 }
 
 /*
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index fec79992c8d..2c3926cbadf 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType)
 
 	switch (backendType)
 	{
-#define PG_PROCTYPE(bktype, description, main_func, shmem_attach)	\
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages)	\
 		case bktype: backendDesc = description; break;
 #include "postmaster/proctypelist.h"
 #undef PG_PROCTYPE
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index 6bc6be13d2a..e7eb8ab2baf 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -2683,6 +2683,16 @@
   boot_val => '"%m [%p] "',
 },
 
+{ name => 'log_min_messages', type => 'string', context => 'PGC_SUSET', group => 'LOGGING_WHEN',
+  short_desc => 'Sets the message levels that are logged.',
+  long_desc => 'Each level includes all the levels that follow it. The later the level, the fewer messages are sent.',
+  flags => 'GUC_LIST_INPUT',
+  variable => 'log_min_messages_string',
+  boot_val => '"WARNING"',
+  check_hook => 'check_log_min_messages',
+  assign_hook => 'assign_log_min_messages',
+},
+
 { name => 'log_timezone', type => 'string', context => 'PGC_SIGHUP', group => 'LOGGING_WHAT',
   short_desc => 'Sets the time zone to use in log messages.',
   variable => 'log_timezone_string',
@@ -3252,14 +3262,6 @@
   options => 'log_error_verbosity_options',
 },
 
-{ name => 'log_min_messages', type => 'enum', context => 'PGC_SUSET', group => 'LOGGING_WHEN',
-  short_desc => 'Sets the message levels that are logged.',
-  long_desc => 'Each level includes all the levels that follow it. The later the level, the fewer messages are sent.',
-  variable => 'log_min_messages',
-  boot_val => 'WARNING',
-  options => 'server_message_level_options',
-},
-
 { name => 'log_min_error_statement', type => 'enum', context => 'PGC_SUSET', group => 'LOGGING_WHEN',
   short_desc => 'Causes all statements generating error at or above this level to be logged.',
   long_desc => 'Each level includes all the levels that follow it. The later the level, the fewer messages are sent.',
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 00c8376cf4d..52b3a902e61 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -146,7 +146,7 @@ static const struct config_enum_entry client_message_level_options[] = {
 	{NULL, 0, false}
 };
 
-static const struct config_enum_entry server_message_level_options[] = {
+const struct config_enum_entry server_message_level_options[] = {
 	{"debug5", DEBUG5, false},
 	{"debug4", DEBUG4, false},
 	{"debug3", DEBUG3, false},
@@ -537,7 +537,6 @@ static bool default_with_oids = false;
 bool		current_role_is_superuser;
 
 int			log_min_error_statement = ERROR;
-int			log_min_messages = WARNING;
 int			client_min_messages = NOTICE;
 int			log_min_duration_sample = -1;
 int			log_min_duration_statement = -1;
@@ -595,6 +594,7 @@ static char *server_version_string;
 static int	server_version_num;
 static char *debug_io_direct_string;
 static char *restrict_nonsystem_relation_kind_string;
+static char *log_min_messages_string;
 
 #ifdef HAVE_SYSLOG
 #define	DEFAULT_SYSLOG_FACILITY LOG_LOCAL0
@@ -639,6 +639,27 @@ char	   *role_string;
 /* should be static, but guc.c needs to get at this */
 bool		in_hot_standby_guc;
 
+/*
+ * It should be static, but commands/variable.c needs to get at this.
+ */
+int			log_min_messages[] = {
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
+	[bktype] = log_min_messages,
+#include "postmaster/proctypelist.h"
+#undef PG_PROCTYPE
+};
+
+/*
+ * It might be in commands/variable.c but for convenience it is near
+ * log_min_messages.
+ */
+const char *const log_min_messages_backend_types[] = {
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
+	[bktype] = bkcategory,
+#include "postmaster/proctypelist.h"
+#undef PG_PROCTYPE
+};
+
 
 /*
  * Displayable names for context types (enum GucContext)
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index c36fcb9ab61..53ee39098b9 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -539,6 +539,8 @@
 					#   log
 					#   fatal
 					#   panic
+					#   and an optional comma-separated list
+					#   of backendtype:level
 
 #log_min_error_statement = error	# values in order of decreasing detail:
 					#   debug5
diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h
index 242862451d8..f18e3342285 100644
--- a/src/include/postmaster/proctypelist.h
+++ b/src/include/postmaster/proctypelist.h
@@ -30,22 +30,22 @@
  */
 
 
-/* bktype, description, main_func, shmem_attach */
-PG_PROCTYPE(B_ARCHIVER, gettext_noop("archiver"), PgArchiverMain, true)
-PG_PROCTYPE(B_AUTOVAC_LAUNCHER, gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true)
-PG_PROCTYPE(B_AUTOVAC_WORKER, gettext_noop("autovacuum worker"), AutoVacWorkerMain, true)
-PG_PROCTYPE(B_BACKEND, gettext_noop("client backend"), BackendMain, true)
-PG_PROCTYPE(B_BG_WORKER, gettext_noop("background worker"), BackgroundWorkerMain, true)
-PG_PROCTYPE(B_BG_WRITER, gettext_noop("background writer"), BackgroundWriterMain, true)
-PG_PROCTYPE(B_CHECKPOINTER, gettext_noop("checkpointer"), CheckpointerMain, true)
-PG_PROCTYPE(B_DEAD_END_BACKEND, gettext_noop("dead-end client backend"), BackendMain, true)
-PG_PROCTYPE(B_INVALID, gettext_noop("unrecognized"), NULL, false)
-PG_PROCTYPE(B_IO_WORKER, gettext_noop("io worker"), IoWorkerMain, true)
-PG_PROCTYPE(B_LOGGER, gettext_noop("syslogger"), SysLoggerMain, false)
-PG_PROCTYPE(B_SLOTSYNC_WORKER, gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true)
-PG_PROCTYPE(B_STANDALONE_BACKEND, gettext_noop("standalone backend"), NULL, false)
-PG_PROCTYPE(B_STARTUP, gettext_noop("startup"), StartupProcessMain, true)
-PG_PROCTYPE(B_WAL_RECEIVER, gettext_noop("walreceiver"), WalReceiverMain, true)
-PG_PROCTYPE(B_WAL_SENDER, gettext_noop("walsender"), NULL, true)
-PG_PROCTYPE(B_WAL_SUMMARIZER, gettext_noop("walsummarizer"), WalSummarizerMain, true)
-PG_PROCTYPE(B_WAL_WRITER, gettext_noop("walwriter"), WalWriterMain, true)
+/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */
+PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING)
+PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING)
+PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING)
+PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING)
+PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING)
+PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING)
+PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING)
+PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING)
+PG_PROCTYPE(B_INVALID, "backend", gettext_noop("unrecognized"), NULL, false, WARNING)
+PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING)
+PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING)
+PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING)
+PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING)
+PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING)
+PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING)
+PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING)
+PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING)
+PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING)
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index f21ec37da89..d68dd350936 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -295,7 +295,7 @@ extern PGDLLIMPORT bool log_duration;
 extern PGDLLIMPORT int log_parameter_max_length;
 extern PGDLLIMPORT int log_parameter_max_length_on_error;
 extern PGDLLIMPORT int log_min_error_statement;
-extern PGDLLIMPORT int log_min_messages;
+extern PGDLLIMPORT int log_min_messages[];
 extern PGDLLIMPORT int client_min_messages;
 extern PGDLLIMPORT int log_min_duration_sample;
 extern PGDLLIMPORT int log_min_duration_statement;
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index 82ac8646a8d..e6ffc96468f 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -174,5 +174,7 @@ extern void assign_wal_sync_method(int new_wal_sync_method, void *extra);
 extern bool check_synchronized_standby_slots(char **newval, void **extra,
 											 GucSource source);
 extern void assign_synchronized_standby_slots(const char *newval, void *extra);
+extern bool check_log_min_messages(char **newval, void **extra, GucSource source);
+extern void assign_log_min_messages(const char *newval, void *extra);
 
 #endif							/* GUC_HOOKS_H */
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index f72ce944d7f..2aefc653f20 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -298,6 +298,10 @@ struct config_enum
 	void	   *reset_extra;
 };
 
+/* log_min_messages */
+extern PGDLLIMPORT const char *const log_min_messages_backend_types[];
+extern PGDLLIMPORT const struct config_enum_entry server_message_level_options[];
+
 /* constant tables corresponding to enums above and in guc.h */
 extern PGDLLIMPORT const char *const config_group_names[];
 extern PGDLLIMPORT const char *const config_type_names[];
diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out
index 7f9e29c765c..be25e81140a 100644
--- a/src/test/regress/expected/guc.out
+++ b/src/test/regress/expected/guc.out
@@ -913,3 +913,57 @@ SELECT name FROM tab_settings_flags
 (0 rows)
 
 DROP TABLE tab_settings_flags;
+-- Test log_min_messages
+SET log_min_messages TO foo;  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "foo"
+DETAIL:  Unrecognized log level: "foo".
+SET log_min_messages TO fatal;
+SHOW log_min_messages;
+ log_min_messages 
+------------------
+ fatal
+(1 row)
+
+SET log_min_messages TO 'fatal';
+SHOW log_min_messages;
+ log_min_messages 
+------------------
+ fatal
+(1 row)
+
+SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1';  --fail
+ERROR:  invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1"
+DETAIL:  Generic log level was not defined.
+SET log_min_messages TO 'debug1, backend:error, fatal';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "debug1, backend:error, fatal"
+DETAIL:  Generic log level was already assigned.
+SET log_min_messages TO 'backend:error, debug1, backend:warning';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning"
+DETAIL:  Backend type "backend" was already assigned.
+SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1"
+DETAIL:  Unrecognized backend type: "foo".
+SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1"
+DETAIL:  Unrecognized log level: "bar".
+SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3';
+SHOW log_min_messages;
+                                        log_min_messages                                         
+-------------------------------------------------------------------------------------------------
+ backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3
+(1 row)
+
+SET log_min_messages TO 'warning, autovacuum:debug1';
+SHOW log_min_messages;
+      log_min_messages      
+----------------------------
+ warning, autovacuum:debug1
+(1 row)
+
+SET log_min_messages TO 'autovacuum:debug1, warning';
+SHOW log_min_messages;
+      log_min_messages      
+----------------------------
+ autovacuum:debug1, warning
+(1 row)
+
diff --git a/src/test/regress/sql/guc.sql b/src/test/regress/sql/guc.sql
index f65f84a2632..f44c72c532e 100644
--- a/src/test/regress/sql/guc.sql
+++ b/src/test/regress/sql/guc.sql
@@ -368,3 +368,21 @@ SELECT name FROM tab_settings_flags
   WHERE no_reset AND NOT no_reset_all
   ORDER BY 1;
 DROP TABLE tab_settings_flags;
+
+-- Test log_min_messages
+SET log_min_messages TO foo;  -- fail
+SET log_min_messages TO fatal;
+SHOW log_min_messages;
+SET log_min_messages TO 'fatal';
+SHOW log_min_messages;
+SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1';  --fail
+SET log_min_messages TO 'debug1, backend:error, fatal';  -- fail
+SET log_min_messages TO 'backend:error, debug1, backend:warning';  -- fail
+SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1';  -- fail
+SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1';  -- fail
+SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3';
+SHOW log_min_messages;
+SET log_min_messages TO 'warning, autovacuum:debug1';
+SHOW log_min_messages;
+SET log_min_messages TO 'autovacuum:debug1, warning';
+SHOW log_min_messages;
-- 
2.39.5



^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-11-06 13:09  Euler Taveira <euler@eulerto.com>
  parent: Euler Taveira <euler@eulerto.com>
  0 siblings, 2 replies; 58+ messages in thread

From: Euler Taveira @ 2025-11-06 13:09 UTC (permalink / raw)
  To: japin <japinli@hotmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org

On Sun, Oct 5, 2025, at 11:18 AM, Euler Taveira wrote:
> This new patch contains the following changes:
>
> - patch was rebased
> - use commit dbf8cfb4f02
> - use some GUC memory allocation functions
> - avoid one memory allocation (suggested by Japin Li)
> - rename backend type: logger -> syslogger
> - adjust tests to increase test coverage
> - improve documentation and comments to reflect the current state
>

Patch was rebased since commit fce7c73fba4 broke it. No modifications.


-- 
Euler Taveira
EDB   https://www.enterprisedb.com/

Attachments:

  [text/x-patch] v5-0001-log_min_messages-per-backend-type.patch (25.4K, ../../72e809ff-c842-4f98-93f1-26a3cb3d92d0@app.fastmail.com/2-v5-0001-log_min_messages-per-backend-type.patch)
  download | inline diff:
From e457cc99c20f54d8e7e998104402e849ecf83c14 Mon Sep 17 00:00:00 2001
From: Euler Taveira <euler@eulerto.com>
Date: Tue, 4 Nov 2025 17:18:20 -0300
Subject: [PATCH v5] log_min_messages per backend type

Change log_min_messages from a single element to a comma-separated list
of elements. Each element is backendtype:loglevel. The backendtype are
archiver, autovacuum (includes launcher and workers), backend, bgworker,
bgwriter, checkpointer, ioworker, syslogger, slotsyncworker, startup,
walreceiver, walsender, walsummarizer and walwriter. A single log level
should be part of this list and it is applied as a final step to the
backend types that were not informed.

The old syntax (a single log level) is still accepted for backward
compatibility. In this case, it means the informed log level is applied
for all backend types. The default log level is the same (WARNING) for
all backend types.
---
 doc/src/sgml/config.sgml                      |  31 ++-
 src/backend/commands/extension.c              |   2 +-
 src/backend/commands/variable.c               | 205 ++++++++++++++++++
 src/backend/postmaster/launch_backend.c       |   2 +-
 src/backend/utils/error/elog.c                |   2 +-
 src/backend/utils/init/miscinit.c             |   2 +-
 src/backend/utils/misc/guc_parameters.dat     |  10 +-
 src/backend/utils/misc/guc_tables.c           |  21 +-
 src/backend/utils/misc/postgresql.conf.sample |   2 +
 src/include/postmaster/proctypelist.h         |  38 ++--
 src/include/utils/guc.h                       |   6 +-
 src/include/utils/guc_hooks.h                 |   2 +
 src/test/regress/expected/guc.out             |  54 +++++
 src/test/regress/sql/guc.sql                  |  18 ++
 14 files changed, 356 insertions(+), 39 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index d8a9f14b618..2ac1e65aa60 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -7053,7 +7053,7 @@ local0.*    /var/log/postgresql
      <variablelist>
 
      <varlistentry id="guc-log-min-messages" xreflabel="log_min_messages">
-      <term><varname>log_min_messages</varname> (<type>enum</type>)
+      <term><varname>log_min_messages</varname> (<type>string</type>)
       <indexterm>
        <primary><varname>log_min_messages</varname> configuration parameter</primary>
       </indexterm>
@@ -7062,14 +7062,27 @@ local0.*    /var/log/postgresql
        <para>
         Controls which <link linkend="runtime-config-severity-levels">message
         levels</link> are written to the server log.
-        Valid values are <literal>DEBUG5</literal>, <literal>DEBUG4</literal>,
-        <literal>DEBUG3</literal>, <literal>DEBUG2</literal>, <literal>DEBUG1</literal>,
-        <literal>INFO</literal>, <literal>NOTICE</literal>, <literal>WARNING</literal>,
-        <literal>ERROR</literal>, <literal>LOG</literal>, <literal>FATAL</literal>, and
-        <literal>PANIC</literal>.  Each level includes all the levels that
-        follow it.  The later the level, the fewer messages are sent
-        to the log.  The default is <literal>WARNING</literal>.  Note that
-        <literal>LOG</literal> has a different rank here than in
+        Valid values are a comma-separated list of <literal>backendtype:level</literal>
+        and a single <literal>level</literal>. The list allows it to use
+        different levels per backend type. Only the single <literal>level</literal>
+        is mandatory (order does not matter) and it is assigned to the backend
+        types that are not specified in the list.
+        Valid <literal>backendtype</literal> values are <literal>archiver</literal>,
+        <literal>autovacuum</literal>, <literal>backend</literal>,
+        <literal>bgworker</literal>, <literal>bgwriter</literal>,
+        <literal>checkpointer</literal>, <literal>ioworker</literal>,
+        <literal>syslogger</literal>, <literal>slotsyncworker</literal>,
+        <literal>startup</literal>, <literal>walreceiver</literal>,
+        <literal>walsender</literal>, <literal>walsummarizer</literal>, and
+        <literal>walwriter</literal>.
+        Valid <literal>level</literal> values are <literal>DEBUG5</literal>,
+        <literal>DEBUG4</literal>, <literal>DEBUG3</literal>, <literal>DEBUG2</literal>,
+        <literal>DEBUG1</literal>, <literal>INFO</literal>, <literal>NOTICE</literal>,
+        <literal>WARNING</literal>, <literal>ERROR</literal>, <literal>LOG</literal>,
+        <literal>FATAL</literal>, and <literal>PANIC</literal>.  Each level includes
+        all the levels that follow it.  The later the level, the fewer messages are sent
+        to the log.  The default is <literal>WARNING</literal> for all backend types.
+        Note that <literal>LOG</literal> has a different rank here than in
         <xref linkend="guc-client-min-messages"/>.
         Only superusers and users with the appropriate <literal>SET</literal>
         privilege can change this setting.
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 93ef1ad106f..81f7bc5e567 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1143,7 +1143,7 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
 		(void) set_config_option("client_min_messages", "warning",
 								 PGC_USERSET, PGC_S_SESSION,
 								 GUC_ACTION_SAVE, true, 0, false);
-	if (log_min_messages < WARNING)
+	if (log_min_messages[MyBackendType] < WARNING)
 		(void) set_config_option_ext("log_min_messages", "warning",
 									 PGC_SUSET, PGC_S_SESSION,
 									 BOOTSTRAP_SUPERUSERID,
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index 608f10d9412..c86bdf74f41 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -34,6 +34,7 @@
 #include "utils/backend_status.h"
 #include "utils/datetime.h"
 #include "utils/fmgrprotos.h"
+#include "utils/guc.h"
 #include "utils/guc_hooks.h"
 #include "utils/snapmgr.h"
 #include "utils/syscache.h"
@@ -1257,3 +1258,207 @@ check_ssl(bool *newval, void **extra, GucSource source)
 #endif
 	return true;
 }
+
+/*
+ * GUC check_hook for log_min_messages
+ *
+ * The parsing consists of a comma-separated list of BACKENDTYPENAME:LEVEL
+ * elements. BACKENDTYPENAME is log_min_messages_backend_types.  LEVEL is
+ * server_message_level_options. A single LEVEL element should be part of this
+ * list and it is applied as a final step to the backend types that are not
+ * specified. For backward compatibility, the old syntax is still accepted and
+ * it means to apply the LEVEL for all backend types.
+ */
+bool
+check_log_min_messages(char **newval, void **extra, GucSource source)
+{
+	char	   *rawstring;
+	List	   *elemlist;
+	ListCell   *l;
+	int			newlevel[BACKEND_NUM_TYPES];
+	bool		assigned[BACKEND_NUM_TYPES];
+	int			genericlevel = -1;	/* -1 means not assigned */
+
+	/* Initialize the array. */
+	memset(newlevel, WARNING, BACKEND_NUM_TYPES * sizeof(int));
+	memset(assigned, false, BACKEND_NUM_TYPES * sizeof(bool));
+
+	/* Need a modifiable copy of string. */
+	rawstring = guc_strdup(LOG, *newval);
+
+	/* Parse string into list of identifiers. */
+	if (!SplitGUCList(rawstring, ',', &elemlist))
+	{
+		/* syntax error in list */
+		GUC_check_errdetail("List syntax is invalid.");
+		guc_free(rawstring);
+		list_free(elemlist);
+		return false;
+	}
+
+	/* Validate and assign log level and backend type. */
+	foreach(l, elemlist)
+	{
+		char	   *tok = (char *) lfirst(l);
+		char	   *sep;
+		const struct config_enum_entry *entry;
+
+		/*
+		 * Check whether there is a backend type following the log level. If
+		 * there is no separator, it means this is the generic log level. The
+		 * generic log level will be assigned to the backend types that were
+		 * not informed.
+		 */
+		sep = strchr(tok, ':');
+		if (sep == NULL)
+		{
+			bool		found = false;
+
+			/* Reject duplicates for generic log level. */
+			if (genericlevel != -1)
+			{
+				GUC_check_errdetail("Generic log level was already assigned.");
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			/* Is the log level valid? */
+			for (entry = server_message_level_options; entry && entry->name; entry++)
+			{
+				if (pg_strcasecmp(entry->name, tok) == 0)
+				{
+					genericlevel = entry->val;
+					found = true;
+					break;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized log level: \"%s\".", tok);
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+		}
+		else
+		{
+			char	   *loglevel;
+			char	   *btype;
+			bool		found = false;
+
+			btype = guc_malloc(LOG, (sep - tok) + 1);
+			if (!btype)
+			{
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+			memcpy(btype, tok, sep - tok);
+			btype[sep - tok] = '\0';
+			loglevel = sep + 1;
+
+			/* Is the log level valid? */
+			for (entry = server_message_level_options; entry && entry->name; entry++)
+			{
+				if (pg_strcasecmp(entry->name, loglevel) == 0)
+				{
+					found = true;
+					break;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel);
+				guc_free(btype);
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			/*
+			 * Is the backend type name valid? There might be multiple entries
+			 * per backend type, don't bail out because it can assign the
+			 * value for multiple entries.
+			 */
+			found = false;
+			for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+			{
+				if (pg_strcasecmp(log_min_messages_backend_types[i], btype) == 0)
+				{
+					/* Reject duplicates for a backend type. */
+					if (assigned[i])
+					{
+						GUC_check_errdetail("Backend type \"%s\" was already assigned.", btype);
+						guc_free(btype);
+						guc_free(rawstring);
+						list_free(elemlist);
+						return false;
+					}
+
+					newlevel[i] = entry->val;
+					assigned[i] = true;
+					found = true;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized backend type: \"%s\".", btype);
+				guc_free(btype);
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			guc_free(btype);
+		}
+	}
+
+	/*
+	 * The generic log level must be specified. It is the fallback value.
+	 */
+	if (genericlevel == -1)
+	{
+		GUC_check_errdetail("Generic log level was not defined.");
+		guc_free(rawstring);
+		list_free(elemlist);
+		return false;
+	}
+
+	/*
+	 * Apply the generic log level after all of the specific backend type have
+	 * been assigned. Hence, it doesn't matter the order you specify the
+	 * generic log level, the final result will be the same.
+	 */
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+	{
+		if (!assigned[i])
+			newlevel[i] = genericlevel;
+	}
+
+	guc_free(rawstring);
+	list_free(elemlist);
+
+	/*
+	 * Pass back data for assign_log_min_messages to use.
+	 */
+	*extra = guc_malloc(LOG, BACKEND_NUM_TYPES * sizeof(int));
+	if (!*extra)
+		return false;
+	memcpy(*extra, newlevel, BACKEND_NUM_TYPES * sizeof(int));
+
+	return true;
+}
+
+/*
+ * GUC assign_hook for log_min_messages
+ */
+void
+assign_log_min_messages(const char *newval, void *extra)
+{
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+		log_min_messages[i] = ((int *) extra)[i];
+}
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 976638a58ac..c2a044321d1 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -179,7 +179,7 @@ typedef struct
 } child_process_kind;
 
 static child_process_kind child_process_kinds[] = {
-#define PG_PROCTYPE(bktype, description, main_func, shmem_attach) \
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
 	[bktype] = {description, main_func, shmem_attach},
 #include "postmaster/proctypelist.h"
 #undef PG_PROCTYPE
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 29643c51439..15d771a16c9 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -235,7 +235,7 @@ is_log_level_output(int elevel, int log_min_level)
 static inline bool
 should_output_to_server(int elevel)
 {
-	return is_log_level_output(elevel, log_min_messages);
+	return is_log_level_output(elevel, log_min_messages[MyBackendType]);
 }
 
 /*
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index fec79992c8d..2c3926cbadf 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType)
 
 	switch (backendType)
 	{
-#define PG_PROCTYPE(bktype, description, main_func, shmem_attach)	\
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages)	\
 		case bktype: backendDesc = description; break;
 #include "postmaster/proctypelist.h"
 #undef PG_PROCTYPE
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index 1128167c025..55dfc2e6548 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -1676,12 +1676,14 @@
   options => 'server_message_level_options',
 },
 
-{ name => 'log_min_messages', type => 'enum', context => 'PGC_SUSET', group => 'LOGGING_WHEN',
+{ name => 'log_min_messages', type => 'string', context => 'PGC_SUSET', group => 'LOGGING_WHEN',
   short_desc => 'Sets the message levels that are logged.',
   long_desc => 'Each level includes all the levels that follow it. The later the level, the fewer messages are sent.',
-  variable => 'log_min_messages',
-  boot_val => 'WARNING',
-  options => 'server_message_level_options',
+  flags => 'GUC_LIST_INPUT',
+  variable => 'log_min_messages_string',
+  boot_val => '"WARNING"',
+  check_hook => 'check_log_min_messages',
+  assign_hook => 'assign_log_min_messages',
 },
 
 { name => 'log_parameter_max_length', type => 'int', context => 'PGC_SUSET', group => 'LOGGING_WHAT',
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 0209b2067a2..8761b371de7 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -146,7 +146,7 @@ static const struct config_enum_entry client_message_level_options[] = {
 	{NULL, 0, false}
 };
 
-static const struct config_enum_entry server_message_level_options[] = {
+const struct config_enum_entry server_message_level_options[] = {
 	{"debug5", DEBUG5, false},
 	{"debug4", DEBUG4, false},
 	{"debug3", DEBUG3, false},
@@ -537,7 +537,6 @@ static bool default_with_oids = false;
 bool		current_role_is_superuser;
 
 int			log_min_error_statement = ERROR;
-int			log_min_messages = WARNING;
 int			client_min_messages = NOTICE;
 int			log_min_duration_sample = -1;
 int			log_min_duration_statement = -1;
@@ -595,6 +594,7 @@ static char *server_version_string;
 static int	server_version_num;
 static char *debug_io_direct_string;
 static char *restrict_nonsystem_relation_kind_string;
+static char *log_min_messages_string;
 
 #ifdef HAVE_SYSLOG
 #define	DEFAULT_SYSLOG_FACILITY LOG_LOCAL0
@@ -639,6 +639,23 @@ char	   *role_string;
 /* should be static, but guc.c needs to get at this */
 bool		in_hot_standby_guc;
 
+/*
+ * log_min_messages
+ */
+int			log_min_messages[] = {
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
+	[bktype] = log_min_messages,
+#include "postmaster/proctypelist.h"
+#undef PG_PROCTYPE
+};
+
+const char *const log_min_messages_backend_types[] = {
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
+	[bktype] = bkcategory,
+#include "postmaster/proctypelist.h"
+#undef PG_PROCTYPE
+};
+
 
 /*
  * Displayable names for context types (enum GucContext)
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index f62b61967ef..fde1054ae0f 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -541,6 +541,8 @@
 					#   log
 					#   fatal
 					#   panic
+					#   and an optional comma-separated list
+					#   of backendtype:level
 
 #log_min_error_statement = error	# values in order of decreasing detail:
 					#   debug5
diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h
index 242862451d8..f18e3342285 100644
--- a/src/include/postmaster/proctypelist.h
+++ b/src/include/postmaster/proctypelist.h
@@ -30,22 +30,22 @@
  */
 
 
-/* bktype, description, main_func, shmem_attach */
-PG_PROCTYPE(B_ARCHIVER, gettext_noop("archiver"), PgArchiverMain, true)
-PG_PROCTYPE(B_AUTOVAC_LAUNCHER, gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true)
-PG_PROCTYPE(B_AUTOVAC_WORKER, gettext_noop("autovacuum worker"), AutoVacWorkerMain, true)
-PG_PROCTYPE(B_BACKEND, gettext_noop("client backend"), BackendMain, true)
-PG_PROCTYPE(B_BG_WORKER, gettext_noop("background worker"), BackgroundWorkerMain, true)
-PG_PROCTYPE(B_BG_WRITER, gettext_noop("background writer"), BackgroundWriterMain, true)
-PG_PROCTYPE(B_CHECKPOINTER, gettext_noop("checkpointer"), CheckpointerMain, true)
-PG_PROCTYPE(B_DEAD_END_BACKEND, gettext_noop("dead-end client backend"), BackendMain, true)
-PG_PROCTYPE(B_INVALID, gettext_noop("unrecognized"), NULL, false)
-PG_PROCTYPE(B_IO_WORKER, gettext_noop("io worker"), IoWorkerMain, true)
-PG_PROCTYPE(B_LOGGER, gettext_noop("syslogger"), SysLoggerMain, false)
-PG_PROCTYPE(B_SLOTSYNC_WORKER, gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true)
-PG_PROCTYPE(B_STANDALONE_BACKEND, gettext_noop("standalone backend"), NULL, false)
-PG_PROCTYPE(B_STARTUP, gettext_noop("startup"), StartupProcessMain, true)
-PG_PROCTYPE(B_WAL_RECEIVER, gettext_noop("walreceiver"), WalReceiverMain, true)
-PG_PROCTYPE(B_WAL_SENDER, gettext_noop("walsender"), NULL, true)
-PG_PROCTYPE(B_WAL_SUMMARIZER, gettext_noop("walsummarizer"), WalSummarizerMain, true)
-PG_PROCTYPE(B_WAL_WRITER, gettext_noop("walwriter"), WalWriterMain, true)
+/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */
+PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING)
+PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING)
+PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING)
+PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING)
+PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING)
+PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING)
+PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING)
+PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING)
+PG_PROCTYPE(B_INVALID, "backend", gettext_noop("unrecognized"), NULL, false, WARNING)
+PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING)
+PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING)
+PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING)
+PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING)
+PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING)
+PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING)
+PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING)
+PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING)
+PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING)
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index f21ec37da89..a4e52433ac0 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -295,7 +295,7 @@ extern PGDLLIMPORT bool log_duration;
 extern PGDLLIMPORT int log_parameter_max_length;
 extern PGDLLIMPORT int log_parameter_max_length_on_error;
 extern PGDLLIMPORT int log_min_error_statement;
-extern PGDLLIMPORT int log_min_messages;
+extern PGDLLIMPORT int log_min_messages[];
 extern PGDLLIMPORT int client_min_messages;
 extern PGDLLIMPORT int log_min_duration_sample;
 extern PGDLLIMPORT int log_min_duration_statement;
@@ -329,6 +329,9 @@ extern PGDLLIMPORT bool trace_sort;
 extern PGDLLIMPORT bool optimize_bounded_sort;
 #endif
 
+/* log_min_messages */
+extern PGDLLIMPORT const char *const log_min_messages_backend_types[];
+
 /*
  * Declarations for options for enum values
  *
@@ -344,6 +347,7 @@ extern PGDLLIMPORT const struct config_enum_entry archive_mode_options[];
 extern PGDLLIMPORT const struct config_enum_entry dynamic_shared_memory_options[];
 extern PGDLLIMPORT const struct config_enum_entry io_method_options[];
 extern PGDLLIMPORT const struct config_enum_entry recovery_target_action_options[];
+extern PGDLLIMPORT const struct config_enum_entry server_message_level_options[];
 extern PGDLLIMPORT const struct config_enum_entry wal_level_options[];
 extern PGDLLIMPORT const struct config_enum_entry wal_sync_method_options[];
 
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index 82ac8646a8d..e6ffc96468f 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -174,5 +174,7 @@ extern void assign_wal_sync_method(int new_wal_sync_method, void *extra);
 extern bool check_synchronized_standby_slots(char **newval, void **extra,
 											 GucSource source);
 extern void assign_synchronized_standby_slots(const char *newval, void *extra);
+extern bool check_log_min_messages(char **newval, void **extra, GucSource source);
+extern void assign_log_min_messages(const char *newval, void *extra);
 
 #endif							/* GUC_HOOKS_H */
diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out
index d6fb879f500..f6ebd27b1a9 100644
--- a/src/test/regress/expected/guc.out
+++ b/src/test/regress/expected/guc.out
@@ -935,3 +935,57 @@ SELECT name FROM tab_settings_flags
 (0 rows)
 
 DROP TABLE tab_settings_flags;
+-- Test log_min_messages
+SET log_min_messages TO foo;  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "foo"
+DETAIL:  Unrecognized log level: "foo".
+SET log_min_messages TO fatal;
+SHOW log_min_messages;
+ log_min_messages 
+------------------
+ fatal
+(1 row)
+
+SET log_min_messages TO 'fatal';
+SHOW log_min_messages;
+ log_min_messages 
+------------------
+ fatal
+(1 row)
+
+SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1';  --fail
+ERROR:  invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1"
+DETAIL:  Generic log level was not defined.
+SET log_min_messages TO 'debug1, backend:error, fatal';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "debug1, backend:error, fatal"
+DETAIL:  Generic log level was already assigned.
+SET log_min_messages TO 'backend:error, debug1, backend:warning';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning"
+DETAIL:  Backend type "backend" was already assigned.
+SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1"
+DETAIL:  Unrecognized backend type: "foo".
+SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1"
+DETAIL:  Unrecognized log level: "bar".
+SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3';
+SHOW log_min_messages;
+                                        log_min_messages                                         
+-------------------------------------------------------------------------------------------------
+ backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3
+(1 row)
+
+SET log_min_messages TO 'warning, autovacuum:debug1';
+SHOW log_min_messages;
+      log_min_messages      
+----------------------------
+ warning, autovacuum:debug1
+(1 row)
+
+SET log_min_messages TO 'autovacuum:debug1, warning';
+SHOW log_min_messages;
+      log_min_messages      
+----------------------------
+ autovacuum:debug1, warning
+(1 row)
+
diff --git a/src/test/regress/sql/guc.sql b/src/test/regress/sql/guc.sql
index bafaf067e82..f7dfc909b42 100644
--- a/src/test/regress/sql/guc.sql
+++ b/src/test/regress/sql/guc.sql
@@ -377,3 +377,21 @@ SELECT name FROM tab_settings_flags
   WHERE no_reset AND NOT no_reset_all
   ORDER BY 1;
 DROP TABLE tab_settings_flags;
+
+-- Test log_min_messages
+SET log_min_messages TO foo;  -- fail
+SET log_min_messages TO fatal;
+SHOW log_min_messages;
+SET log_min_messages TO 'fatal';
+SHOW log_min_messages;
+SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1';  --fail
+SET log_min_messages TO 'debug1, backend:error, fatal';  -- fail
+SET log_min_messages TO 'backend:error, debug1, backend:warning';  -- fail
+SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1';  -- fail
+SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1';  -- fail
+SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3';
+SHOW log_min_messages;
+SET log_min_messages TO 'warning, autovacuum:debug1';
+SHOW log_min_messages;
+SET log_min_messages TO 'autovacuum:debug1, warning';
+SHOW log_min_messages;
-- 
2.39.5



^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-11-06 14:53  Chao Li <li.evan.chao@gmail.com>
  parent: Euler Taveira <euler@eulerto.com>
  1 sibling, 1 reply; 58+ messages in thread

From: Chao Li @ 2025-11-06 14:53 UTC (permalink / raw)
  To: Euler Taveira <euler@eulerto.com>; +Cc: japin <japinli@hotmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org

Hi Euler,

I just reviewed the patch and got a few comments.

> On Nov 6, 2025, at 21:09, Euler Taveira <euler@eulerto.com> wrote:
> 
> On Sun, Oct 5, 2025, at 11:18 AM, Euler Taveira wrote:
>> This new patch contains the following changes:
>> 
>> - patch was rebased
>> - use commit dbf8cfb4f02
>> - use some GUC memory allocation functions
>> - avoid one memory allocation (suggested by Japin Li)
>> - rename backend type: logger -> syslogger
>> - adjust tests to increase test coverage
>> - improve documentation and comments to reflect the current state
>> 
> 
> Patch was rebased since commit fce7c73fba4 broke it. No modifications.
> 
> 
> -- 
> Euler Taveira
> EDB   https://www.enterprisedb.com/<v5-0001-log_min_messages-per-backend-type.patch;

1 - variable.c
```
+	/* Initialize the array. */
+	memset(newlevel, WARNING, BACKEND_NUM_TYPES * sizeof(int));
```

I think this statement is wrong, because memset() writes bytes but integers, so this statement will set very byte to WARNING, which should not be the intention. You will need to use a loop to initialize every element of newlevel.

2 - variable.c
```
+	/* Parse string into list of identifiers. */
+	if (!SplitGUCList(rawstring, ',', &elemlist))
+	{
+		/* syntax error in list */
+		GUC_check_errdetail("List syntax is invalid.");
+		guc_free(rawstring);
+		list_free(elemlist);
+		return false;
+	}
```

Every element of elemlist points to a position of rawstring, so it’s better to list_free(elemlist) first then guc_free(rawstring).

3 - launch_backend.c
```
 static inline bool
 should_output_to_server(int elevel)
 {
-	return is_log_level_output(elevel, log_min_messages);
+	return is_log_level_output(elevel, log_min_messages[MyBackendType]);
 }
```

Is it possible that when this function is called, MyBackendType has not been initialized? To be safe, maybe we can check if MyBackendType is 0 (B_INVALID), then use the generic log_min_message.

4 - config.sgml
```
+        Valid values are a comma-separated list of <literal>backendtype:level</literal>
+        and a single <literal>level</literal>. The list allows it to use
+        different levels per backend type. Only the single <literal>level</literal>
+        is mandatory (order does not matter) and it is assigned to the backend
+        types that are not specified in the list.
+        Valid <literal>backendtype</literal> values are <literal>archiver</literal>,
+        <literal>autovacuum</literal>, <literal>backend</literal>,
+        <literal>bgworker</literal>, <literal>bgwriter</literal>,
+        <literal>checkpointer</literal>, <literal>ioworker</literal>,
+        <literal>syslogger</literal>, <literal>slotsyncworker</literal>,
+        <literal>startup</literal>, <literal>walreceiver</literal>,
+        <literal>walsender</literal>, <literal>walsummarizer</literal>, and
+        <literal>walwriter</literal>.
+        Valid <literal>level</literal> values are <literal>DEBUG5</literal>,
+        <literal>DEBUG4</literal>, <literal>DEBUG3</literal>, <literal>DEBUG2</literal>,
+        <literal>DEBUG1</literal>, <literal>INFO</literal>, <literal>NOTICE</literal>,
+        <literal>WARNING</literal>, <literal>ERROR</literal>, <literal>LOG</literal>,
+        <literal>FATAL</literal>, and <literal>PANIC</literal>.  Each level includes
+        all the levels that follow it.  The later the level, the fewer messages are sent
+        to the log.  The default is <literal>WARNING</literal> for all backend types.
+        Note that <literal>LOG</literal> has a different rank here than in
```

* “Valid values are …”, here “are” usually mean both are needed, so maybe change “are” to “can be”.
* It says “the single level is mandatory”, then why there is still a default value?

Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/









^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-11-06 16:01  Alvaro Herrera <alvherre@alvh.no-ip.org>
  parent: Euler Taveira <euler@eulerto.com>
  1 sibling, 1 reply; 58+ messages in thread

From: Alvaro Herrera @ 2025-11-06 16:01 UTC (permalink / raw)
  To: Euler Taveira <euler@eulerto.com>; +Cc: japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org

On 2025-Nov-06, Euler Taveira wrote:

> +        Valid values are a comma-separated list of <literal>backendtype:level</literal>
> +        and a single <literal>level</literal>. The list allows it to use
> +        different levels per backend type. Only the single <literal>level</literal>
> +        is mandatory (order does not matter) and it is assigned to the backend
> +        types that are not specified in the list.
> +        Valid <literal>backendtype</literal> values are <literal>archiver</literal>,
> +        <literal>autovacuum</literal>, <literal>backend</literal>,
> +        <literal>bgworker</literal>, <literal>bgwriter</literal>,
> +        <literal>checkpointer</literal>, <literal>ioworker</literal>,
> +        <literal>syslogger</literal>, <literal>slotsyncworker</literal>,
> +        <literal>startup</literal>, <literal>walreceiver</literal>,
> +        <literal>walsender</literal>, <literal>walsummarizer</literal>, and
> +        <literal>walwriter</literal>.
> +        Valid <literal>level</literal> values are <literal>DEBUG5</literal>,
> +        <literal>DEBUG4</literal>, <literal>DEBUG3</literal>, <literal>DEBUG2</literal>,
> +        <literal>DEBUG1</literal>, <literal>INFO</literal>, <literal>NOTICE</literal>,
> +        <literal>WARNING</literal>, <literal>ERROR</literal>, <literal>LOG</literal>,
> +        <literal>FATAL</literal>, and <literal>PANIC</literal>.  Each level includes
> +        all the levels that follow it.  The later the level, the fewer messages are sent
> +        to the log.  The default is <literal>WARNING</literal> for all backend types.

I would use <literal>type:level</literal> rather than "backendtype".
Also, the glossary says we have "auxiliary processes" and "backends", so
from the user perspective, these types are not all backends, but instead
process types.

I think the list of backend types is pretty hard to read.  What do you
think about using 
<simplelist type="vert" columns="4">
to create a friendlier representation?  

So you would say something like "Valid types are listed in the table
below, each corresponding to either postmaster, an auxiliary process
type or a backend".  (Eh, wait, you seem not to have "postmaster" in
your list, what's up with that?)

(I'm not sure about making the log levels also a <simplelist>, because
for that list the order matters, and if we have more than one column
then the order is going to be ambiguous, and if we have just one column
it's going to be too tall.  But maybe it's not all that bad?)

-- 
Álvaro Herrera               48°01'N 7°57'E  —  https://www.EnterpriseDB.com/





^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-11-18 23:38  Euler Taveira <euler@eulerto.com>
  parent: Alvaro Herrera <alvherre@alvh.no-ip.org>
  0 siblings, 1 reply; 58+ messages in thread

From: Euler Taveira @ 2025-11-18 23:38 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org

On Thu, Nov 6, 2025, at 1:01 PM, Alvaro Herrera wrote:
>
> I would use <literal>type:level</literal> rather than "backendtype".
> Also, the glossary says we have "auxiliary processes" and "backends", so
> from the user perspective, these types are not all backends, but instead
> process types.
>

Hasn't that ship already sailed? If your suggestion is limited to "type:level",
that's ok. I wouldn't like to use 2 terminologies ("process type" and "backend
type") in the documentation.

See miscadmin.h.

/*
 * MyBackendType indicates what kind of a backend this is.
 *
 * If you add entries, please also update the child_process_kinds array in
 * launch_backend.c.
 */
typedef enum BackendType

The "backend type" terminology is exposed. It is even available in the
pg_stat_activity view. I agree that "backend" is not a good name to define a
Postgres process. I don't think we should be inconsistent only in this patch.
Even if the proposal is to rename enum BackendType (I won't propose it as part
of this patch), it would make some extension authors unhappy according to
codesearch.debian.net.

> I think the list of backend types is pretty hard to read.  What do you
> think about using 
> <simplelist type="vert" columns="4">
> to create a friendlier representation?  
>

I tried but didn't like the result. See the attached image. You said table but
it is a multi-column list; it doesn't contain a header or borders. Reading this
message and Chao Li message, I realized the description is not good enough yet.

> So you would say something like "Valid types are listed in the table
> below, each corresponding to either postmaster, an auxiliary process
> type or a backend".  (Eh, wait, you seem not to have "postmaster" in
> your list, what's up with that?)
>

Good question. The current patch uses "backend" to B_INVALID (that's exactly the
MyBackendType for postmaster -- see below). I think it is reasonable to create a
new category "postmaster" and assign it to B_INVALID. If, for some reason, a
process temporarily has MyBackendType equals to B_INVALID, it is ok to still
consider it a postmaster process.

$ ps aux | grep postgres
euler      56968  0.0  0.0 217144 29016 ?        Ss   19:28   0:00 /c/pg/bin/postgres -D /c/pg/pgdata
euler      56969  0.0  0.0 217276  9948 ?        Ss   19:28   0:00 postgres: io worker 0
euler      56970  0.0  0.0 217276  7620 ?        Ss   19:28   0:00 postgres: io worker 1
euler      56971  0.0  0.0 217144  6408 ?        Ss   19:28   0:00 postgres: io worker 2
euler      56972  0.0  0.0 217276  8388 ?        Ss   19:28   0:00 postgres: checkpointer
euler      56973  0.0  0.0 217300  8744 ?        Ss   19:28   0:00 postgres: background writer
euler      56975  0.0  0.0 217276 11292 ?        Ss   19:28   0:00 postgres: walwriter
euler      56976  0.0  0.0 218724  9140 ?        Ss   19:28   0:00 postgres: autovacuum launcher
euler      56977  0.0  0.0 218724  9232 ?        Ss   19:28   0:00 postgres: logical replication launcher
euler      58717  0.0  0.0   6676  2232 pts/1    S+   19:39   0:00 grep --color=auto postgres
$ for p in $(pgrep postgres); do echo "pid: $p" && gdb -q -batch -ex 'set pagination off' -ex "attach $p" -ex 'p MyBackendType' -ex 'quit' 2> /dev/null; done | grep -E '^\$1|pid'
pid: 56968
$1 = B_INVALID
pid: 56969
$1 = B_IO_WORKER
pid: 56970
$1 = B_IO_WORKER
pid: 56971
$1 = B_IO_WORKER
pid: 56972
$1 = B_CHECKPOINTER
pid: 56973
$1 = B_BG_WRITER
pid: 56975
$1 = B_WAL_WRITER
pid: 56976
$1 = B_AUTOVAC_LAUNCHER
pid: 56977
$1 = B_BG_WORKER

> (I'm not sure about making the log levels also a <simplelist>, because
> for that list the order matters, and if we have more than one column
> then the order is going to be ambiguous, and if we have just one column
> it's going to be too tall.  But maybe it's not all that bad?)
>

+1.


-- 
Euler Taveira
EDB   https://www.enterprisedb.com/

Attachments:

  [image/png] lmm.png (90.9K, ../../6201c638-92e2-46dd-b022-9cde55727b05@app.fastmail.com/2-lmm.png)
  download | view image

^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-11-19 10:44  Alvaro Herrera <alvherre@alvh.no-ip.org>
  parent: Euler Taveira <euler@eulerto.com>
  0 siblings, 1 reply; 58+ messages in thread

From: Alvaro Herrera @ 2025-11-19 10:44 UTC (permalink / raw)
  To: Euler Taveira <euler@eulerto.com>; +Cc: japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org

On 2025-Nov-18, Euler Taveira wrote:

> On Thu, Nov 6, 2025, at 1:01 PM, Alvaro Herrera wrote:

> See miscadmin.h.
> 
> /*
>  * MyBackendType indicates what kind of a backend this is.
>  *
>  * If you add entries, please also update the child_process_kinds array in
>  * launch_backend.c.
>  */
> typedef enum BackendType
> 
> The "backend type" terminology is exposed.

Something appearing in the source code does not equate it being exposed
to end users.  Things are exposed when they are in the documentation, or
when the SQL interface requires you to use the term.  So I don't think
the fact that BackendType appears in the code forces us to use that name
in the user-visible interface now.

In the glossary, we talk about "processes"; in the definitions there,
"backend" is one type of process.  So in this list of "backend types"
that you want to introduce, what you really should be talking about is
"process types", where "backend" is one of the options.

> It is even available in the
> pg_stat_activity view. I agree that "backend" is not a good name to define a
> Postgres process. I don't think we should be inconsistent only in this patch.
> Even if the proposal is to rename enum BackendType (I won't propose it as part
> of this patch), it would make some extension authors unhappy according to
> codesearch.debian.net.

I don't think we should rename the BackendType enum.  That's in the
source code, not in the documentation or the SQL interface, so we don't
need to care.

> > I think the list of backend types is pretty hard to read.  What do you
> > think about using 
> > <simplelist type="vert" columns="4">
> > to create a friendlier representation?  
> 
> I tried but didn't like the result. See the attached image.

Well, I like it very much.  The original is a solid wall of text, very
hard to read, where you have to squint hunting commas in order to
distinguish one item from the next.  (Maybe you are young and have good
eyesight, but you won't be young forever.)  In the screenshot you show,
the list of possible process types to use is nicely separated, which
makes it very easy to catch at a glance.  Maybe remove "the table",
which is obviously inappropriate, and just say "Valid process types are
listed below, each corresponding to either postmaster, an auxiliary
process type or a backend".

(Note your original wording says "a backend type, corresponding to [blah
blah] or a backend" which makes no sense -- how is a backend a type of
backend?  It isn't.  It's a type of process.)

> You said table but it is a multi-column list; it doesn't contain a
> header or borders.

Right.  You don't need a full-blown table here: this simple list is
perfectly adequate.

> Good question. The current patch uses "backend" to B_INVALID (that's exactly the
> MyBackendType for postmaster -- see below). I think it is reasonable to create a
> new category "postmaster" and assign it to B_INVALID.

I guess that would work, but I think it's inadequate.  Maybe we could
add a new value B_POSTMASTER and have postmaster switch to that as early
as possible.  Then anything that still has B_INVALID must necessarily be
an improperly identified process.  Users wouldn't assign a value to that
one (the GUC wouldn't let you); instead those would always use the
default value.  Hopefully nobody would see that very often, or at all.

-- 
Álvaro Herrera        Breisgau, Deutschland  —  https://www.EnterpriseDB.com/





^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-11-21 14:13  Euler Taveira <euler@eulerto.com>
  parent: Alvaro Herrera <alvherre@alvh.no-ip.org>
  0 siblings, 1 reply; 58+ messages in thread

From: Euler Taveira @ 2025-11-21 14:13 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org

On Wed, Nov 19, 2025, at 7:44 AM, Alvaro Herrera wrote:
> On 2025-Nov-18, Euler Taveira wrote:
>> The "backend type" terminology is exposed.
>
> Something appearing in the source code does not equate it being exposed
> to end users.  Things are exposed when they are in the documentation, or
> when the SQL interface requires you to use the term.  So I don't think
> the fact that BackendType appears in the code forces us to use that name
> in the user-visible interface now.
>

It is *already* exposed.

$ grep -B 3 -A 3 'backend type' doc/src/sgml/config.sgml 
         </informaltable>

         <para>
          The backend type corresponds to the column
          <structfield>backend_type</structfield> in the view
          <link linkend="monitoring-pg-stat-activity-view">
          <structname>pg_stat_activity</structname></link>,
--
        character count of the error position therein,
        location of the error in the PostgreSQL source code
        (if <varname>log_error_verbosity</varname> is set to <literal>verbose</literal>),
        application name, backend type, process ID of parallel group leader,
        and query id.
        Here is a sample table definition for storing CSV-format log output:

$ grep -B 3 -A 3 'backend_type' doc/src/sgml/config.sgml 

         <para>
          The backend type corresponds to the column
          <structfield>backend_type</structfield> in the view
          <link linkend="monitoring-pg-stat-activity-view">
          <structname>pg_stat_activity</structname></link>,
          but additional types can appear
--
  query_pos integer,
  location text,
  application_name text,
  backend_type text,
  leader_pid integer,
  query_id bigint,
  PRIMARY KEY (session_id, session_line_num)
--
         <entry>Client application name</entry>
        </row>
        <row>
         <entry><literal>backend_type</literal></entry>
         <entry>string</entry>
         <entry>Type of backend</entry>
        </row>

$ psql -c "SELECT backend_type FROM pg_stat_activity" -d postgres
         backend_type         
------------------------------
 client backend
 autovacuum launcher
 logical replication launcher
 io worker
 io worker
 io worker
 checkpointer
 background writer
 walwriter
(9 rows)

> In the glossary, we talk about "processes"; in the definitions there,
> "backend" is one type of process.  So in this list of "backend types"
> that you want to introduce, what you really should be talking about is
> "process types", where "backend" is one of the options.
>
>> It is even available in the
>> pg_stat_activity view. I agree that "backend" is not a good name to define a
>> Postgres process. I don't think we should be inconsistent only in this patch.
>> Even if the proposal is to rename enum BackendType (I won't propose it as part
>> of this patch), it would make some extension authors unhappy according to
>> codesearch.debian.net.
>
> I don't think we should rename the BackendType enum.  That's in the
> source code, not in the documentation or the SQL interface, so we don't
> need to care.
>

I think you missed my point here. As I said if your proposal will let us with 2
terminologies (backend type and process type) as shown above.  We can debate if
we (1) keep the current terminology (backend type), (2) use the proposed one
(process type) or (3) use a mixed variation (keep the SQL interface --
backend_type column but change the description to "process type"). I wouldn't
like to break compatibility (pg_stat_activity changes tend to break a lot of
stuff) so I'm fine with (1) and (3).

>> > I think the list of backend types is pretty hard to read.  What do you
>> > think about using 
>> > <simplelist type="vert" columns="4">
>> > to create a friendlier representation?  
>> 
>> I tried but didn't like the result. See the attached image.
>
> Well, I like it very much.  The original is a solid wall of text, very
> hard to read, where you have to squint hunting commas in order to
> distinguish one item from the next.  (Maybe you are young and have good
> eyesight, but you won't be young forever.)  In the screenshot you show,
> the list of possible process types to use is nicely separated, which
> makes it very easy to catch at a glance.  Maybe remove "the table",
> which is obviously inappropriate, and just say "Valid process types are
> listed below, each corresponding to either postmaster, an auxiliary
> process type or a backend".
>

I don't have a strong preference. One advantage of this suggestion is that it 
is visually easier to identify the types.

> (Note your original wording says "a backend type, corresponding to [blah
> blah] or a backend" which makes no sense -- how is a backend a type of
> backend?  It isn't.  It's a type of process.)
>

I'm rewriting that paragraph.

>> Good question. The current patch uses "backend" to B_INVALID (that's exactly the
>> MyBackendType for postmaster -- see below). I think it is reasonable to create a
>> new category "postmaster" and assign it to B_INVALID.
>
> I guess that would work, but I think it's inadequate.  Maybe we could
> add a new value B_POSTMASTER and have postmaster switch to that as early
> as possible.  Then anything that still has B_INVALID must necessarily be
> an improperly identified process.  Users wouldn't assign a value to that
> one (the GUC wouldn't let you); instead those would always use the
> default value.  Hopefully nobody would see that very often, or at all.
>

I will create a separate patch for this suggestion.


-- 
Euler Taveira
EDB   https://www.enterprisedb.com/





^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-11-26 02:11  Euler Taveira <euler@eulerto.com>
  parent: Euler Taveira <euler@eulerto.com>
  0 siblings, 1 reply; 58+ messages in thread

From: Euler Taveira @ 2025-11-26 02:11 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org, "Chao Li" <li.evan.chao@gmail.com>

On Fri, Nov 21, 2025, at 11:13 AM, Euler Taveira wrote:
> I think you missed my point here. As I said if your proposal will let us with 2
> terminologies (backend type and process type) as shown above.  We can debate if
> we (1) keep the current terminology (backend type), (2) use the proposed one
> (process type) or (3) use a mixed variation (keep the SQL interface --
> backend_type column but change the description to "process type"). I wouldn't
> like to break compatibility (pg_stat_activity changes tend to break a lot of
> stuff) so I'm fine with (1) and (3).
>

After digesting it for a couple of days, I decided to adopt the terminology in
commit dbf8cfb4f02e (process type) just for this patch. This discussion about
changing "backend type" terminology in other parts belongs to another patch.

>>> Good question. The current patch uses "backend" to B_INVALID (that's exactly the
>>> MyBackendType for postmaster -- see below). I think it is reasonable to create a
>>> new category "postmaster" and assign it to B_INVALID.
>>
>> I guess that would work, but I think it's inadequate.  Maybe we could
>> add a new value B_POSTMASTER and have postmaster switch to that as early
>> as possible.  Then anything that still has B_INVALID must necessarily be
>> an improperly identified process.  Users wouldn't assign a value to that
>> one (the GUC wouldn't let you); instead those would always use the
>> default value.  Hopefully nobody would see that very often, or at all.
>>
>
> I will create a separate patch for this suggestion.
>

After reflection, add B_POSTMASTER into BackendType / proctypelist.h is not a
good idea. These data structures were designed to represent postmaster
children. The child_process_kinds array (launch_backend.c) is assembled with
proctypelist.h which means a function like postmaster_child_launch() could use
one of its elements (e.g. B_POSTMASTER) to start a new child; that's awkward. Do
you envision any issues to use B_INVALID for postmaster? (I wrote 0002 to
minimize the windows that each child process has B_INVALID.)

This new version contains the following changes:

- fix variable assignment (Chao Li)
- fix memory release order (Chao Li)
- change category for B_INVALID (backend -> postmaster) (Alvaro)
- rewrite documentation (Chao Li, Alvaro)
- rename backend type to process type (Alvaro)
- new patch (0002) that assigns MyBackendType as earlier as possible


-- 
Euler Taveira
EDB   https://www.enterprisedb.com/

Attachments:

  [text/x-patch] v6-0001-log_min_messages-per-process-type.patch (26.3K, ../../144f0f96-c6d3-4ad6-a236-00200513e2e0@app.fastmail.com/2-v6-0001-log_min_messages-per-process-type.patch)
  download | inline diff:
From 5a6bfa1c9c6183eba8de5bbdeac43ea4ea5d8229 Mon Sep 17 00:00:00 2001
From: Euler Taveira <euler@eulerto.com>
Date: Tue, 4 Nov 2025 17:18:20 -0300
Subject: [PATCH v6 1/2] log_min_messages per process type

Change log_min_messages from a single element to a comma-separated list
of elements. Each element is type:level. The types are archiver,
autovacuum (includes launcher and workers), backend, bgworker, bgwriter,
checkpointer, ioworker, postmaster, syslogger, slotsyncworker, startup,
walreceiver, walsender, walsummarizer and walwriter. A single log level
should be part of this list and it is applied as a final step to the
process types that were not informed.

The old syntax (a single log level) is still accepted for backward
compatibility. In this case, it means the informed log level is applied
for all process types. The default log level is the same (WARNING) for
all process types.
---
 doc/src/sgml/config.sgml                      |  42 +++-
 src/backend/commands/extension.c              |   2 +-
 src/backend/commands/variable.c               | 208 ++++++++++++++++++
 src/backend/postmaster/launch_backend.c       |   2 +-
 src/backend/utils/error/elog.c                |   2 +-
 src/backend/utils/init/miscinit.c             |   2 +-
 src/backend/utils/misc/guc_parameters.dat     |  10 +-
 src/backend/utils/misc/guc_tables.c           |  21 +-
 src/backend/utils/misc/postgresql.conf.sample |   2 +
 src/include/postmaster/proctypelist.h         |  42 ++--
 src/include/utils/guc.h                       |   6 +-
 src/include/utils/guc_hooks.h                 |   2 +
 src/test/regress/expected/guc.out             |  54 +++++
 src/test/regress/sql/guc.sql                  |  18 ++
 14 files changed, 372 insertions(+), 41 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 023b3f03ba9..fba26aa746e 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -7063,7 +7063,7 @@ local0.*    /var/log/postgresql
      <variablelist>
 
      <varlistentry id="guc-log-min-messages" xreflabel="log_min_messages">
-      <term><varname>log_min_messages</varname> (<type>enum</type>)
+      <term><varname>log_min_messages</varname> (<type>string</type>)
       <indexterm>
        <primary><varname>log_min_messages</varname> configuration parameter</primary>
       </indexterm>
@@ -7072,14 +7072,38 @@ local0.*    /var/log/postgresql
        <para>
         Controls which <link linkend="runtime-config-severity-levels">message
         levels</link> are written to the server log.
-        Valid values are <literal>DEBUG5</literal>, <literal>DEBUG4</literal>,
-        <literal>DEBUG3</literal>, <literal>DEBUG2</literal>, <literal>DEBUG1</literal>,
-        <literal>INFO</literal>, <literal>NOTICE</literal>, <literal>WARNING</literal>,
-        <literal>ERROR</literal>, <literal>LOG</literal>, <literal>FATAL</literal>, and
-        <literal>PANIC</literal>.  Each level includes all the levels that
-        follow it.  The later the level, the fewer messages are sent
-        to the log.  The default is <literal>WARNING</literal>.  Note that
-        <literal>LOG</literal> has a different rank here than in
+        Valid values are a comma-separated list of <literal>type:level</literal>
+        and a single <literal>level</literal>. The list allows it to use
+        different levels per process type. Only the single <literal>level</literal>
+        is mandatory (order does not matter) and it is assigned to the process
+        types that are not specified in the list.
+        Valid process types are listed in the table below, each corresponding to
+        either postmaster, an auxiliary process type or a backend.
+        <simplelist type="vert" columns="4">
+         <member><literal>archiver</literal></member>
+         <member><literal>autovacuum</literal></member>
+         <member><literal>backend</literal></member>
+         <member><literal>bgworker</literal></member>
+         <member><literal>bgwriter</literal></member>
+         <member><literal>checkpointer</literal></member>
+         <member><literal>ioworker</literal></member>
+         <member><literal>postmaster</literal></member>
+         <member><literal>syslogger</literal></member>
+         <member><literal>slotsyncworker</literal></member>
+         <member><literal>startup</literal></member>
+         <member><literal>walreceiver</literal></member>
+         <member><literal>walsender</literal></member>
+         <member><literal>walsummarizer</literal></member>
+         <member><literal>walwriter</literal></member>
+        </simplelist>
+        Valid <literal>level</literal> values are <literal>DEBUG5</literal>,
+        <literal>DEBUG4</literal>, <literal>DEBUG3</literal>, <literal>DEBUG2</literal>,
+        <literal>DEBUG1</literal>, <literal>INFO</literal>, <literal>NOTICE</literal>,
+        <literal>WARNING</literal>, <literal>ERROR</literal>, <literal>LOG</literal>,
+        <literal>FATAL</literal>, and <literal>PANIC</literal>.  Each level includes
+        all the levels that follow it.  The later the level, the fewer messages are sent
+        to the log.  The default is <literal>WARNING</literal> for all process types.
+        Note that <literal>LOG</literal> has a different rank here than in
         <xref linkend="guc-client-min-messages"/>.
         Only superusers and users with the appropriate <literal>SET</literal>
         privilege can change this setting.
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index ebc204c4462..d123b7ebb77 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1143,7 +1143,7 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
 		(void) set_config_option("client_min_messages", "warning",
 								 PGC_USERSET, PGC_S_SESSION,
 								 GUC_ACTION_SAVE, true, 0, false);
-	if (log_min_messages < WARNING)
+	if (log_min_messages[MyBackendType] < WARNING)
 		(void) set_config_option_ext("log_min_messages", "warning",
 									 PGC_SUSET, PGC_S_SESSION,
 									 BOOTSTRAP_SUPERUSERID,
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index 608f10d9412..2b309791ee7 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -34,6 +34,7 @@
 #include "utils/backend_status.h"
 #include "utils/datetime.h"
 #include "utils/fmgrprotos.h"
+#include "utils/guc.h"
 #include "utils/guc_hooks.h"
 #include "utils/snapmgr.h"
 #include "utils/syscache.h"
@@ -1257,3 +1258,210 @@ check_ssl(bool *newval, void **extra, GucSource source)
 #endif
 	return true;
 }
+
+/*
+ * GUC check_hook for log_min_messages
+ *
+ * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE
+ * is log_min_messages_process_types.  LEVEL is server_message_level_options. A
+ * single LEVEL element should be part of this list and it is applied as a
+ * final step to the process types that are not specified. For backward
+ * compatibility, the old syntax is still accepted and it means to apply the
+ * LEVEL for all process types.
+ */
+bool
+check_log_min_messages(char **newval, void **extra, GucSource source)
+{
+	char	   *rawstring;
+	List	   *elemlist;
+	ListCell   *l;
+	int			newlevel[BACKEND_NUM_TYPES];
+	bool		assigned[BACKEND_NUM_TYPES];
+	int			genericlevel = -1;	/* -1 means not assigned */
+
+	/* Initialize the array. */
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+	{
+		newlevel[i] = WARNING;
+		assigned[i] = false;
+	}
+
+	/* Need a modifiable copy of string. */
+	rawstring = guc_strdup(LOG, *newval);
+
+	/* Parse string into list of identifiers. */
+	if (!SplitGUCList(rawstring, ',', &elemlist))
+	{
+		/* syntax error in list */
+		GUC_check_errdetail("List syntax is invalid.");
+		list_free(elemlist);
+		guc_free(rawstring);
+		return false;
+	}
+
+	/* Validate and assign log level and process type. */
+	foreach(l, elemlist)
+	{
+		char	   *tok = (char *) lfirst(l);
+		char	   *sep;
+		const struct config_enum_entry *entry;
+
+		/*
+		 * Check whether there is a process type following the log level. If
+		 * there is no separator, it means this is the generic log level. The
+		 * generic log level will be assigned to the process types that were
+		 * not informed.
+		 */
+		sep = strchr(tok, ':');
+		if (sep == NULL)
+		{
+			bool		found = false;
+
+			/* Reject duplicates for generic log level. */
+			if (genericlevel != -1)
+			{
+				GUC_check_errdetail("Generic log level was already assigned.");
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			/* Is the log level valid? */
+			for (entry = server_message_level_options; entry && entry->name; entry++)
+			{
+				if (pg_strcasecmp(entry->name, tok) == 0)
+				{
+					genericlevel = entry->val;
+					found = true;
+					break;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized log level: \"%s\".", tok);
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+		}
+		else
+		{
+			char	   *loglevel;
+			char	   *ptype;
+			bool		found = false;
+
+			ptype = guc_malloc(LOG, (sep - tok) + 1);
+			if (!ptype)
+			{
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+			memcpy(ptype, tok, sep - tok);
+			ptype[sep - tok] = '\0';
+			loglevel = sep + 1;
+
+			/* Is the log level valid? */
+			for (entry = server_message_level_options; entry && entry->name; entry++)
+			{
+				if (pg_strcasecmp(entry->name, loglevel) == 0)
+				{
+					found = true;
+					break;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel);
+				guc_free(ptype);
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			/*
+			 * Is the process type name valid? There might be multiple entries
+			 * per process type, don't bail out because it can assign the
+			 * value for multiple entries.
+			 */
+			found = false;
+			for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+			{
+				if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0)
+				{
+					/* Reject duplicates for a process type. */
+					if (assigned[i])
+					{
+						GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype);
+						guc_free(ptype);
+						guc_free(rawstring);
+						list_free(elemlist);
+						return false;
+					}
+
+					newlevel[i] = entry->val;
+					assigned[i] = true;
+					found = true;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype);
+				guc_free(ptype);
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			guc_free(ptype);
+		}
+	}
+
+	/*
+	 * The generic log level must be specified. It is the fallback value.
+	 */
+	if (genericlevel == -1)
+	{
+		GUC_check_errdetail("Generic log level was not defined.");
+		guc_free(rawstring);
+		list_free(elemlist);
+		return false;
+	}
+
+	/*
+	 * Apply the generic log level after all of the specific process types
+	 * have been assigned. Hence, it doesn't matter the order you specify the
+	 * generic log level, the final result will be the same.
+	 */
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+	{
+		if (!assigned[i])
+			newlevel[i] = genericlevel;
+	}
+
+	guc_free(rawstring);
+	list_free(elemlist);
+
+	/*
+	 * Pass back data for assign_log_min_messages to use.
+	 */
+	*extra = guc_malloc(LOG, BACKEND_NUM_TYPES * sizeof(int));
+	if (!*extra)
+		return false;
+	memcpy(*extra, newlevel, BACKEND_NUM_TYPES * sizeof(int));
+
+	return true;
+}
+
+/*
+ * GUC assign_hook for log_min_messages
+ */
+void
+assign_log_min_messages(const char *newval, void *extra)
+{
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+		log_min_messages[i] = ((int *) extra)[i];
+}
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 976638a58ac..c2a044321d1 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -179,7 +179,7 @@ typedef struct
 } child_process_kind;
 
 static child_process_kind child_process_kinds[] = {
-#define PG_PROCTYPE(bktype, description, main_func, shmem_attach) \
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
 	[bktype] = {description, main_func, shmem_attach},
 #include "postmaster/proctypelist.h"
 #undef PG_PROCTYPE
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 29643c51439..15d771a16c9 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -235,7 +235,7 @@ is_log_level_output(int elevel, int log_min_level)
 static inline bool
 should_output_to_server(int elevel)
 {
-	return is_log_level_output(elevel, log_min_messages);
+	return is_log_level_output(elevel, log_min_messages[MyBackendType]);
 }
 
 /*
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index fec79992c8d..2c3926cbadf 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType)
 
 	switch (backendType)
 	{
-#define PG_PROCTYPE(bktype, description, main_func, shmem_attach)	\
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages)	\
 		case bktype: backendDesc = description; break;
 #include "postmaster/proctypelist.h"
 #undef PG_PROCTYPE
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index 1128167c025..55dfc2e6548 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -1676,12 +1676,14 @@
   options => 'server_message_level_options',
 },
 
-{ name => 'log_min_messages', type => 'enum', context => 'PGC_SUSET', group => 'LOGGING_WHEN',
+{ name => 'log_min_messages', type => 'string', context => 'PGC_SUSET', group => 'LOGGING_WHEN',
   short_desc => 'Sets the message levels that are logged.',
   long_desc => 'Each level includes all the levels that follow it. The later the level, the fewer messages are sent.',
-  variable => 'log_min_messages',
-  boot_val => 'WARNING',
-  options => 'server_message_level_options',
+  flags => 'GUC_LIST_INPUT',
+  variable => 'log_min_messages_string',
+  boot_val => '"WARNING"',
+  check_hook => 'check_log_min_messages',
+  assign_hook => 'assign_log_min_messages',
 },
 
 { name => 'log_parameter_max_length', type => 'int', context => 'PGC_SUSET', group => 'LOGGING_WHAT',
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 0209b2067a2..ff9b491327d 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -146,7 +146,7 @@ static const struct config_enum_entry client_message_level_options[] = {
 	{NULL, 0, false}
 };
 
-static const struct config_enum_entry server_message_level_options[] = {
+const struct config_enum_entry server_message_level_options[] = {
 	{"debug5", DEBUG5, false},
 	{"debug4", DEBUG4, false},
 	{"debug3", DEBUG3, false},
@@ -537,7 +537,6 @@ static bool default_with_oids = false;
 bool		current_role_is_superuser;
 
 int			log_min_error_statement = ERROR;
-int			log_min_messages = WARNING;
 int			client_min_messages = NOTICE;
 int			log_min_duration_sample = -1;
 int			log_min_duration_statement = -1;
@@ -595,6 +594,7 @@ static char *server_version_string;
 static int	server_version_num;
 static char *debug_io_direct_string;
 static char *restrict_nonsystem_relation_kind_string;
+static char *log_min_messages_string;
 
 #ifdef HAVE_SYSLOG
 #define	DEFAULT_SYSLOG_FACILITY LOG_LOCAL0
@@ -639,6 +639,23 @@ char	   *role_string;
 /* should be static, but guc.c needs to get at this */
 bool		in_hot_standby_guc;
 
+/*
+ * log_min_messages
+ */
+int			log_min_messages[] = {
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
+	[bktype] = log_min_messages,
+#include "postmaster/proctypelist.h"
+#undef PG_PROCTYPE
+};
+
+const char *const log_min_messages_process_types[] = {
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
+	[bktype] = bkcategory,
+#include "postmaster/proctypelist.h"
+#undef PG_PROCTYPE
+};
+
 
 /*
  * Displayable names for context types (enum GucContext)
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index dc9e2255f8a..24b5d983590 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -541,6 +541,8 @@
                                         #   log
                                         #   fatal
                                         #   panic
+                                        #   and an optional comma-separated list
+                                        #   of type:level
 
 #log_min_error_statement = error        # values in order of decreasing detail:
                                         #   debug5
diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h
index 242862451d8..5fbb8e7ab7c 100644
--- a/src/include/postmaster/proctypelist.h
+++ b/src/include/postmaster/proctypelist.h
@@ -25,27 +25,27 @@
  */
 
 /*
- * List of process types (symbol, description, Main function, shmem_attach)
- * entries.
+ * List of process types (symbol, category, description, Main function,
+ * shmem_attach, message level) entries.
  */
 
 
-/* bktype, description, main_func, shmem_attach */
-PG_PROCTYPE(B_ARCHIVER, gettext_noop("archiver"), PgArchiverMain, true)
-PG_PROCTYPE(B_AUTOVAC_LAUNCHER, gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true)
-PG_PROCTYPE(B_AUTOVAC_WORKER, gettext_noop("autovacuum worker"), AutoVacWorkerMain, true)
-PG_PROCTYPE(B_BACKEND, gettext_noop("client backend"), BackendMain, true)
-PG_PROCTYPE(B_BG_WORKER, gettext_noop("background worker"), BackgroundWorkerMain, true)
-PG_PROCTYPE(B_BG_WRITER, gettext_noop("background writer"), BackgroundWriterMain, true)
-PG_PROCTYPE(B_CHECKPOINTER, gettext_noop("checkpointer"), CheckpointerMain, true)
-PG_PROCTYPE(B_DEAD_END_BACKEND, gettext_noop("dead-end client backend"), BackendMain, true)
-PG_PROCTYPE(B_INVALID, gettext_noop("unrecognized"), NULL, false)
-PG_PROCTYPE(B_IO_WORKER, gettext_noop("io worker"), IoWorkerMain, true)
-PG_PROCTYPE(B_LOGGER, gettext_noop("syslogger"), SysLoggerMain, false)
-PG_PROCTYPE(B_SLOTSYNC_WORKER, gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true)
-PG_PROCTYPE(B_STANDALONE_BACKEND, gettext_noop("standalone backend"), NULL, false)
-PG_PROCTYPE(B_STARTUP, gettext_noop("startup"), StartupProcessMain, true)
-PG_PROCTYPE(B_WAL_RECEIVER, gettext_noop("walreceiver"), WalReceiverMain, true)
-PG_PROCTYPE(B_WAL_SENDER, gettext_noop("walsender"), NULL, true)
-PG_PROCTYPE(B_WAL_SUMMARIZER, gettext_noop("walsummarizer"), WalSummarizerMain, true)
-PG_PROCTYPE(B_WAL_WRITER, gettext_noop("walwriter"), WalWriterMain, true)
+/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */
+PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING)
+PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING)
+PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING)
+PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING)
+PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING)
+PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING)
+PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING)
+PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING)
+PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING)
+PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING)
+PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING)
+PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING)
+PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING)
+PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING)
+PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING)
+PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING)
+PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING)
+PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING)
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index f21ec37da89..98b0ba08c0c 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -295,7 +295,7 @@ extern PGDLLIMPORT bool log_duration;
 extern PGDLLIMPORT int log_parameter_max_length;
 extern PGDLLIMPORT int log_parameter_max_length_on_error;
 extern PGDLLIMPORT int log_min_error_statement;
-extern PGDLLIMPORT int log_min_messages;
+extern PGDLLIMPORT int log_min_messages[];
 extern PGDLLIMPORT int client_min_messages;
 extern PGDLLIMPORT int log_min_duration_sample;
 extern PGDLLIMPORT int log_min_duration_statement;
@@ -329,6 +329,9 @@ extern PGDLLIMPORT bool trace_sort;
 extern PGDLLIMPORT bool optimize_bounded_sort;
 #endif
 
+/* log_min_messages */
+extern PGDLLIMPORT const char *const log_min_messages_process_types[];
+
 /*
  * Declarations for options for enum values
  *
@@ -344,6 +347,7 @@ extern PGDLLIMPORT const struct config_enum_entry archive_mode_options[];
 extern PGDLLIMPORT const struct config_enum_entry dynamic_shared_memory_options[];
 extern PGDLLIMPORT const struct config_enum_entry io_method_options[];
 extern PGDLLIMPORT const struct config_enum_entry recovery_target_action_options[];
+extern PGDLLIMPORT const struct config_enum_entry server_message_level_options[];
 extern PGDLLIMPORT const struct config_enum_entry wal_level_options[];
 extern PGDLLIMPORT const struct config_enum_entry wal_sync_method_options[];
 
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index 82ac8646a8d..e6ffc96468f 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -174,5 +174,7 @@ extern void assign_wal_sync_method(int new_wal_sync_method, void *extra);
 extern bool check_synchronized_standby_slots(char **newval, void **extra,
 											 GucSource source);
 extern void assign_synchronized_standby_slots(const char *newval, void *extra);
+extern bool check_log_min_messages(char **newval, void **extra, GucSource source);
+extern void assign_log_min_messages(const char *newval, void *extra);
 
 #endif							/* GUC_HOOKS_H */
diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out
index d6fb879f500..3b8f44566b2 100644
--- a/src/test/regress/expected/guc.out
+++ b/src/test/regress/expected/guc.out
@@ -935,3 +935,57 @@ SELECT name FROM tab_settings_flags
 (0 rows)
 
 DROP TABLE tab_settings_flags;
+-- Test log_min_messages
+SET log_min_messages TO foo;  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "foo"
+DETAIL:  Unrecognized log level: "foo".
+SET log_min_messages TO fatal;
+SHOW log_min_messages;
+ log_min_messages 
+------------------
+ fatal
+(1 row)
+
+SET log_min_messages TO 'fatal';
+SHOW log_min_messages;
+ log_min_messages 
+------------------
+ fatal
+(1 row)
+
+SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1';  --fail
+ERROR:  invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1"
+DETAIL:  Generic log level was not defined.
+SET log_min_messages TO 'debug1, backend:error, fatal';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "debug1, backend:error, fatal"
+DETAIL:  Generic log level was already assigned.
+SET log_min_messages TO 'backend:error, debug1, backend:warning';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning"
+DETAIL:  Process type "backend" was already assigned.
+SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1"
+DETAIL:  Unrecognized process type: "foo".
+SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1"
+DETAIL:  Unrecognized log level: "bar".
+SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3';
+SHOW log_min_messages;
+                                        log_min_messages                                         
+-------------------------------------------------------------------------------------------------
+ backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3
+(1 row)
+
+SET log_min_messages TO 'warning, autovacuum:debug1';
+SHOW log_min_messages;
+      log_min_messages      
+----------------------------
+ warning, autovacuum:debug1
+(1 row)
+
+SET log_min_messages TO 'autovacuum:debug1, warning';
+SHOW log_min_messages;
+      log_min_messages      
+----------------------------
+ autovacuum:debug1, warning
+(1 row)
+
diff --git a/src/test/regress/sql/guc.sql b/src/test/regress/sql/guc.sql
index bafaf067e82..f7dfc909b42 100644
--- a/src/test/regress/sql/guc.sql
+++ b/src/test/regress/sql/guc.sql
@@ -377,3 +377,21 @@ SELECT name FROM tab_settings_flags
   WHERE no_reset AND NOT no_reset_all
   ORDER BY 1;
 DROP TABLE tab_settings_flags;
+
+-- Test log_min_messages
+SET log_min_messages TO foo;  -- fail
+SET log_min_messages TO fatal;
+SHOW log_min_messages;
+SET log_min_messages TO 'fatal';
+SHOW log_min_messages;
+SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1';  --fail
+SET log_min_messages TO 'debug1, backend:error, fatal';  -- fail
+SET log_min_messages TO 'backend:error, debug1, backend:warning';  -- fail
+SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1';  -- fail
+SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1';  -- fail
+SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3';
+SHOW log_min_messages;
+SET log_min_messages TO 'warning, autovacuum:debug1';
+SHOW log_min_messages;
+SET log_min_messages TO 'autovacuum:debug1, warning';
+SHOW log_min_messages;
-- 
2.39.5



  [text/x-patch] v6-0002-Assign-backend-type-earlier.patch (7.0K, ../../144f0f96-c6d3-4ad6-a236-00200513e2e0@app.fastmail.com/3-v6-0002-Assign-backend-type-earlier.patch)
  download | inline diff:
From 25204568735906a01004bf5c33096dfddb67a2c0 Mon Sep 17 00:00:00 2001
From: Euler Taveira <euler@eulerto.com>
Date: Tue, 25 Nov 2025 21:09:11 -0300
Subject: [PATCH v6 2/2] Assign backend type earlier

Instead of assigning the backend type into the Main function of each
postmaster child, do it earlier (after fork()). The backend type is
already known by postmaster_child_launch() before it calls fork(). This
reduces the time frame that MyBackendType is correct. (This is important
for log_min_messages per backend type that relies on a known
MyBackendType to decide if it writes a log message or not.)
---
 src/backend/postmaster/autovacuum.c        | 2 --
 src/backend/postmaster/bgworker.c          | 1 -
 src/backend/postmaster/bgwriter.c          | 1 -
 src/backend/postmaster/checkpointer.c      | 1 -
 src/backend/postmaster/launch_backend.c    | 2 ++
 src/backend/postmaster/pgarch.c            | 1 -
 src/backend/postmaster/startup.c           | 1 -
 src/backend/postmaster/syslogger.c         | 1 -
 src/backend/postmaster/walsummarizer.c     | 1 -
 src/backend/postmaster/walwriter.c         | 1 -
 src/backend/replication/logical/slotsync.c | 2 --
 src/backend/replication/walreceiver.c      | 1 -
 src/backend/storage/aio/method_worker.c    | 1 -
 13 files changed, 2 insertions(+), 14 deletions(-)

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 1c38488f2cb..36c1911a3a8 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -388,7 +388,6 @@ AutoVacLauncherMain(const void *startup_data, size_t startup_data_len)
 		PostmasterContext = NULL;
 	}
 
-	MyBackendType = B_AUTOVAC_LAUNCHER;
 	init_ps_display(NULL);
 
 	ereport(DEBUG1,
@@ -1401,7 +1400,6 @@ AutoVacWorkerMain(const void *startup_data, size_t startup_data_len)
 		PostmasterContext = NULL;
 	}
 
-	MyBackendType = B_AUTOVAC_WORKER;
 	init_ps_display(NULL);
 
 	Assert(GetProcessingMode() == InitProcessing);
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 142a02eb5e9..260f051711e 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -741,7 +741,6 @@ BackgroundWorkerMain(const void *startup_data, size_t startup_data_len)
 	}
 
 	MyBgworkerEntry = worker;
-	MyBackendType = B_BG_WORKER;
 	init_ps_display(worker->bgw_name);
 
 	Assert(GetProcessingMode() == InitProcessing);
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index 72f5acceec7..4119758a72c 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -94,7 +94,6 @@ BackgroundWriterMain(const void *startup_data, size_t startup_data_len)
 
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_BG_WRITER;
 	AuxiliaryProcessMainCommon();
 
 	/*
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index e84e8663e96..32e849bea15 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -199,7 +199,6 @@ CheckpointerMain(const void *startup_data, size_t startup_data_len)
 
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_CHECKPOINTER;
 	AuxiliaryProcessMainCommon();
 
 	CheckpointerShmem->checkpointer_pid = MyProcPid;
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index c2a044321d1..35420b8c9fb 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -224,6 +224,8 @@ postmaster_child_launch(BackendType child_type, int child_slot,
 	pid = fork_process();
 	if (pid == 0)				/* child */
 	{
+		MyBackendType = child_type;
+
 		/* Capture and transfer timings that may be needed for logging */
 		if (IsExternalConnectionBackend(child_type))
 		{
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index ce6b5299324..b760d1aed40 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -222,7 +222,6 @@ PgArchiverMain(const void *startup_data, size_t startup_data_len)
 {
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_ARCHIVER;
 	AuxiliaryProcessMainCommon();
 
 	/*
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index 27e86cf393f..13e86a2921e 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -217,7 +217,6 @@ StartupProcessMain(const void *startup_data, size_t startup_data_len)
 {
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_STARTUP;
 	AuxiliaryProcessMainCommon();
 
 	/* Arrange to clean up at startup process exit */
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index 50c2edec1f6..a378b15a6de 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -206,7 +206,6 @@ SysLoggerMain(const void *startup_data, size_t startup_data_len)
 
 	now = MyStartTime;
 
-	MyBackendType = B_LOGGER;
 	init_ps_display(NULL);
 
 	/*
diff --git a/src/backend/postmaster/walsummarizer.c b/src/backend/postmaster/walsummarizer.c
index c4a888a081c..ce497d8ec42 100644
--- a/src/backend/postmaster/walsummarizer.c
+++ b/src/backend/postmaster/walsummarizer.c
@@ -234,7 +234,6 @@ WalSummarizerMain(const void *startup_data, size_t startup_data_len)
 
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_WAL_SUMMARIZER;
 	AuxiliaryProcessMainCommon();
 
 	ereport(DEBUG1,
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index fd92c8b7a33..c1b4ddda555 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -94,7 +94,6 @@ WalWriterMain(const void *startup_data, size_t startup_data_len)
 
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_WAL_WRITER;
 	AuxiliaryProcessMainCommon();
 
 	/*
diff --git a/src/backend/replication/logical/slotsync.c b/src/backend/replication/logical/slotsync.c
index 1f4f06d467b..c9069f299b6 100644
--- a/src/backend/replication/logical/slotsync.c
+++ b/src/backend/replication/logical/slotsync.c
@@ -1403,8 +1403,6 @@ ReplSlotSyncWorkerMain(const void *startup_data, size_t startup_data_len)
 
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_SLOTSYNC_WORKER;
-
 	init_ps_display(NULL);
 
 	Assert(GetProcessingMode() == InitProcessing);
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 4217fc54e2e..fe35cacb95c 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -168,7 +168,6 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len)
 
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_WAL_RECEIVER;
 	AuxiliaryProcessMainCommon();
 
 	/*
diff --git a/src/backend/storage/aio/method_worker.c b/src/backend/storage/aio/method_worker.c
index b5ac073a910..fee1dae7e2d 100644
--- a/src/backend/storage/aio/method_worker.c
+++ b/src/backend/storage/aio/method_worker.c
@@ -391,7 +391,6 @@ IoWorkerMain(const void *startup_data, size_t startup_data_len)
 	volatile int error_errno = 0;
 	char		cmd[128];
 
-	MyBackendType = B_IO_WORKER;
 	AuxiliaryProcessMainCommon();
 
 	pqsignal(SIGHUP, SignalHandlerForConfigReload);
-- 
2.39.5



^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-11-26 02:15  Euler Taveira <euler@eulerto.com>
  parent: Chao Li <li.evan.chao@gmail.com>
  0 siblings, 0 replies; 58+ messages in thread

From: Euler Taveira @ 2025-11-26 02:15 UTC (permalink / raw)
  To: Chao Li <li.evan.chao@gmail.com>; +Cc: japin <japinli@hotmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org

On Thu, Nov 6, 2025, at 11:53 AM, Chao Li wrote:
> I just reviewed the patch and got a few comments.
>

Thanks for your review.

> +	/* Initialize the array. */
> +	memset(newlevel, WARNING, BACKEND_NUM_TYPES * sizeof(int));
> ```
>
> I think this statement is wrong, because memset() writes bytes but 
> integers, so this statement will set very byte to WARNING, which should 
> not be the intention. You will need to use a loop to initialize every 
> element of newlevel.
>

Facepalm. Good catch.

> 2 - variable.c
> ```
> +	/* Parse string into list of identifiers. */
> +	if (!SplitGUCList(rawstring, ',', &elemlist))
> +	{
> +		/* syntax error in list */
> +		GUC_check_errdetail("List syntax is invalid.");
> +		guc_free(rawstring);
> +		list_free(elemlist);
> +		return false;
> +	}
> ```
>
> Every element of elemlist points to a position of rawstring, so it’s 
> better to list_free(elemlist) first then guc_free(rawstring).
>

Fixed.

> 3 - launch_backend.c
> ```
>  static inline bool
>  should_output_to_server(int elevel)
>  {
> -	return is_log_level_output(elevel, log_min_messages);
> +	return is_log_level_output(elevel, log_min_messages[MyBackendType]);
>  }
> ```
>
> Is it possible that when this function is called, MyBackendType has not 
> been initialized? To be safe, maybe we can check if MyBackendType is 0 
> (B_INVALID), then use the generic log_min_message.
>

It uses the generic log level if you don't modify backend type "backend". If
you do specify "backend", that level is used instead. After Alvaro's question
in the other email, I added "postmaster" backend type and assigned it to
B_INVALID. That's exactly the log level that will be used. As I said in that
email, it is ok to consider a process whose backend type is B_INVALID as a
postmaster process.

> * “Valid values are …”, here “are” usually mean both are needed, so 
> maybe change “are” to “can be”.
> * It says “the single level is mandatory”, then why there is still a 
> default value?
>

It seems the current sentence that describe the comma-separated list is
ambiguous. The sentence doesn't make it clear that the list contains 2 elements
(type:level and level) and the "level" element is mandatory. What about the new
sentence?


-- 
Euler Taveira
EDB   https://www.enterprisedb.com/





^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-12-09 08:58  Alvaro Herrera <alvherre@alvh.no-ip.org>
  parent: Euler Taveira <euler@eulerto.com>
  0 siblings, 2 replies; 58+ messages in thread

From: Alvaro Herrera @ 2025-12-09 08:58 UTC (permalink / raw)
  To: Euler Taveira <euler@eulerto.com>; +Cc: japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org, Chao Li <li.evan.chao@gmail.com>

Hello

I noticed failures under Windows, and realized that the EXEC_BACKEND
case was busted.  That's easy to fix -- just assign MyBackendType in
SubPostmasterMain() as well.  With that fix I was still getting some
crashes in three test modules, and after some debugging it turned out
that if you have shared_preload_library with a background worker and use
%b in the log_line_prefix, you get a crash trying to expand a name that
hasn't been set up yet.  I figured we can use a constant string in that
case.  Better ideas for that string welcome.

So here's your v6 again with those fixes as 0003 -- let's see what CI
thinks of this.  I haven't looked at your doc changes yet.


BTW with %b in log_line_prefix, the log file looks ... interesting.

-- 
Álvaro Herrera        Breisgau, Deutschland  —  https://www.EnterpriseDB.com/
"Before you were born your parents weren't as boring as they are now. They
got that way paying your bills, cleaning up your room and listening to you
tell them how idealistic you are."  -- Charles J. Sykes' advice to teenagers

Attachments:

  [text/x-diff] v7-0001-log_min_messages-per-process-type.patch (26.3K, ../../202512090853.hk6e6t56bq5z@alvherre.pgsql/2-v7-0001-log_min_messages-per-process-type.patch)
  download | inline diff:
From d6e56c7259088529ae7a114f57f4dc875697dd97 Mon Sep 17 00:00:00 2001
From: Euler Taveira <euler@eulerto.com>
Date: Tue, 4 Nov 2025 17:18:20 -0300
Subject: [PATCH v7 1/3] log_min_messages per process type

Change log_min_messages from a single element to a comma-separated list
of elements. Each element is type:level. The types are archiver,
autovacuum (includes launcher and workers), backend, bgworker, bgwriter,
checkpointer, ioworker, postmaster, syslogger, slotsyncworker, startup,
walreceiver, walsender, walsummarizer and walwriter. A single log level
should be part of this list and it is applied as a final step to the
process types that were not informed.

The old syntax (a single log level) is still accepted for backward
compatibility. In this case, it means the informed log level is applied
for all process types. The default log level is the same (WARNING) for
all process types.
---
 doc/src/sgml/config.sgml                      |  42 +++-
 src/backend/commands/extension.c              |   2 +-
 src/backend/commands/variable.c               | 208 ++++++++++++++++++
 src/backend/postmaster/launch_backend.c       |   2 +-
 src/backend/utils/error/elog.c                |   2 +-
 src/backend/utils/init/miscinit.c             |   2 +-
 src/backend/utils/misc/guc_parameters.dat     |  10 +-
 src/backend/utils/misc/guc_tables.c           |  21 +-
 src/backend/utils/misc/postgresql.conf.sample |   2 +
 src/include/postmaster/proctypelist.h         |  42 ++--
 src/include/utils/guc.h                       |   6 +-
 src/include/utils/guc_hooks.h                 |   2 +
 src/test/regress/expected/guc.out             |  54 +++++
 src/test/regress/sql/guc.sql                  |  18 ++
 14 files changed, 372 insertions(+), 41 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 405c9689bd0..62d6c0d36a2 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -7069,7 +7069,7 @@ local0.*    /var/log/postgresql
      <variablelist>
 
      <varlistentry id="guc-log-min-messages" xreflabel="log_min_messages">
-      <term><varname>log_min_messages</varname> (<type>enum</type>)
+      <term><varname>log_min_messages</varname> (<type>string</type>)
       <indexterm>
        <primary><varname>log_min_messages</varname> configuration parameter</primary>
       </indexterm>
@@ -7078,14 +7078,38 @@ local0.*    /var/log/postgresql
        <para>
         Controls which <link linkend="runtime-config-severity-levels">message
         levels</link> are written to the server log.
-        Valid values are <literal>DEBUG5</literal>, <literal>DEBUG4</literal>,
-        <literal>DEBUG3</literal>, <literal>DEBUG2</literal>, <literal>DEBUG1</literal>,
-        <literal>INFO</literal>, <literal>NOTICE</literal>, <literal>WARNING</literal>,
-        <literal>ERROR</literal>, <literal>LOG</literal>, <literal>FATAL</literal>, and
-        <literal>PANIC</literal>.  Each level includes all the levels that
-        follow it.  The later the level, the fewer messages are sent
-        to the log.  The default is <literal>WARNING</literal>.  Note that
-        <literal>LOG</literal> has a different rank here than in
+        Valid values are a comma-separated list of <literal>type:level</literal>
+        and a single <literal>level</literal>. The list allows it to use
+        different levels per process type. Only the single <literal>level</literal>
+        is mandatory (order does not matter) and it is assigned to the process
+        types that are not specified in the list.
+        Valid process types are listed in the table below, each corresponding to
+        either postmaster, an auxiliary process type or a backend.
+        <simplelist type="vert" columns="4">
+         <member><literal>archiver</literal></member>
+         <member><literal>autovacuum</literal></member>
+         <member><literal>backend</literal></member>
+         <member><literal>bgworker</literal></member>
+         <member><literal>bgwriter</literal></member>
+         <member><literal>checkpointer</literal></member>
+         <member><literal>ioworker</literal></member>
+         <member><literal>postmaster</literal></member>
+         <member><literal>syslogger</literal></member>
+         <member><literal>slotsyncworker</literal></member>
+         <member><literal>startup</literal></member>
+         <member><literal>walreceiver</literal></member>
+         <member><literal>walsender</literal></member>
+         <member><literal>walsummarizer</literal></member>
+         <member><literal>walwriter</literal></member>
+        </simplelist>
+        Valid <literal>level</literal> values are <literal>DEBUG5</literal>,
+        <literal>DEBUG4</literal>, <literal>DEBUG3</literal>, <literal>DEBUG2</literal>,
+        <literal>DEBUG1</literal>, <literal>INFO</literal>, <literal>NOTICE</literal>,
+        <literal>WARNING</literal>, <literal>ERROR</literal>, <literal>LOG</literal>,
+        <literal>FATAL</literal>, and <literal>PANIC</literal>.  Each level includes
+        all the levels that follow it.  The later the level, the fewer messages are sent
+        to the log.  The default is <literal>WARNING</literal> for all process types.
+        Note that <literal>LOG</literal> has a different rank here than in
         <xref linkend="guc-client-min-messages"/>.
         Only superusers and users with the appropriate <literal>SET</literal>
         privilege can change this setting.
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index ebc204c4462..d123b7ebb77 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1143,7 +1143,7 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
 		(void) set_config_option("client_min_messages", "warning",
 								 PGC_USERSET, PGC_S_SESSION,
 								 GUC_ACTION_SAVE, true, 0, false);
-	if (log_min_messages < WARNING)
+	if (log_min_messages[MyBackendType] < WARNING)
 		(void) set_config_option_ext("log_min_messages", "warning",
 									 PGC_SUSET, PGC_S_SESSION,
 									 BOOTSTRAP_SUPERUSERID,
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index 608f10d9412..2b309791ee7 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -34,6 +34,7 @@
 #include "utils/backend_status.h"
 #include "utils/datetime.h"
 #include "utils/fmgrprotos.h"
+#include "utils/guc.h"
 #include "utils/guc_hooks.h"
 #include "utils/snapmgr.h"
 #include "utils/syscache.h"
@@ -1257,3 +1258,210 @@ check_ssl(bool *newval, void **extra, GucSource source)
 #endif
 	return true;
 }
+
+/*
+ * GUC check_hook for log_min_messages
+ *
+ * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE
+ * is log_min_messages_process_types.  LEVEL is server_message_level_options. A
+ * single LEVEL element should be part of this list and it is applied as a
+ * final step to the process types that are not specified. For backward
+ * compatibility, the old syntax is still accepted and it means to apply the
+ * LEVEL for all process types.
+ */
+bool
+check_log_min_messages(char **newval, void **extra, GucSource source)
+{
+	char	   *rawstring;
+	List	   *elemlist;
+	ListCell   *l;
+	int			newlevel[BACKEND_NUM_TYPES];
+	bool		assigned[BACKEND_NUM_TYPES];
+	int			genericlevel = -1;	/* -1 means not assigned */
+
+	/* Initialize the array. */
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+	{
+		newlevel[i] = WARNING;
+		assigned[i] = false;
+	}
+
+	/* Need a modifiable copy of string. */
+	rawstring = guc_strdup(LOG, *newval);
+
+	/* Parse string into list of identifiers. */
+	if (!SplitGUCList(rawstring, ',', &elemlist))
+	{
+		/* syntax error in list */
+		GUC_check_errdetail("List syntax is invalid.");
+		list_free(elemlist);
+		guc_free(rawstring);
+		return false;
+	}
+
+	/* Validate and assign log level and process type. */
+	foreach(l, elemlist)
+	{
+		char	   *tok = (char *) lfirst(l);
+		char	   *sep;
+		const struct config_enum_entry *entry;
+
+		/*
+		 * Check whether there is a process type following the log level. If
+		 * there is no separator, it means this is the generic log level. The
+		 * generic log level will be assigned to the process types that were
+		 * not informed.
+		 */
+		sep = strchr(tok, ':');
+		if (sep == NULL)
+		{
+			bool		found = false;
+
+			/* Reject duplicates for generic log level. */
+			if (genericlevel != -1)
+			{
+				GUC_check_errdetail("Generic log level was already assigned.");
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			/* Is the log level valid? */
+			for (entry = server_message_level_options; entry && entry->name; entry++)
+			{
+				if (pg_strcasecmp(entry->name, tok) == 0)
+				{
+					genericlevel = entry->val;
+					found = true;
+					break;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized log level: \"%s\".", tok);
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+		}
+		else
+		{
+			char	   *loglevel;
+			char	   *ptype;
+			bool		found = false;
+
+			ptype = guc_malloc(LOG, (sep - tok) + 1);
+			if (!ptype)
+			{
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+			memcpy(ptype, tok, sep - tok);
+			ptype[sep - tok] = '\0';
+			loglevel = sep + 1;
+
+			/* Is the log level valid? */
+			for (entry = server_message_level_options; entry && entry->name; entry++)
+			{
+				if (pg_strcasecmp(entry->name, loglevel) == 0)
+				{
+					found = true;
+					break;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel);
+				guc_free(ptype);
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			/*
+			 * Is the process type name valid? There might be multiple entries
+			 * per process type, don't bail out because it can assign the
+			 * value for multiple entries.
+			 */
+			found = false;
+			for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+			{
+				if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0)
+				{
+					/* Reject duplicates for a process type. */
+					if (assigned[i])
+					{
+						GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype);
+						guc_free(ptype);
+						guc_free(rawstring);
+						list_free(elemlist);
+						return false;
+					}
+
+					newlevel[i] = entry->val;
+					assigned[i] = true;
+					found = true;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype);
+				guc_free(ptype);
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			guc_free(ptype);
+		}
+	}
+
+	/*
+	 * The generic log level must be specified. It is the fallback value.
+	 */
+	if (genericlevel == -1)
+	{
+		GUC_check_errdetail("Generic log level was not defined.");
+		guc_free(rawstring);
+		list_free(elemlist);
+		return false;
+	}
+
+	/*
+	 * Apply the generic log level after all of the specific process types
+	 * have been assigned. Hence, it doesn't matter the order you specify the
+	 * generic log level, the final result will be the same.
+	 */
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+	{
+		if (!assigned[i])
+			newlevel[i] = genericlevel;
+	}
+
+	guc_free(rawstring);
+	list_free(elemlist);
+
+	/*
+	 * Pass back data for assign_log_min_messages to use.
+	 */
+	*extra = guc_malloc(LOG, BACKEND_NUM_TYPES * sizeof(int));
+	if (!*extra)
+		return false;
+	memcpy(*extra, newlevel, BACKEND_NUM_TYPES * sizeof(int));
+
+	return true;
+}
+
+/*
+ * GUC assign_hook for log_min_messages
+ */
+void
+assign_log_min_messages(const char *newval, void *extra)
+{
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+		log_min_messages[i] = ((int *) extra)[i];
+}
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 976638a58ac..c2a044321d1 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -179,7 +179,7 @@ typedef struct
 } child_process_kind;
 
 static child_process_kind child_process_kinds[] = {
-#define PG_PROCTYPE(bktype, description, main_func, shmem_attach) \
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
 	[bktype] = {description, main_func, shmem_attach},
 #include "postmaster/proctypelist.h"
 #undef PG_PROCTYPE
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 29643c51439..15d771a16c9 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -235,7 +235,7 @@ is_log_level_output(int elevel, int log_min_level)
 static inline bool
 should_output_to_server(int elevel)
 {
-	return is_log_level_output(elevel, log_min_messages);
+	return is_log_level_output(elevel, log_min_messages[MyBackendType]);
 }
 
 /*
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index fec79992c8d..2c3926cbadf 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType)
 
 	switch (backendType)
 	{
-#define PG_PROCTYPE(bktype, description, main_func, shmem_attach)	\
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages)	\
 		case bktype: backendDesc = description; break;
 #include "postmaster/proctypelist.h"
 #undef PG_PROCTYPE
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index 3b9d8349078..450180bee31 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -1683,12 +1683,14 @@
   options => 'server_message_level_options',
 },
 
-{ name => 'log_min_messages', type => 'enum', context => 'PGC_SUSET', group => 'LOGGING_WHEN',
+{ name => 'log_min_messages', type => 'string', context => 'PGC_SUSET', group => 'LOGGING_WHEN',
   short_desc => 'Sets the message levels that are logged.',
   long_desc => 'Each level includes all the levels that follow it. The later the level, the fewer messages are sent.',
-  variable => 'log_min_messages',
-  boot_val => 'WARNING',
-  options => 'server_message_level_options',
+  flags => 'GUC_LIST_INPUT',
+  variable => 'log_min_messages_string',
+  boot_val => '"WARNING"',
+  check_hook => 'check_log_min_messages',
+  assign_hook => 'assign_log_min_messages',
 },
 
 { name => 'log_parameter_max_length', type => 'int', context => 'PGC_SUSET', group => 'LOGGING_WHAT',
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index f87b558c2c6..a9ef447d1d4 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -146,7 +146,7 @@ static const struct config_enum_entry client_message_level_options[] = {
 	{NULL, 0, false}
 };
 
-static const struct config_enum_entry server_message_level_options[] = {
+const struct config_enum_entry server_message_level_options[] = {
 	{"debug5", DEBUG5, false},
 	{"debug4", DEBUG4, false},
 	{"debug3", DEBUG3, false},
@@ -537,7 +537,6 @@ static bool default_with_oids = false;
 bool		current_role_is_superuser;
 
 int			log_min_error_statement = ERROR;
-int			log_min_messages = WARNING;
 int			client_min_messages = NOTICE;
 int			log_min_duration_sample = -1;
 int			log_min_duration_statement = -1;
@@ -595,6 +594,7 @@ static char *server_version_string;
 static int	server_version_num;
 static char *debug_io_direct_string;
 static char *restrict_nonsystem_relation_kind_string;
+static char *log_min_messages_string;
 
 #ifdef HAVE_SYSLOG
 #define	DEFAULT_SYSLOG_FACILITY LOG_LOCAL0
@@ -646,6 +646,23 @@ char	   *role_string;
 /* should be static, but guc.c needs to get at this */
 bool		in_hot_standby_guc;
 
+/*
+ * log_min_messages
+ */
+int			log_min_messages[] = {
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
+	[bktype] = log_min_messages,
+#include "postmaster/proctypelist.h"
+#undef PG_PROCTYPE
+};
+
+const char *const log_min_messages_process_types[] = {
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
+	[bktype] = bkcategory,
+#include "postmaster/proctypelist.h"
+#undef PG_PROCTYPE
+};
+
 
 /*
  * Displayable names for context types (enum GucContext)
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index dc9e2255f8a..24b5d983590 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -541,6 +541,8 @@
                                         #   log
                                         #   fatal
                                         #   panic
+                                        #   and an optional comma-separated list
+                                        #   of type:level
 
 #log_min_error_statement = error        # values in order of decreasing detail:
                                         #   debug5
diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h
index 242862451d8..5fbb8e7ab7c 100644
--- a/src/include/postmaster/proctypelist.h
+++ b/src/include/postmaster/proctypelist.h
@@ -25,27 +25,27 @@
  */
 
 /*
- * List of process types (symbol, description, Main function, shmem_attach)
- * entries.
+ * List of process types (symbol, category, description, Main function,
+ * shmem_attach, message level) entries.
  */
 
 
-/* bktype, description, main_func, shmem_attach */
-PG_PROCTYPE(B_ARCHIVER, gettext_noop("archiver"), PgArchiverMain, true)
-PG_PROCTYPE(B_AUTOVAC_LAUNCHER, gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true)
-PG_PROCTYPE(B_AUTOVAC_WORKER, gettext_noop("autovacuum worker"), AutoVacWorkerMain, true)
-PG_PROCTYPE(B_BACKEND, gettext_noop("client backend"), BackendMain, true)
-PG_PROCTYPE(B_BG_WORKER, gettext_noop("background worker"), BackgroundWorkerMain, true)
-PG_PROCTYPE(B_BG_WRITER, gettext_noop("background writer"), BackgroundWriterMain, true)
-PG_PROCTYPE(B_CHECKPOINTER, gettext_noop("checkpointer"), CheckpointerMain, true)
-PG_PROCTYPE(B_DEAD_END_BACKEND, gettext_noop("dead-end client backend"), BackendMain, true)
-PG_PROCTYPE(B_INVALID, gettext_noop("unrecognized"), NULL, false)
-PG_PROCTYPE(B_IO_WORKER, gettext_noop("io worker"), IoWorkerMain, true)
-PG_PROCTYPE(B_LOGGER, gettext_noop("syslogger"), SysLoggerMain, false)
-PG_PROCTYPE(B_SLOTSYNC_WORKER, gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true)
-PG_PROCTYPE(B_STANDALONE_BACKEND, gettext_noop("standalone backend"), NULL, false)
-PG_PROCTYPE(B_STARTUP, gettext_noop("startup"), StartupProcessMain, true)
-PG_PROCTYPE(B_WAL_RECEIVER, gettext_noop("walreceiver"), WalReceiverMain, true)
-PG_PROCTYPE(B_WAL_SENDER, gettext_noop("walsender"), NULL, true)
-PG_PROCTYPE(B_WAL_SUMMARIZER, gettext_noop("walsummarizer"), WalSummarizerMain, true)
-PG_PROCTYPE(B_WAL_WRITER, gettext_noop("walwriter"), WalWriterMain, true)
+/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */
+PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING)
+PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING)
+PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING)
+PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING)
+PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING)
+PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING)
+PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING)
+PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING)
+PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING)
+PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING)
+PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING)
+PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING)
+PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING)
+PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING)
+PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING)
+PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING)
+PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING)
+PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING)
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index f21ec37da89..98b0ba08c0c 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -295,7 +295,7 @@ extern PGDLLIMPORT bool log_duration;
 extern PGDLLIMPORT int log_parameter_max_length;
 extern PGDLLIMPORT int log_parameter_max_length_on_error;
 extern PGDLLIMPORT int log_min_error_statement;
-extern PGDLLIMPORT int log_min_messages;
+extern PGDLLIMPORT int log_min_messages[];
 extern PGDLLIMPORT int client_min_messages;
 extern PGDLLIMPORT int log_min_duration_sample;
 extern PGDLLIMPORT int log_min_duration_statement;
@@ -329,6 +329,9 @@ extern PGDLLIMPORT bool trace_sort;
 extern PGDLLIMPORT bool optimize_bounded_sort;
 #endif
 
+/* log_min_messages */
+extern PGDLLIMPORT const char *const log_min_messages_process_types[];
+
 /*
  * Declarations for options for enum values
  *
@@ -344,6 +347,7 @@ extern PGDLLIMPORT const struct config_enum_entry archive_mode_options[];
 extern PGDLLIMPORT const struct config_enum_entry dynamic_shared_memory_options[];
 extern PGDLLIMPORT const struct config_enum_entry io_method_options[];
 extern PGDLLIMPORT const struct config_enum_entry recovery_target_action_options[];
+extern PGDLLIMPORT const struct config_enum_entry server_message_level_options[];
 extern PGDLLIMPORT const struct config_enum_entry wal_level_options[];
 extern PGDLLIMPORT const struct config_enum_entry wal_sync_method_options[];
 
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index 82ac8646a8d..e6ffc96468f 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -174,5 +174,7 @@ extern void assign_wal_sync_method(int new_wal_sync_method, void *extra);
 extern bool check_synchronized_standby_slots(char **newval, void **extra,
 											 GucSource source);
 extern void assign_synchronized_standby_slots(const char *newval, void *extra);
+extern bool check_log_min_messages(char **newval, void **extra, GucSource source);
+extern void assign_log_min_messages(const char *newval, void *extra);
 
 #endif							/* GUC_HOOKS_H */
diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out
index d6fb879f500..3b8f44566b2 100644
--- a/src/test/regress/expected/guc.out
+++ b/src/test/regress/expected/guc.out
@@ -935,3 +935,57 @@ SELECT name FROM tab_settings_flags
 (0 rows)
 
 DROP TABLE tab_settings_flags;
+-- Test log_min_messages
+SET log_min_messages TO foo;  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "foo"
+DETAIL:  Unrecognized log level: "foo".
+SET log_min_messages TO fatal;
+SHOW log_min_messages;
+ log_min_messages 
+------------------
+ fatal
+(1 row)
+
+SET log_min_messages TO 'fatal';
+SHOW log_min_messages;
+ log_min_messages 
+------------------
+ fatal
+(1 row)
+
+SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1';  --fail
+ERROR:  invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1"
+DETAIL:  Generic log level was not defined.
+SET log_min_messages TO 'debug1, backend:error, fatal';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "debug1, backend:error, fatal"
+DETAIL:  Generic log level was already assigned.
+SET log_min_messages TO 'backend:error, debug1, backend:warning';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning"
+DETAIL:  Process type "backend" was already assigned.
+SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1"
+DETAIL:  Unrecognized process type: "foo".
+SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1"
+DETAIL:  Unrecognized log level: "bar".
+SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3';
+SHOW log_min_messages;
+                                        log_min_messages                                         
+-------------------------------------------------------------------------------------------------
+ backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3
+(1 row)
+
+SET log_min_messages TO 'warning, autovacuum:debug1';
+SHOW log_min_messages;
+      log_min_messages      
+----------------------------
+ warning, autovacuum:debug1
+(1 row)
+
+SET log_min_messages TO 'autovacuum:debug1, warning';
+SHOW log_min_messages;
+      log_min_messages      
+----------------------------
+ autovacuum:debug1, warning
+(1 row)
+
diff --git a/src/test/regress/sql/guc.sql b/src/test/regress/sql/guc.sql
index bafaf067e82..f7dfc909b42 100644
--- a/src/test/regress/sql/guc.sql
+++ b/src/test/regress/sql/guc.sql
@@ -377,3 +377,21 @@ SELECT name FROM tab_settings_flags
   WHERE no_reset AND NOT no_reset_all
   ORDER BY 1;
 DROP TABLE tab_settings_flags;
+
+-- Test log_min_messages
+SET log_min_messages TO foo;  -- fail
+SET log_min_messages TO fatal;
+SHOW log_min_messages;
+SET log_min_messages TO 'fatal';
+SHOW log_min_messages;
+SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1';  --fail
+SET log_min_messages TO 'debug1, backend:error, fatal';  -- fail
+SET log_min_messages TO 'backend:error, debug1, backend:warning';  -- fail
+SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1';  -- fail
+SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1';  -- fail
+SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3';
+SHOW log_min_messages;
+SET log_min_messages TO 'warning, autovacuum:debug1';
+SHOW log_min_messages;
+SET log_min_messages TO 'autovacuum:debug1, warning';
+SHOW log_min_messages;
-- 
2.47.3

  [text/x-diff] v7-0002-Assign-backend-type-earlier.patch (7.0K, ../../202512090853.hk6e6t56bq5z@alvherre.pgsql/3-v7-0002-Assign-backend-type-earlier.patch)
  download | inline diff:
From c92d8b66de44b965ce1460e61ed047d1ae9d6b44 Mon Sep 17 00:00:00 2001
From: Euler Taveira <euler@eulerto.com>
Date: Tue, 25 Nov 2025 21:09:11 -0300
Subject: [PATCH v7 2/3] Assign backend type earlier

Instead of assigning the backend type into the Main function of each
postmaster child, do it earlier (after fork()). The backend type is
already known by postmaster_child_launch() before it calls fork(). This
reduces the time frame that MyBackendType is correct. (This is important
for log_min_messages per backend type that relies on a known
MyBackendType to decide if it writes a log message or not.)
---
 src/backend/postmaster/autovacuum.c        | 2 --
 src/backend/postmaster/bgworker.c          | 1 -
 src/backend/postmaster/bgwriter.c          | 1 -
 src/backend/postmaster/checkpointer.c      | 1 -
 src/backend/postmaster/launch_backend.c    | 2 ++
 src/backend/postmaster/pgarch.c            | 1 -
 src/backend/postmaster/startup.c           | 1 -
 src/backend/postmaster/syslogger.c         | 1 -
 src/backend/postmaster/walsummarizer.c     | 1 -
 src/backend/postmaster/walwriter.c         | 1 -
 src/backend/replication/logical/slotsync.c | 2 --
 src/backend/replication/walreceiver.c      | 1 -
 src/backend/storage/aio/method_worker.c    | 1 -
 13 files changed, 2 insertions(+), 14 deletions(-)

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 1c38488f2cb..36c1911a3a8 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -388,7 +388,6 @@ AutoVacLauncherMain(const void *startup_data, size_t startup_data_len)
 		PostmasterContext = NULL;
 	}
 
-	MyBackendType = B_AUTOVAC_LAUNCHER;
 	init_ps_display(NULL);
 
 	ereport(DEBUG1,
@@ -1401,7 +1400,6 @@ AutoVacWorkerMain(const void *startup_data, size_t startup_data_len)
 		PostmasterContext = NULL;
 	}
 
-	MyBackendType = B_AUTOVAC_WORKER;
 	init_ps_display(NULL);
 
 	Assert(GetProcessingMode() == InitProcessing);
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 142a02eb5e9..260f051711e 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -741,7 +741,6 @@ BackgroundWorkerMain(const void *startup_data, size_t startup_data_len)
 	}
 
 	MyBgworkerEntry = worker;
-	MyBackendType = B_BG_WORKER;
 	init_ps_display(worker->bgw_name);
 
 	Assert(GetProcessingMode() == InitProcessing);
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index 72f5acceec7..4119758a72c 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -94,7 +94,6 @@ BackgroundWriterMain(const void *startup_data, size_t startup_data_len)
 
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_BG_WRITER;
 	AuxiliaryProcessMainCommon();
 
 	/*
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index afb3d6d8630..427f50752d4 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -199,7 +199,6 @@ CheckpointerMain(const void *startup_data, size_t startup_data_len)
 
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_CHECKPOINTER;
 	AuxiliaryProcessMainCommon();
 
 	CheckpointerShmem->checkpointer_pid = MyProcPid;
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index c2a044321d1..35420b8c9fb 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -224,6 +224,8 @@ postmaster_child_launch(BackendType child_type, int child_slot,
 	pid = fork_process();
 	if (pid == 0)				/* child */
 	{
+		MyBackendType = child_type;
+
 		/* Capture and transfer timings that may be needed for logging */
 		if (IsExternalConnectionBackend(child_type))
 		{
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index ce6b5299324..b760d1aed40 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -222,7 +222,6 @@ PgArchiverMain(const void *startup_data, size_t startup_data_len)
 {
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_ARCHIVER;
 	AuxiliaryProcessMainCommon();
 
 	/*
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index 27e86cf393f..13e86a2921e 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -217,7 +217,6 @@ StartupProcessMain(const void *startup_data, size_t startup_data_len)
 {
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_STARTUP;
 	AuxiliaryProcessMainCommon();
 
 	/* Arrange to clean up at startup process exit */
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index 50c2edec1f6..a378b15a6de 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -206,7 +206,6 @@ SysLoggerMain(const void *startup_data, size_t startup_data_len)
 
 	now = MyStartTime;
 
-	MyBackendType = B_LOGGER;
 	init_ps_display(NULL);
 
 	/*
diff --git a/src/backend/postmaster/walsummarizer.c b/src/backend/postmaster/walsummarizer.c
index c4a888a081c..ce497d8ec42 100644
--- a/src/backend/postmaster/walsummarizer.c
+++ b/src/backend/postmaster/walsummarizer.c
@@ -234,7 +234,6 @@ WalSummarizerMain(const void *startup_data, size_t startup_data_len)
 
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_WAL_SUMMARIZER;
 	AuxiliaryProcessMainCommon();
 
 	ereport(DEBUG1,
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index fd92c8b7a33..c1b4ddda555 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -94,7 +94,6 @@ WalWriterMain(const void *startup_data, size_t startup_data_len)
 
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_WAL_WRITER;
 	AuxiliaryProcessMainCommon();
 
 	/*
diff --git a/src/backend/replication/logical/slotsync.c b/src/backend/replication/logical/slotsync.c
index 7e3b4c4413e..364df1ad519 100644
--- a/src/backend/replication/logical/slotsync.c
+++ b/src/backend/replication/logical/slotsync.c
@@ -1436,8 +1436,6 @@ ReplSlotSyncWorkerMain(const void *startup_data, size_t startup_data_len)
 
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_SLOTSYNC_WORKER;
-
 	init_ps_display(NULL);
 
 	Assert(GetProcessingMode() == InitProcessing);
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index 3a94c9683a7..154fe9e0cb5 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -168,7 +168,6 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len)
 
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_WAL_RECEIVER;
 	AuxiliaryProcessMainCommon();
 
 	/*
diff --git a/src/backend/storage/aio/method_worker.c b/src/backend/storage/aio/method_worker.c
index 866a35abbd1..64caaa7b766 100644
--- a/src/backend/storage/aio/method_worker.c
+++ b/src/backend/storage/aio/method_worker.c
@@ -390,7 +390,6 @@ IoWorkerMain(const void *startup_data, size_t startup_data_len)
 	volatile int error_errno = 0;
 	char		cmd[128];
 
-	MyBackendType = B_IO_WORKER;
 	AuxiliaryProcessMainCommon();
 
 	pqsignal(SIGHUP, SignalHandlerForConfigReload);
-- 
2.47.3

  [text/x-diff] v7-0003-Fixup-assign-backend-type-earlier.patch (1.8K, ../../202512090853.hk6e6t56bq5z@alvherre.pgsql/4-v7-0003-Fixup-assign-backend-type-earlier.patch)
  download | inline diff:
From 8aa3f579cd0e070e7449883ecad64bbf182e21e5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Herrera?= <alvherre@kurilemu.de>
Date: Tue, 9 Dec 2025 09:53:01 +0100
Subject: [PATCH v7 3/3] Fixup: assign backend type earlier

---
 src/backend/postmaster/launch_backend.c | 3 +++
 src/backend/utils/error/elog.c          | 7 ++++++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 35420b8c9fb..96624e08b5a 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -588,6 +588,8 @@ SubPostmasterMain(int argc, char *argv[])
 	IsPostmasterEnvironment = true;
 	whereToSendOutput = DestNone;
 
+	MyBackendType = B_BACKEND;
+
 	/*
 	 * Capture the end of process creation for logging. We don't include the
 	 * time spent copying data from shared memory and setting up the backend.
@@ -611,6 +613,7 @@ SubPostmasterMain(int argc, char *argv[])
 		if (strcmp(child_process_kinds[idx].name, child_kind) == 0)
 		{
 			child_type = (BackendType) idx;
+			MyBackendType = child_type;
 			found = true;
 			break;
 		}
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 15d771a16c9..d7f4a44c5a7 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -2779,7 +2779,12 @@ get_backend_type_for_log(void)
 	if (MyProcPid == PostmasterPid)
 		backend_type_str = "postmaster";
 	else if (MyBackendType == B_BG_WORKER)
-		backend_type_str = MyBgworkerEntry->bgw_type;
+	{
+		if (MyBgworkerEntry && MyBgworkerEntry->bgw_type[0] != '\0')
+			backend_type_str = MyBgworkerEntry->bgw_type;
+		else
+			backend_type_str = "early bgworker";
+	}
 	else
 		backend_type_str = GetBackendTypeDesc(MyBackendType);
 
-- 
2.47.3

^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-12-09 16:30  Alvaro Herrera <alvherre@alvh.no-ip.org>
  parent: Alvaro Herrera <alvherre@alvh.no-ip.org>
  1 sibling, 1 reply; 58+ messages in thread

From: Alvaro Herrera @ 2025-12-09 16:30 UTC (permalink / raw)
  To: Euler Taveira <euler@eulerto.com>; +Cc: japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org, Chao Li <li.evan.chao@gmail.com>

BTW another thing I realized while looking this over, is that we quite
uselessly transform the integer backend type to a string, pass it as a
string using the --forkchild= argument to the child process, then parse
the string back to an int to use as an array index.  It would be much
easier to just use the integer value everywhere, as the attached shows.

-- 
Álvaro Herrera               48°01'N 7°57'E  —  https://www.EnterpriseDB.com/
"I love the Postgres community. It's all about doing things _properly_. :-)"
(David Garamond)
From 4c2e63bc3c78b567e83a4d6d0df60ddeb0f51a3c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Herrera?= <alvherre@kurilemu.de>
Date: Tue, 9 Dec 2025 15:29:43 +0100
Subject: [PATCH] Use integers not strings to identify backend type

---
 src/backend/postmaster/launch_backend.c | 32 +++++++++----------------
 1 file changed, 11 insertions(+), 21 deletions(-)

diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 976638a58ac..3606a569758 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -162,7 +162,7 @@ static bool save_backend_variables(BackendParameters *param, int child_slot,
 #endif
 								   const void *startup_data, size_t startup_data_len);
 
-static pid_t internal_forkexec(const char *child_kind, int child_slot,
+static pid_t internal_forkexec(int child_kind, int child_slot,
 							   const void *startup_data, size_t startup_data_len,
 							   ClientSocket *client_sock);
 
@@ -217,7 +217,7 @@ postmaster_child_launch(BackendType child_type, int child_slot,
 		((BackendStartupData *) startup_data)->fork_started = GetCurrentTimestamp();
 
 #ifdef EXEC_BACKEND
-	pid = internal_forkexec(child_process_kinds[child_type].name, child_slot,
+	pid = internal_forkexec(child_type, child_slot,
 							startup_data, startup_data_len, client_sock);
 	/* the child process will arrive in SubPostmasterMain */
 #else							/* !EXEC_BACKEND */
@@ -282,7 +282,7 @@ postmaster_child_launch(BackendType child_type, int child_slot,
  * - fork():s, and then exec():s the child process
  */
 static pid_t
-internal_forkexec(const char *child_kind, int child_slot,
+internal_forkexec(int child_kind, int child_slot,
 				  const void *startup_data, size_t startup_data_len, ClientSocket *client_sock)
 {
 	static unsigned long tmpBackendFileNum = 0;
@@ -358,7 +358,7 @@ internal_forkexec(const char *child_kind, int child_slot,
 
 	/* set up argv properly */
 	argv[0] = "postgres";
-	snprintf(forkav, MAXPGPATH, "--forkchild=%s", child_kind);
+	snprintf(forkav, MAXPGPATH, "--forkchild=%d", child_kind);
 	argv[1] = forkav;
 	/* Insert temp file name after --forkchild argument */
 	argv[2] = tmpfilename;
@@ -392,7 +392,7 @@ internal_forkexec(const char *child_kind, int child_slot,
  *	 file is complete.
  */
 static pid_t
-internal_forkexec(const char *child_kind, int child_slot,
+internal_forkexec(int child_kind, int child_slot,
 				  const void *startup_data, size_t startup_data_len, ClientSocket *client_sock)
 {
 	int			retry_count = 0;
@@ -444,7 +444,7 @@ retry:
 #else
 	sprintf(paramHandleStr, "%lu", (DWORD) paramHandle);
 #endif
-	l = snprintf(cmdLine, sizeof(cmdLine) - 1, "\"%s\" --forkchild=\"%s\" %s",
+	l = snprintf(cmdLine, sizeof(cmdLine) - 1, "\"%s\" --forkchild=%d %s",
 				 postgres_exec_path, child_kind, paramHandleStr);
 	if (l >= sizeof(cmdLine))
 	{
@@ -567,8 +567,8 @@ retry:
  *			to what it would be if we'd simply forked on Unix, and then
  *			dispatch to the appropriate place.
  *
- * The first two command line arguments are expected to be "--forkchild=<name>",
- * where <name> indicates which postmaster child we are to become, and
+ * The first two command line arguments are expected to be "--forkchild=<kind>",
+ * where <kind> indicates which process type we are to become, and
  * the name of a variables file that we can read to load data that would
  * have been inherited by fork() on Unix.
  */
@@ -579,7 +579,6 @@ SubPostmasterMain(int argc, char *argv[])
 	size_t		startup_data_len;
 	char	   *child_kind;
 	BackendType child_type;
-	bool		found = false;
 	TimestampTz fork_end;
 
 	/* In EXEC_BACKEND case we will not have inherited these settings */
@@ -599,21 +598,12 @@ SubPostmasterMain(int argc, char *argv[])
 	if (argc != 3)
 		elog(FATAL, "invalid subpostmaster invocation");
 
-	/* Find the entry in child_process_kinds */
+	/* Parse the --forkchild argument to find our process type */
 	if (strncmp(argv[1], "--forkchild=", 12) != 0)
 		elog(FATAL, "invalid subpostmaster invocation (--forkchild argument missing)");
 	child_kind = argv[1] + 12;
-	found = false;
-	for (int idx = 0; idx < lengthof(child_process_kinds); idx++)
-	{
-		if (strcmp(child_process_kinds[idx].name, child_kind) == 0)
-		{
-			child_type = (BackendType) idx;
-			found = true;
-			break;
-		}
-	}
-	if (!found)
+	child_type = (BackendType) atoi(child_kind);
+	if (child_type <= B_INVALID || child_type > BACKEND_NUM_TYPES)
 		elog(ERROR, "unknown child kind %s", child_kind);
 
 	/* Read in the variables file */
-- 
2.47.3

Attachments:

  [text/plain] 0001-Use-integers-not-strings-to-identify-backend-type.patch.nocfbot (4.5K, ../../202512090935.k3xrtr44hxkn@alvherre.pgsql/2-0001-Use-integers-not-strings-to-identify-backend-type.patch.nocfbot)
  download | inline diff:
From 4c2e63bc3c78b567e83a4d6d0df60ddeb0f51a3c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Herrera?= <alvherre@kurilemu.de>
Date: Tue, 9 Dec 2025 15:29:43 +0100
Subject: [PATCH] Use integers not strings to identify backend type

---
 src/backend/postmaster/launch_backend.c | 32 +++++++++----------------
 1 file changed, 11 insertions(+), 21 deletions(-)

diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 976638a58ac..3606a569758 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -162,7 +162,7 @@ static bool save_backend_variables(BackendParameters *param, int child_slot,
 #endif
 								   const void *startup_data, size_t startup_data_len);
 
-static pid_t internal_forkexec(const char *child_kind, int child_slot,
+static pid_t internal_forkexec(int child_kind, int child_slot,
 							   const void *startup_data, size_t startup_data_len,
 							   ClientSocket *client_sock);
 
@@ -217,7 +217,7 @@ postmaster_child_launch(BackendType child_type, int child_slot,
 		((BackendStartupData *) startup_data)->fork_started = GetCurrentTimestamp();
 
 #ifdef EXEC_BACKEND
-	pid = internal_forkexec(child_process_kinds[child_type].name, child_slot,
+	pid = internal_forkexec(child_type, child_slot,
 							startup_data, startup_data_len, client_sock);
 	/* the child process will arrive in SubPostmasterMain */
 #else							/* !EXEC_BACKEND */
@@ -282,7 +282,7 @@ postmaster_child_launch(BackendType child_type, int child_slot,
  * - fork():s, and then exec():s the child process
  */
 static pid_t
-internal_forkexec(const char *child_kind, int child_slot,
+internal_forkexec(int child_kind, int child_slot,
 				  const void *startup_data, size_t startup_data_len, ClientSocket *client_sock)
 {
 	static unsigned long tmpBackendFileNum = 0;
@@ -358,7 +358,7 @@ internal_forkexec(const char *child_kind, int child_slot,
 
 	/* set up argv properly */
 	argv[0] = "postgres";
-	snprintf(forkav, MAXPGPATH, "--forkchild=%s", child_kind);
+	snprintf(forkav, MAXPGPATH, "--forkchild=%d", child_kind);
 	argv[1] = forkav;
 	/* Insert temp file name after --forkchild argument */
 	argv[2] = tmpfilename;
@@ -392,7 +392,7 @@ internal_forkexec(const char *child_kind, int child_slot,
  *	 file is complete.
  */
 static pid_t
-internal_forkexec(const char *child_kind, int child_slot,
+internal_forkexec(int child_kind, int child_slot,
 				  const void *startup_data, size_t startup_data_len, ClientSocket *client_sock)
 {
 	int			retry_count = 0;
@@ -444,7 +444,7 @@ retry:
 #else
 	sprintf(paramHandleStr, "%lu", (DWORD) paramHandle);
 #endif
-	l = snprintf(cmdLine, sizeof(cmdLine) - 1, "\"%s\" --forkchild=\"%s\" %s",
+	l = snprintf(cmdLine, sizeof(cmdLine) - 1, "\"%s\" --forkchild=%d %s",
 				 postgres_exec_path, child_kind, paramHandleStr);
 	if (l >= sizeof(cmdLine))
 	{
@@ -567,8 +567,8 @@ retry:
  *			to what it would be if we'd simply forked on Unix, and then
  *			dispatch to the appropriate place.
  *
- * The first two command line arguments are expected to be "--forkchild=<name>",
- * where <name> indicates which postmaster child we are to become, and
+ * The first two command line arguments are expected to be "--forkchild=<kind>",
+ * where <kind> indicates which process type we are to become, and
  * the name of a variables file that we can read to load data that would
  * have been inherited by fork() on Unix.
  */
@@ -579,7 +579,6 @@ SubPostmasterMain(int argc, char *argv[])
 	size_t		startup_data_len;
 	char	   *child_kind;
 	BackendType child_type;
-	bool		found = false;
 	TimestampTz fork_end;
 
 	/* In EXEC_BACKEND case we will not have inherited these settings */
@@ -599,21 +598,12 @@ SubPostmasterMain(int argc, char *argv[])
 	if (argc != 3)
 		elog(FATAL, "invalid subpostmaster invocation");
 
-	/* Find the entry in child_process_kinds */
+	/* Parse the --forkchild argument to find our process type */
 	if (strncmp(argv[1], "--forkchild=", 12) != 0)
 		elog(FATAL, "invalid subpostmaster invocation (--forkchild argument missing)");
 	child_kind = argv[1] + 12;
-	found = false;
-	for (int idx = 0; idx < lengthof(child_process_kinds); idx++)
-	{
-		if (strcmp(child_process_kinds[idx].name, child_kind) == 0)
-		{
-			child_type = (BackendType) idx;
-			found = true;
-			break;
-		}
-	}
-	if (!found)
+	child_type = (BackendType) atoi(child_kind);
+	if (child_type <= B_INVALID || child_type > BACKEND_NUM_TYPES)
 		elog(ERROR, "unknown child kind %s", child_kind);
 
 	/* Read in the variables file */
-- 
2.47.3

^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-12-09 18:24  Alvaro Herrera <alvherre@alvh.no-ip.org>
  parent: Alvaro Herrera <alvherre@alvh.no-ip.org>
  1 sibling, 2 replies; 58+ messages in thread

From: Alvaro Herrera @ 2025-12-09 18:24 UTC (permalink / raw)
  To: Euler Taveira <euler@eulerto.com>; +Cc: japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org, Chao Li <li.evan.chao@gmail.com>

On 2025-Dec-09, Alvaro Herrera wrote:

> So here's your v6 again with those fixes as 0003 -- let's see what CI
> thinks of this.  I haven't looked at your doc changes yet.

This passed CI, so I have marked it as Ready for Committer.  Further
comments are still welcome, of course, but if there are none, I intend
to get it committed in a few days.


I'm not really happy with our usage of the translatable description
field for things such as %b in log_line_prefix or the ps display; I
think we should use the shorter names there instead.  Otherwise, you end
up with log lines that look something like this (visible in a server
with %b in log_line_prefix, which the TAP tests as well as pg_regress
have):

  2025-12-08 21:38:04.304 CET autovacuum launcher[2452437] DEBUG: autovacuum launcher started

where the bit before the PID is marked for translation.  I think it
should rather be

  2025-12-08 21:38:04.304 CET autovacuum[2452437] DEBUG: autovacuum launcher started

where that name (the same we'll use in log_min_messages) is not
translated.

However, this issue is rather independent of the patch in this thread,
so I'm going to discuss it in another thread; the name string though is
added by this patch.

-- 
Álvaro Herrera        Breisgau, Deutschland  —  https://www.EnterpriseDB.com/
"Java is clearly an example of money oriented programming"  (A. Stepanov)





^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-12-10 02:00  Chao Li <li.evan.chao@gmail.com>
  parent: Alvaro Herrera <alvherre@alvh.no-ip.org>
  1 sibling, 1 reply; 58+ messages in thread

From: Chao Li @ 2025-12-10 02:00 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: Euler Taveira <euler@eulerto.com>; japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org



> On Dec 10, 2025, at 02:24, Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:
> 
> On 2025-Dec-09, Alvaro Herrera wrote:
> 
>> So here's your v6 again with those fixes as 0003 -- let's see what CI
>> thinks of this.  I haven't looked at your doc changes yet.
> 
> This passed CI, so I have marked it as Ready for Committer.  Further
> comments are still welcome, of course, but if there are none, I intend
> to get it committed in a few days.
> 
> 
> I'm not really happy with our usage of the translatable description
> field for things such as %b in log_line_prefix or the ps display; I
> think we should use the shorter names there instead.  Otherwise, you end
> up with log lines that look something like this (visible in a server
> with %b in log_line_prefix, which the TAP tests as well as pg_regress
> have):
> 
>  2025-12-08 21:38:04.304 CET autovacuum launcher[2452437] DEBUG: autovacuum launcher started
> 
> where the bit before the PID is marked for translation.  I think it
> should rather be
> 
>  2025-12-08 21:38:04.304 CET autovacuum[2452437] DEBUG: autovacuum launcher started
> 
> where that name (the same we'll use in log_min_messages) is not
> translated.
> 
> However, this issue is rather independent of the patch in this thread,
> so I'm going to discuss it in another thread; the name string though is
> added by this patch.
> 
> -- 
> Álvaro Herrera        Breisgau, Deutschland  —  https://www.EnterpriseDB.com/
> "Java is clearly an example of money oriented programming"  (A. Stepanov)

Overall the patch is solid, I just have a couple of suggestions:

1 - user experience
```
evantest=# show log_min_messages;
  log_min_messages
---------------------
 warning,backend:log
(1 row)
```

Now “show log_min_messages” prints the raw string the user set, in above example, there is not a white-space between the two log levels, and “show” result doesn’t have a white-space between the two log levels either. IMO, “SHOW log_min_messages” should display a stable result, in other words, say “fatal, backend:log” and “backend:log, fatal” should show the same result as they are actually meaning the same. So, I would suggest normalize the raw string: put the general level in the first place and sort others by process type, then SHOW returns the normalized string.

2 - refactoring for 0001
```
+		sep = strchr(tok, ':');
+		if (sep == NULL)
+		{
+			bool		found = false;
+
+			/* Reject duplicates for generic log level. */
+			if (genericlevel != -1)
+			{
+				GUC_check_errdetail("Generic log level was already assigned.");
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			/* Is the log level valid? */
+			for (entry = server_message_level_options; entry && entry->name; entry++)
+			{
+				if (pg_strcasecmp(entry->name, tok) == 0)
+				{
+					genericlevel = entry->val;
+					found = true;
+					break;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized log level: \"%s\".", tok);
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+		}
+		else
+		{
+			char	   *loglevel;
+			char	   *ptype;
+			bool		found = false;
+
+			ptype = guc_malloc(LOG, (sep - tok) + 1);
+			if (!ptype)
+			{
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+			memcpy(ptype, tok, sep - tok);
+			ptype[sep - tok] = '\0';
+			loglevel = sep + 1;
+
+			/* Is the log level valid? */
+			for (entry = server_message_level_options; entry && entry->name; entry++)
+			{
+				if (pg_strcasecmp(entry->name, loglevel) == 0)
+				{
+					found = true;
+					break;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel);
+				guc_free(ptype);
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
```

In the “if” and “else” clauses, there are duplicate code to valid log levels. We should refactor the code to avoid the duplication. For example, pull up “loglevel” to the “for” loop level, then we can valid it after the “if-else”.

Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/









^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-12-11 01:57  Euler Taveira <euler@eulerto.com>
  parent: Chao Li <li.evan.chao@gmail.com>
  0 siblings, 1 reply; 58+ messages in thread

From: Euler Taveira @ 2025-12-11 01:57 UTC (permalink / raw)
  To: Chao Li <li.evan.chao@gmail.com>; Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org

On Tue, Dec 9, 2025, at 11:00 PM, Chao Li wrote:
> Now “show log_min_messages” prints the raw string the user set, in 
> above example, there is not a white-space between the two log levels, 
> and “show” result doesn’t have a white-space between the two log levels 
> either. IMO, “SHOW log_min_messages” should display a stable result, in 
> other words, say “fatal, backend:log” and “backend:log, fatal” should 
> show the same result as they are actually meaning the same. So, I would 
> suggest normalize the raw string: put the general level in the first 
> place and sort others by process type, then SHOW returns the normalized 
> string.
>

I thought about it but leave it alone because (a) it would increase this patch
footprint and (b) the input might be different from the output. I could also be
done in another patch but under reflection an unstable output can break tests
or whatever uses the SHOW log_min_messages output. I thought this change would
require a new show_log_min_messages to manipulate the input again but we can
reassign the GUC value after sorting the existing list and creating a new string
list.

> In the “if” and “else” clauses, there are duplicate code to valid log 
> levels. We should refactor the code to avoid the duplication. For 
> example, pull up “loglevel” to the “for” loop level, then we can valid 
> it after the “if-else”.
>

The for loop is duplicate but if you create a separate function for it but the
result is:

 src/backend/commands/variable.c | 43 ++++++++++++++++++++++---------------------
 1 file changed, 22 insertions(+), 21 deletions(-)


I'll post a patch in a couple of hours after spend more time in it.


-- 
Euler Taveira
EDB   https://www.enterprisedb.com/





^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2025-12-11 02:11  Chao Li <li.evan.chao@gmail.com>
  parent: Euler Taveira <euler@eulerto.com>
  0 siblings, 0 replies; 58+ messages in thread

From: Chao Li @ 2025-12-11 02:11 UTC (permalink / raw)
  To: Euler Taveira <euler@eulerto.com>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org



> On Dec 11, 2025, at 09:57, Euler Taveira <euler@eulerto.com> wrote:
> 
> 
>> In the “if” and “else” clauses, there are duplicate code to valid log 
>> levels. We should refactor the code to avoid the duplication. For 
>> example, pull up “loglevel” to the “for” loop level, then we can valid 
>> it after the “if-else”.
>> 
> 
> The for loop is duplicate but if you create a separate function for it but the
> result is:
> 
> src/backend/commands/variable.c | 43 ++++++++++++++++++++++---------------------
> 1 file changed, 22 insertions(+), 21 deletions(-)
> 
> 

I don’t think we need to add a separate function. We can use ‘if-else” to parse log level, then verify it after “if-else”.

Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/









^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2026-01-15 13:50  Euler Taveira <euler@eulerto.com>
  parent: Alvaro Herrera <alvherre@alvh.no-ip.org>
  0 siblings, 1 reply; 58+ messages in thread

From: Euler Taveira @ 2026-01-15 13:50 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org, "Chao Li" <li.evan.chao@gmail.com>

On Tue, Dec 9, 2025, at 1:30 PM, Alvaro Herrera wrote:
> BTW another thing I realized while looking this over, is that we quite
> uselessly transform the integer backend type to a string, pass it as a
> string using the --forkchild= argument to the child process, then parse
> the string back to an int to use as an array index.  It would be much
> easier to just use the integer value everywhere, as the attached shows.
>

It is a good simplification. There is just one oversight.

+   child_type = (BackendType) atoi(child_kind);
+   if (child_type <= B_INVALID || child_type > BACKEND_NUM_TYPES)
        elog(ERROR, "unknown child kind %s", child_kind);

It should be BACKEND_NUM_TYPES - 1.


-- 
Euler Taveira
EDB   https://www.enterprisedb.com/





^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2026-01-15 16:07  Euler Taveira <euler@eulerto.com>
  parent: Alvaro Herrera <alvherre@alvh.no-ip.org>
  1 sibling, 4 replies; 58+ messages in thread

From: Euler Taveira @ 2026-01-15 16:07 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org, "Chao Li" <li.evan.chao@gmail.com>

On Tue, Dec 9, 2025, at 3:24 PM, Alvaro Herrera wrote:
> On 2025-Dec-09, Alvaro Herrera wrote:
>
>> So here's your v6 again with those fixes as 0003 -- let's see what CI
>> thinks of this.  I haven't looked at your doc changes yet.
>
> This passed CI, so I have marked it as Ready for Committer.  Further
> comments are still welcome, of course, but if there are none, I intend
> to get it committed in a few days.
>

I took another look after Chao Li comments [1]. I created the 0003 patch
that does the sort as suggested. I think it is good to be consistent but
I'm fine if we decided the additional code is not worth. The 32 in the
MAX_LMM_STR_LEN is arbitrary but it is based on the size of the largest
element in the list ("dead-end client backend:warning"). I didn't take
into account the comma and space between elements but it is not
necessary since other elements are smaller than the largest one.
I didn't implement the 2nd suggestion.

I also merged Alvaro's fix to 0002. The v8 is attached.

I didn't change the commit message but if 0003 is merged into 0001 then
it should mention that

8<--------------------------------------------------------------------8<
The SHOW command presents well-formatted list sorted by process type and
the generic log level is the first element list. It improves readability
and has a clear indentation.
8<--------------------------------------------------------------------8<

Do we really need a different backend type in this case? For background
workers the description is "background worker". Shoundn't it use the
same description for this edge case too?

-       backend_type_str = MyBgworkerEntry->bgw_type;
+   {
+       if (MyBgworkerEntry && MyBgworkerEntry->bgw_type[0] != '\0')
+           backend_type_str = MyBgworkerEntry->bgw_type;
+       else
+           backend_type_str = "early bgworker";
+   }

I also noticed that commit 18d67a8d7d30 forgot to add gettext_noop to
the get_backend_type_for_log function. It should be consistent with
GetBackendTypeDesc() return.


[1] https://postgr.es/m/1130A10B-E7AE-49F3-9E13-2CD69B3F9DFD@gmail.com


-- 
Euler Taveira
EDB   https://www.enterprisedb.com/

Attachments:

  [text/x-patch] v8-0001-log_min_messages-per-process-type.patch (26.3K, ../../f20103f2-d960-4206-9969-c4a936524ab1@app.fastmail.com/2-v8-0001-log_min_messages-per-process-type.patch)
  download | inline diff:
From 7b327eacb382c7efe7c7fcab309bb04c0043507d Mon Sep 17 00:00:00 2001
From: Euler Taveira <euler@eulerto.com>
Date: Tue, 4 Nov 2025 17:18:20 -0300
Subject: [PATCH v8 1/3] log_min_messages per process type

Change log_min_messages from a single element to a comma-separated list
of elements. Each element is type:level. The types are archiver,
autovacuum (includes launcher and workers), backend, bgworker, bgwriter,
checkpointer, ioworker, postmaster, syslogger, slotsyncworker, startup,
walreceiver, walsender, walsummarizer and walwriter. A single log level
should be part of this list and it is applied as a final step to the
process types that were not informed.

The old syntax (a single log level) is still accepted for backward
compatibility. In this case, it means the informed log level is applied
for all process types. The default log level is the same (WARNING) for
all process types.
---
 doc/src/sgml/config.sgml                      |  42 +++-
 src/backend/commands/extension.c              |   2 +-
 src/backend/commands/variable.c               | 208 ++++++++++++++++++
 src/backend/postmaster/launch_backend.c       |   2 +-
 src/backend/utils/error/elog.c                |   2 +-
 src/backend/utils/init/miscinit.c             |   2 +-
 src/backend/utils/misc/guc_parameters.dat     |  10 +-
 src/backend/utils/misc/guc_tables.c           |  21 +-
 src/backend/utils/misc/postgresql.conf.sample |   2 +
 src/include/postmaster/proctypelist.h         |  42 ++--
 src/include/utils/guc.h                       |   6 +-
 src/include/utils/guc_hooks.h                 |   2 +
 src/test/regress/expected/guc.out             |  54 +++++
 src/test/regress/sql/guc.sql                  |  18 ++
 14 files changed, 372 insertions(+), 41 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 0fad34da6eb..4df87e1bc52 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -7083,7 +7083,7 @@ local0.*    /var/log/postgresql
      <variablelist>
 
      <varlistentry id="guc-log-min-messages" xreflabel="log_min_messages">
-      <term><varname>log_min_messages</varname> (<type>enum</type>)
+      <term><varname>log_min_messages</varname> (<type>string</type>)
       <indexterm>
        <primary><varname>log_min_messages</varname> configuration parameter</primary>
       </indexterm>
@@ -7092,14 +7092,38 @@ local0.*    /var/log/postgresql
        <para>
         Controls which <link linkend="runtime-config-severity-levels">message
         levels</link> are written to the server log.
-        Valid values are <literal>DEBUG5</literal>, <literal>DEBUG4</literal>,
-        <literal>DEBUG3</literal>, <literal>DEBUG2</literal>, <literal>DEBUG1</literal>,
-        <literal>INFO</literal>, <literal>NOTICE</literal>, <literal>WARNING</literal>,
-        <literal>ERROR</literal>, <literal>LOG</literal>, <literal>FATAL</literal>, and
-        <literal>PANIC</literal>.  Each level includes all the levels that
-        follow it.  The later the level, the fewer messages are sent
-        to the log.  The default is <literal>WARNING</literal>.  Note that
-        <literal>LOG</literal> has a different rank here than in
+        Valid values are a comma-separated list of <literal>type:level</literal>
+        and a single <literal>level</literal>. The list allows it to use
+        different levels per process type. Only the single <literal>level</literal>
+        is mandatory (order does not matter) and it is assigned to the process
+        types that are not specified in the list.
+        Valid process types are listed in the table below, each corresponding to
+        either postmaster, an auxiliary process type or a backend.
+        <simplelist type="vert" columns="4">
+         <member><literal>archiver</literal></member>
+         <member><literal>autovacuum</literal></member>
+         <member><literal>backend</literal></member>
+         <member><literal>bgworker</literal></member>
+         <member><literal>bgwriter</literal></member>
+         <member><literal>checkpointer</literal></member>
+         <member><literal>ioworker</literal></member>
+         <member><literal>postmaster</literal></member>
+         <member><literal>syslogger</literal></member>
+         <member><literal>slotsyncworker</literal></member>
+         <member><literal>startup</literal></member>
+         <member><literal>walreceiver</literal></member>
+         <member><literal>walsender</literal></member>
+         <member><literal>walsummarizer</literal></member>
+         <member><literal>walwriter</literal></member>
+        </simplelist>
+        Valid <literal>level</literal> values are <literal>DEBUG5</literal>,
+        <literal>DEBUG4</literal>, <literal>DEBUG3</literal>, <literal>DEBUG2</literal>,
+        <literal>DEBUG1</literal>, <literal>INFO</literal>, <literal>NOTICE</literal>,
+        <literal>WARNING</literal>, <literal>ERROR</literal>, <literal>LOG</literal>,
+        <literal>FATAL</literal>, and <literal>PANIC</literal>.  Each level includes
+        all the levels that follow it.  The later the level, the fewer messages are sent
+        to the log.  The default is <literal>WARNING</literal> for all process types.
+        Note that <literal>LOG</literal> has a different rank here than in
         <xref linkend="guc-client-min-messages"/>.
         Only superusers and users with the appropriate <literal>SET</literal>
         privilege can change this setting.
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 596105ee078..688f1874f31 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1191,7 +1191,7 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
 		(void) set_config_option("client_min_messages", "warning",
 								 PGC_USERSET, PGC_S_SESSION,
 								 GUC_ACTION_SAVE, true, 0, false);
-	if (log_min_messages < WARNING)
+	if (log_min_messages[MyBackendType] < WARNING)
 		(void) set_config_option_ext("log_min_messages", "warning",
 									 PGC_SUSET, PGC_S_SESSION,
 									 BOOTSTRAP_SUPERUSERID,
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index bda932d4992..f4f5d4c367c 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -34,6 +34,7 @@
 #include "utils/backend_status.h"
 #include "utils/datetime.h"
 #include "utils/fmgrprotos.h"
+#include "utils/guc.h"
 #include "utils/guc_hooks.h"
 #include "utils/snapmgr.h"
 #include "utils/syscache.h"
@@ -1257,3 +1258,210 @@ check_ssl(bool *newval, void **extra, GucSource source)
 #endif
 	return true;
 }
+
+/*
+ * GUC check_hook for log_min_messages
+ *
+ * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE
+ * is log_min_messages_process_types.  LEVEL is server_message_level_options. A
+ * single LEVEL element should be part of this list and it is applied as a
+ * final step to the process types that are not specified. For backward
+ * compatibility, the old syntax is still accepted and it means to apply the
+ * LEVEL for all process types.
+ */
+bool
+check_log_min_messages(char **newval, void **extra, GucSource source)
+{
+	char	   *rawstring;
+	List	   *elemlist;
+	ListCell   *l;
+	int			newlevel[BACKEND_NUM_TYPES];
+	bool		assigned[BACKEND_NUM_TYPES];
+	int			genericlevel = -1;	/* -1 means not assigned */
+
+	/* Initialize the array. */
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+	{
+		newlevel[i] = WARNING;
+		assigned[i] = false;
+	}
+
+	/* Need a modifiable copy of string. */
+	rawstring = guc_strdup(LOG, *newval);
+
+	/* Parse string into list of identifiers. */
+	if (!SplitGUCList(rawstring, ',', &elemlist))
+	{
+		/* syntax error in list */
+		GUC_check_errdetail("List syntax is invalid.");
+		list_free(elemlist);
+		guc_free(rawstring);
+		return false;
+	}
+
+	/* Validate and assign log level and process type. */
+	foreach(l, elemlist)
+	{
+		char	   *tok = (char *) lfirst(l);
+		char	   *sep;
+		const struct config_enum_entry *entry;
+
+		/*
+		 * Check whether there is a process type following the log level. If
+		 * there is no separator, it means this is the generic log level. The
+		 * generic log level will be assigned to the process types that were
+		 * not informed.
+		 */
+		sep = strchr(tok, ':');
+		if (sep == NULL)
+		{
+			bool		found = false;
+
+			/* Reject duplicates for generic log level. */
+			if (genericlevel != -1)
+			{
+				GUC_check_errdetail("Generic log level was already assigned.");
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			/* Is the log level valid? */
+			for (entry = server_message_level_options; entry && entry->name; entry++)
+			{
+				if (pg_strcasecmp(entry->name, tok) == 0)
+				{
+					genericlevel = entry->val;
+					found = true;
+					break;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized log level: \"%s\".", tok);
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+		}
+		else
+		{
+			char	   *loglevel;
+			char	   *ptype;
+			bool		found = false;
+
+			ptype = guc_malloc(LOG, (sep - tok) + 1);
+			if (!ptype)
+			{
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+			memcpy(ptype, tok, sep - tok);
+			ptype[sep - tok] = '\0';
+			loglevel = sep + 1;
+
+			/* Is the log level valid? */
+			for (entry = server_message_level_options; entry && entry->name; entry++)
+			{
+				if (pg_strcasecmp(entry->name, loglevel) == 0)
+				{
+					found = true;
+					break;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel);
+				guc_free(ptype);
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			/*
+			 * Is the process type name valid? There might be multiple entries
+			 * per process type, don't bail out because it can assign the
+			 * value for multiple entries.
+			 */
+			found = false;
+			for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+			{
+				if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0)
+				{
+					/* Reject duplicates for a process type. */
+					if (assigned[i])
+					{
+						GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype);
+						guc_free(ptype);
+						guc_free(rawstring);
+						list_free(elemlist);
+						return false;
+					}
+
+					newlevel[i] = entry->val;
+					assigned[i] = true;
+					found = true;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype);
+				guc_free(ptype);
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			guc_free(ptype);
+		}
+	}
+
+	/*
+	 * The generic log level must be specified. It is the fallback value.
+	 */
+	if (genericlevel == -1)
+	{
+		GUC_check_errdetail("Generic log level was not defined.");
+		guc_free(rawstring);
+		list_free(elemlist);
+		return false;
+	}
+
+	/*
+	 * Apply the generic log level after all of the specific process types
+	 * have been assigned. Hence, it doesn't matter the order you specify the
+	 * generic log level, the final result will be the same.
+	 */
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+	{
+		if (!assigned[i])
+			newlevel[i] = genericlevel;
+	}
+
+	guc_free(rawstring);
+	list_free(elemlist);
+
+	/*
+	 * Pass back data for assign_log_min_messages to use.
+	 */
+	*extra = guc_malloc(LOG, BACKEND_NUM_TYPES * sizeof(int));
+	if (!*extra)
+		return false;
+	memcpy(*extra, newlevel, BACKEND_NUM_TYPES * sizeof(int));
+
+	return true;
+}
+
+/*
+ * GUC assign_hook for log_min_messages
+ */
+void
+assign_log_min_messages(const char *newval, void *extra)
+{
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+		log_min_messages[i] = ((int *) extra)[i];
+}
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index eb91503522f..57303e1731b 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -179,7 +179,7 @@ typedef struct
 } child_process_kind;
 
 static child_process_kind child_process_kinds[] = {
-#define PG_PROCTYPE(bktype, description, main_func, shmem_attach) \
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
 	[bktype] = {description, main_func, shmem_attach},
 #include "postmaster/proctypelist.h"
 #undef PG_PROCTYPE
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index aa530d3685e..59975309a4e 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -235,7 +235,7 @@ is_log_level_output(int elevel, int log_min_level)
 static inline bool
 should_output_to_server(int elevel)
 {
-	return is_log_level_output(elevel, log_min_messages);
+	return is_log_level_output(elevel, log_min_messages[MyBackendType]);
 }
 
 /*
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index 563f20374ff..efba20fa8b9 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType)
 
 	switch (backendType)
 	{
-#define PG_PROCTYPE(bktype, description, main_func, shmem_attach)	\
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages)	\
 		case bktype: backendDesc = description; break;
 #include "postmaster/proctypelist.h"
 #undef PG_PROCTYPE
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index 7c60b125564..81d76133ce4 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -1692,12 +1692,14 @@
   options => 'server_message_level_options',
 },
 
-{ name => 'log_min_messages', type => 'enum', context => 'PGC_SUSET', group => 'LOGGING_WHEN',
+{ name => 'log_min_messages', type => 'string', context => 'PGC_SUSET', group => 'LOGGING_WHEN',
   short_desc => 'Sets the message levels that are logged.',
   long_desc => 'Each level includes all the levels that follow it. The later the level, the fewer messages are sent.',
-  variable => 'log_min_messages',
-  boot_val => 'WARNING',
-  options => 'server_message_level_options',
+  flags => 'GUC_LIST_INPUT',
+  variable => 'log_min_messages_string',
+  boot_val => '"WARNING"',
+  check_hook => 'check_log_min_messages',
+  assign_hook => 'assign_log_min_messages',
 },
 
 { name => 'log_parameter_max_length', type => 'int', context => 'PGC_SUSET', group => 'LOGGING_WHAT',
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 73ff6ad0a32..d2a98007e9b 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -146,7 +146,7 @@ static const struct config_enum_entry client_message_level_options[] = {
 	{NULL, 0, false}
 };
 
-static const struct config_enum_entry server_message_level_options[] = {
+const struct config_enum_entry server_message_level_options[] = {
 	{"debug5", DEBUG5, false},
 	{"debug4", DEBUG4, false},
 	{"debug3", DEBUG3, false},
@@ -537,7 +537,6 @@ static bool default_with_oids = false;
 bool		current_role_is_superuser;
 
 int			log_min_error_statement = ERROR;
-int			log_min_messages = WARNING;
 int			client_min_messages = NOTICE;
 int			log_min_duration_sample = -1;
 int			log_min_duration_statement = -1;
@@ -595,6 +594,7 @@ static char *server_version_string;
 static int	server_version_num;
 static char *debug_io_direct_string;
 static char *restrict_nonsystem_relation_kind_string;
+static char *log_min_messages_string;
 
 #ifdef HAVE_SYSLOG
 #define	DEFAULT_SYSLOG_FACILITY LOG_LOCAL0
@@ -647,6 +647,23 @@ char	   *role_string;
 /* should be static, but guc.c needs to get at this */
 bool		in_hot_standby_guc;
 
+/*
+ * log_min_messages
+ */
+int			log_min_messages[] = {
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
+	[bktype] = log_min_messages,
+#include "postmaster/proctypelist.h"
+#undef PG_PROCTYPE
+};
+
+const char *const log_min_messages_process_types[] = {
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
+	[bktype] = bkcategory,
+#include "postmaster/proctypelist.h"
+#undef PG_PROCTYPE
+};
+
 
 /*
  * Displayable names for context types (enum GucContext)
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index dc9e2255f8a..24b5d983590 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -541,6 +541,8 @@
                                         #   log
                                         #   fatal
                                         #   panic
+                                        #   and an optional comma-separated list
+                                        #   of type:level
 
 #log_min_error_statement = error        # values in order of decreasing detail:
                                         #   debug5
diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h
index 0b99eaabfd0..0ddd805f0e7 100644
--- a/src/include/postmaster/proctypelist.h
+++ b/src/include/postmaster/proctypelist.h
@@ -25,27 +25,27 @@
  */
 
 /*
- * List of process types (symbol, description, Main function, shmem_attach)
- * entries.
+ * List of process types (symbol, category, description, Main function,
+ * shmem_attach, message level) entries.
  */
 
 
-/* bktype, description, main_func, shmem_attach */
-PG_PROCTYPE(B_ARCHIVER, gettext_noop("archiver"), PgArchiverMain, true)
-PG_PROCTYPE(B_AUTOVAC_LAUNCHER, gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true)
-PG_PROCTYPE(B_AUTOVAC_WORKER, gettext_noop("autovacuum worker"), AutoVacWorkerMain, true)
-PG_PROCTYPE(B_BACKEND, gettext_noop("client backend"), BackendMain, true)
-PG_PROCTYPE(B_BG_WORKER, gettext_noop("background worker"), BackgroundWorkerMain, true)
-PG_PROCTYPE(B_BG_WRITER, gettext_noop("background writer"), BackgroundWriterMain, true)
-PG_PROCTYPE(B_CHECKPOINTER, gettext_noop("checkpointer"), CheckpointerMain, true)
-PG_PROCTYPE(B_DEAD_END_BACKEND, gettext_noop("dead-end client backend"), BackendMain, true)
-PG_PROCTYPE(B_INVALID, gettext_noop("unrecognized"), NULL, false)
-PG_PROCTYPE(B_IO_WORKER, gettext_noop("io worker"), IoWorkerMain, true)
-PG_PROCTYPE(B_LOGGER, gettext_noop("syslogger"), SysLoggerMain, false)
-PG_PROCTYPE(B_SLOTSYNC_WORKER, gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true)
-PG_PROCTYPE(B_STANDALONE_BACKEND, gettext_noop("standalone backend"), NULL, false)
-PG_PROCTYPE(B_STARTUP, gettext_noop("startup"), StartupProcessMain, true)
-PG_PROCTYPE(B_WAL_RECEIVER, gettext_noop("walreceiver"), WalReceiverMain, true)
-PG_PROCTYPE(B_WAL_SENDER, gettext_noop("walsender"), NULL, true)
-PG_PROCTYPE(B_WAL_SUMMARIZER, gettext_noop("walsummarizer"), WalSummarizerMain, true)
-PG_PROCTYPE(B_WAL_WRITER, gettext_noop("walwriter"), WalWriterMain, true)
+/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */
+PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING)
+PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING)
+PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING)
+PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING)
+PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING)
+PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING)
+PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING)
+PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING)
+PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING)
+PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING)
+PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING)
+PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING)
+PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING)
+PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING)
+PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING)
+PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING)
+PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING)
+PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING)
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index bf39878c43e..47cdc72bf02 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -295,7 +295,7 @@ extern PGDLLIMPORT bool log_duration;
 extern PGDLLIMPORT int log_parameter_max_length;
 extern PGDLLIMPORT int log_parameter_max_length_on_error;
 extern PGDLLIMPORT int log_min_error_statement;
-extern PGDLLIMPORT int log_min_messages;
+extern PGDLLIMPORT int log_min_messages[];
 extern PGDLLIMPORT int client_min_messages;
 extern PGDLLIMPORT int log_min_duration_sample;
 extern PGDLLIMPORT int log_min_duration_statement;
@@ -329,6 +329,9 @@ extern PGDLLIMPORT bool trace_sort;
 extern PGDLLIMPORT bool optimize_bounded_sort;
 #endif
 
+/* log_min_messages */
+extern PGDLLIMPORT const char *const log_min_messages_process_types[];
+
 /*
  * Declarations for options for enum values
  *
@@ -344,6 +347,7 @@ extern PGDLLIMPORT const struct config_enum_entry archive_mode_options[];
 extern PGDLLIMPORT const struct config_enum_entry dynamic_shared_memory_options[];
 extern PGDLLIMPORT const struct config_enum_entry io_method_options[];
 extern PGDLLIMPORT const struct config_enum_entry recovery_target_action_options[];
+extern PGDLLIMPORT const struct config_enum_entry server_message_level_options[];
 extern PGDLLIMPORT const struct config_enum_entry wal_level_options[];
 extern PGDLLIMPORT const struct config_enum_entry wal_sync_method_options[];
 
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index f723668da9e..ac27737a107 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -175,5 +175,7 @@ extern void assign_wal_sync_method(int new_wal_sync_method, void *extra);
 extern bool check_synchronized_standby_slots(char **newval, void **extra,
 											 GucSource source);
 extern void assign_synchronized_standby_slots(const char *newval, void *extra);
+extern bool check_log_min_messages(char **newval, void **extra, GucSource source);
+extern void assign_log_min_messages(const char *newval, void *extra);
 
 #endif							/* GUC_HOOKS_H */
diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out
index d6fb879f500..3b8f44566b2 100644
--- a/src/test/regress/expected/guc.out
+++ b/src/test/regress/expected/guc.out
@@ -935,3 +935,57 @@ SELECT name FROM tab_settings_flags
 (0 rows)
 
 DROP TABLE tab_settings_flags;
+-- Test log_min_messages
+SET log_min_messages TO foo;  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "foo"
+DETAIL:  Unrecognized log level: "foo".
+SET log_min_messages TO fatal;
+SHOW log_min_messages;
+ log_min_messages 
+------------------
+ fatal
+(1 row)
+
+SET log_min_messages TO 'fatal';
+SHOW log_min_messages;
+ log_min_messages 
+------------------
+ fatal
+(1 row)
+
+SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1';  --fail
+ERROR:  invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1"
+DETAIL:  Generic log level was not defined.
+SET log_min_messages TO 'debug1, backend:error, fatal';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "debug1, backend:error, fatal"
+DETAIL:  Generic log level was already assigned.
+SET log_min_messages TO 'backend:error, debug1, backend:warning';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning"
+DETAIL:  Process type "backend" was already assigned.
+SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1"
+DETAIL:  Unrecognized process type: "foo".
+SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1"
+DETAIL:  Unrecognized log level: "bar".
+SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3';
+SHOW log_min_messages;
+                                        log_min_messages                                         
+-------------------------------------------------------------------------------------------------
+ backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3
+(1 row)
+
+SET log_min_messages TO 'warning, autovacuum:debug1';
+SHOW log_min_messages;
+      log_min_messages      
+----------------------------
+ warning, autovacuum:debug1
+(1 row)
+
+SET log_min_messages TO 'autovacuum:debug1, warning';
+SHOW log_min_messages;
+      log_min_messages      
+----------------------------
+ autovacuum:debug1, warning
+(1 row)
+
diff --git a/src/test/regress/sql/guc.sql b/src/test/regress/sql/guc.sql
index bafaf067e82..f7dfc909b42 100644
--- a/src/test/regress/sql/guc.sql
+++ b/src/test/regress/sql/guc.sql
@@ -377,3 +377,21 @@ SELECT name FROM tab_settings_flags
   WHERE no_reset AND NOT no_reset_all
   ORDER BY 1;
 DROP TABLE tab_settings_flags;
+
+-- Test log_min_messages
+SET log_min_messages TO foo;  -- fail
+SET log_min_messages TO fatal;
+SHOW log_min_messages;
+SET log_min_messages TO 'fatal';
+SHOW log_min_messages;
+SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1';  --fail
+SET log_min_messages TO 'debug1, backend:error, fatal';  -- fail
+SET log_min_messages TO 'backend:error, debug1, backend:warning';  -- fail
+SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1';  -- fail
+SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1';  -- fail
+SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3';
+SHOW log_min_messages;
+SET log_min_messages TO 'warning, autovacuum:debug1';
+SHOW log_min_messages;
+SET log_min_messages TO 'autovacuum:debug1, warning';
+SHOW log_min_messages;
-- 
2.39.5



  [text/x-patch] v8-0002-Assign-backend-type-earlier.patch (8.6K, ../../f20103f2-d960-4206-9969-c4a936524ab1@app.fastmail.com/3-v8-0002-Assign-backend-type-earlier.patch)
  download | inline diff:
From 0c91ca9b3546c3cdc91d1586bf63e247d09efe38 Mon Sep 17 00:00:00 2001
From: Euler Taveira <euler@eulerto.com>
Date: Tue, 25 Nov 2025 21:09:11 -0300
Subject: [PATCH v8 2/3] Assign backend type earlier
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Instead of assigning the backend type into the Main function of each
postmaster child, do it earlier (after fork()). The backend type is
already known by postmaster_child_launch() before it calls fork(). This
reduces the time frame that MyBackendType is correct. (This is important
for log_min_messages per backend type that relies on a known
MyBackendType to decide if it writes a log message or not.)

Author: Euler Taveira <euler@eulerto.com>
Author: Álvaro Herrera <alvherre@kurilemu.de>
Discussion: https://www.postgresql.org/message-id/flat/e85c6671-1600-4112-8887-f97a8a5d07b2@app.fastmail.com
---
 src/backend/postmaster/autovacuum.c        | 2 --
 src/backend/postmaster/bgworker.c          | 1 -
 src/backend/postmaster/bgwriter.c          | 1 -
 src/backend/postmaster/checkpointer.c      | 1 -
 src/backend/postmaster/launch_backend.c    | 5 +++++
 src/backend/postmaster/pgarch.c            | 1 -
 src/backend/postmaster/startup.c           | 1 -
 src/backend/postmaster/syslogger.c         | 1 -
 src/backend/postmaster/walsummarizer.c     | 1 -
 src/backend/postmaster/walwriter.c         | 1 -
 src/backend/replication/logical/slotsync.c | 2 --
 src/backend/replication/walreceiver.c      | 1 -
 src/backend/storage/aio/method_worker.c    | 1 -
 src/backend/utils/error/elog.c             | 7 ++++++-
 14 files changed, 11 insertions(+), 15 deletions(-)

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 3e507d23cc9..5d1c3fdfb0f 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -388,7 +388,6 @@ AutoVacLauncherMain(const void *startup_data, size_t startup_data_len)
 		PostmasterContext = NULL;
 	}
 
-	MyBackendType = B_AUTOVAC_LAUNCHER;
 	init_ps_display(NULL);
 
 	ereport(DEBUG1,
@@ -1401,7 +1400,6 @@ AutoVacWorkerMain(const void *startup_data, size_t startup_data_len)
 		PostmasterContext = NULL;
 	}
 
-	MyBackendType = B_AUTOVAC_WORKER;
 	init_ps_display(NULL);
 
 	Assert(GetProcessingMode() == InitProcessing);
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 65deabe91a7..5144d70095c 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -753,7 +753,6 @@ BackgroundWorkerMain(const void *startup_data, size_t startup_data_len)
 	}
 
 	MyBgworkerEntry = worker;
-	MyBackendType = B_BG_WORKER;
 	init_ps_display(worker->bgw_name);
 
 	Assert(GetProcessingMode() == InitProcessing);
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index 80e3088fc7e..0956bd39a85 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -94,7 +94,6 @@ BackgroundWriterMain(const void *startup_data, size_t startup_data_len)
 
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_BG_WRITER;
 	AuxiliaryProcessMainCommon();
 
 	/*
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 6482c21b8f9..e03c19123bc 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -199,7 +199,6 @@ CheckpointerMain(const void *startup_data, size_t startup_data_len)
 
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_CHECKPOINTER;
 	AuxiliaryProcessMainCommon();
 
 	CheckpointerShmem->checkpointer_pid = MyProcPid;
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 57303e1731b..501bad56832 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -224,6 +224,8 @@ postmaster_child_launch(BackendType child_type, int child_slot,
 	pid = fork_process();
 	if (pid == 0)				/* child */
 	{
+		MyBackendType = child_type;
+
 		/* Capture and transfer timings that may be needed for logging */
 		if (IsExternalConnectionBackend(child_type))
 		{
@@ -586,6 +588,8 @@ SubPostmasterMain(int argc, char *argv[])
 	IsPostmasterEnvironment = true;
 	whereToSendOutput = DestNone;
 
+	MyBackendType = B_BACKEND;
+
 	/*
 	 * Capture the end of process creation for logging. We don't include the
 	 * time spent copying data from shared memory and setting up the backend.
@@ -609,6 +613,7 @@ SubPostmasterMain(int argc, char *argv[])
 		if (strcmp(child_process_kinds[idx].name, child_kind) == 0)
 		{
 			child_type = (BackendType) idx;
+			MyBackendType = child_type;
 			found = true;
 			break;
 		}
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 1a20387c4bd..82731e452fc 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -222,7 +222,6 @@ PgArchiverMain(const void *startup_data, size_t startup_data_len)
 {
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_ARCHIVER;
 	AuxiliaryProcessMainCommon();
 
 	/*
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index a1a4f65f9a9..cdbe53dd262 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -217,7 +217,6 @@ StartupProcessMain(const void *startup_data, size_t startup_data_len)
 {
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_STARTUP;
 	AuxiliaryProcessMainCommon();
 
 	/* Arrange to clean up at startup process exit */
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index 1c443b3d126..86c5e376b40 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -206,7 +206,6 @@ SysLoggerMain(const void *startup_data, size_t startup_data_len)
 
 	now = MyStartTime;
 
-	MyBackendType = B_LOGGER;
 	init_ps_display(NULL);
 
 	/*
diff --git a/src/backend/postmaster/walsummarizer.c b/src/backend/postmaster/walsummarizer.c
index c3d56c866d3..2d8f57099fd 100644
--- a/src/backend/postmaster/walsummarizer.c
+++ b/src/backend/postmaster/walsummarizer.c
@@ -234,7 +234,6 @@ WalSummarizerMain(const void *startup_data, size_t startup_data_len)
 
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_WAL_SUMMARIZER;
 	AuxiliaryProcessMainCommon();
 
 	ereport(DEBUG1,
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 38ec8a4c8c7..23e79a32345 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -94,7 +94,6 @@ WalWriterMain(const void *startup_data, size_t startup_data_len)
 
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_WAL_WRITER;
 	AuxiliaryProcessMainCommon();
 
 	/*
diff --git a/src/backend/replication/logical/slotsync.c b/src/backend/replication/logical/slotsync.c
index 73fc51ea53e..b53f888b804 100644
--- a/src/backend/replication/logical/slotsync.c
+++ b/src/backend/replication/logical/slotsync.c
@@ -1540,8 +1540,6 @@ ReplSlotSyncWorkerMain(const void *startup_data, size_t startup_data_len)
 
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_SLOTSYNC_WORKER;
-
 	init_ps_display(NULL);
 
 	Assert(GetProcessingMode() == InitProcessing);
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index a41453530a1..8dbd8cb2009 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -169,7 +169,6 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len)
 
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_WAL_RECEIVER;
 	AuxiliaryProcessMainCommon();
 
 	/*
diff --git a/src/backend/storage/aio/method_worker.c b/src/backend/storage/aio/method_worker.c
index d7c144cd8f7..d9617c20e76 100644
--- a/src/backend/storage/aio/method_worker.c
+++ b/src/backend/storage/aio/method_worker.c
@@ -390,7 +390,6 @@ IoWorkerMain(const void *startup_data, size_t startup_data_len)
 	volatile int error_errno = 0;
 	char		cmd[128];
 
-	MyBackendType = B_IO_WORKER;
 	AuxiliaryProcessMainCommon();
 
 	pqsignal(SIGHUP, SignalHandlerForConfigReload);
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 59975309a4e..f35ec0429b2 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -2779,7 +2779,12 @@ get_backend_type_for_log(void)
 	if (MyProcPid == PostmasterPid)
 		backend_type_str = "postmaster";
 	else if (MyBackendType == B_BG_WORKER)
-		backend_type_str = MyBgworkerEntry->bgw_type;
+	{
+		if (MyBgworkerEntry && MyBgworkerEntry->bgw_type[0] != '\0')
+			backend_type_str = MyBgworkerEntry->bgw_type;
+		else
+			backend_type_str = "early bgworker";
+	}
 	else
 		backend_type_str = GetBackendTypeDesc(MyBackendType);
 
-- 
2.39.5



  [text/x-patch] v8-0003-fixup-log_min_messages-per-process-type.patch (3.5K, ../../f20103f2-d960-4206-9969-c4a936524ab1@app.fastmail.com/4-v8-0003-fixup-log_min_messages-per-process-type.patch)
  download | inline diff:
From 2370da9bbe5757f26505dd45a3ddd2091c621108 Mon Sep 17 00:00:00 2001
From: Euler Taveira <euler@eulerto.com>
Date: Wed, 14 Jan 2026 17:29:09 -0300
Subject: [PATCH v8 3/3] fixup! log_min_messages per process type

---
 src/backend/commands/variable.c   | 55 +++++++++++++++++++++++++++++++
 src/test/regress/expected/guc.out |  4 +--
 2 files changed, 57 insertions(+), 2 deletions(-)

diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index f4f5d4c367c..53fcce5ea43 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -42,6 +42,10 @@
 #include "utils/tzparser.h"
 #include "utils/varlena.h"
 
+#define	MAX_LMM_STR_LEN (BACKEND_NUM_TYPES * 32)
+
+static int	string_cmp(const ListCell *a, const ListCell *b);
+
 /*
  * DATESTYLE
  */
@@ -1275,10 +1279,16 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 	char	   *rawstring;
 	List	   *elemlist;
 	ListCell   *l;
+	char	   *result;
+	bool		first = true;
 	int			newlevel[BACKEND_NUM_TYPES];
 	bool		assigned[BACKEND_NUM_TYPES];
 	int			genericlevel = -1;	/* -1 means not assigned */
 
+	result = (char *) guc_malloc(LOG, MAX_LMM_STR_LEN);
+	if (!result)
+		return false;
+
 	/* Initialize the array. */
 	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
 	{
@@ -1442,6 +1452,42 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 			newlevel[i] = genericlevel;
 	}
 
+	/*
+	 * Use a stable representation of log_min_messages. The generic level is
+	 * always the first element and the other elements (type:level) are sorted
+	 * by process type.
+	 */
+	list_sort(elemlist, string_cmp);
+	foreach(l, elemlist)
+	{
+		char	   *tok = (char *) lfirst(l);
+
+		if (strchr(tok, ':') == NULL)
+		{
+			elemlist = list_delete(elemlist, tok);
+			elemlist = lcons(tok, elemlist);
+			break;
+		}
+	}
+
+	foreach(l, elemlist)
+	{
+		char	   *tok = (char *) lfirst(l);
+
+		if (first)
+		{
+			strncpy(result, tok, MAX_LMM_STR_LEN);
+			first = false;
+		}
+		else
+		{
+			strlcat(result, ", ", MAX_LMM_STR_LEN);
+			strlcat(result, tok, MAX_LMM_STR_LEN);
+		}
+	}
+	guc_free(*newval);
+	*newval = result;
+
 	guc_free(rawstring);
 	list_free(elemlist);
 
@@ -1456,6 +1502,15 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 	return true;
 }
 
+static int
+string_cmp(const ListCell *a, const ListCell *b)
+{
+	const char *s = lfirst(a);
+	const char *t = lfirst(b);
+
+	return strcmp(s, t);
+}
+
 /*
  * GUC assign_hook for log_min_messages
  */
diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out
index 3b8f44566b2..9a6fd503009 100644
--- a/src/test/regress/expected/guc.out
+++ b/src/test/regress/expected/guc.out
@@ -972,7 +972,7 @@ SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:deb
 SHOW log_min_messages;
                                         log_min_messages                                         
 -------------------------------------------------------------------------------------------------
- backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3
+ fatal, archiver:debug2, autovacuum:debug1, backend:error, checkpointer:debug3, walsender:debug3
 (1 row)
 
 SET log_min_messages TO 'warning, autovacuum:debug1';
@@ -986,6 +986,6 @@ SET log_min_messages TO 'autovacuum:debug1, warning';
 SHOW log_min_messages;
       log_min_messages      
 ----------------------------
- autovacuum:debug1, warning
+ warning, autovacuum:debug1
 (1 row)
 
-- 
2.39.5



^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2026-01-16 05:54  Japin Li <japinli@hotmail.com>
  parent: Euler Taveira <euler@eulerto.com>
  3 siblings, 0 replies; 58+ messages in thread

From: Japin Li @ 2026-01-16 05:54 UTC (permalink / raw)
  To: Euler Taveira <euler@eulerto.com>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org, "Chao Li" <li.evan.chao@gmail.com>


Hi Euler,

On Thu, 15 Jan 2026 at 13:07, "Euler Taveira" <euler@eulerto.com> wrote:
> On Tue, Dec 9, 2025, at 3:24 PM, Alvaro Herrera wrote:
>> On 2025-Dec-09, Alvaro Herrera wrote:
>>
>>> So here's your v6 again with those fixes as 0003 -- let's see what CI
>>> thinks of this.  I haven't looked at your doc changes yet.
>>
>> This passed CI, so I have marked it as Ready for Committer.  Further
>> comments are still welcome, of course, but if there are none, I intend
>> to get it committed in a few days.
>>
>
> I took another look after Chao Li comments [1]. I created the 0003 patch
> that does the sort as suggested. I think it is good to be consistent but
> I'm fine if we decided the additional code is not worth. The 32 in the
> MAX_LMM_STR_LEN is arbitrary but it is based on the size of the largest
> element in the list ("dead-end client backend:warning"). I didn't take
> into account the comma and space between elements but it is not
> necessary since other elements are smaller than the largest one.
> I didn't implement the 2nd suggestion.
>
> I also merged Alvaro's fix to 0002. The v8 is attached.
>
> I didn't change the commit message but if 0003 is merged into 0001 then
> it should mention that
>
> 8<--------------------------------------------------------------------8<
> The SHOW command presents well-formatted list sorted by process type and
> the generic log level is the first element list. It improves readability
> and has a clear indentation.
> 8<--------------------------------------------------------------------8<
>
> Do we really need a different backend type in this case? For background
> workers the description is "background worker". Shoundn't it use the
> same description for this edge case too?
>
> -       backend_type_str = MyBgworkerEntry->bgw_type;
> +   {
> +       if (MyBgworkerEntry && MyBgworkerEntry->bgw_type[0] != '\0')
> +           backend_type_str = MyBgworkerEntry->bgw_type;
> +       else
> +           backend_type_str = "early bgworker";
> +   }
>
> I also noticed that commit 18d67a8d7d30 forgot to add gettext_noop to
> the get_backend_type_for_log function. It should be consistent with
> GetBackendTypeDesc() return.
>
>
> [1] https://postgr.es/m/1130A10B-E7AE-49F3-9E13-2CD69B3F9DFD@gmail.com
>
>

Thanks for updating the patch set.

Here are some comments.

1.
We can replace foreach with foreach_ptr in both v8-0001 and v8-0003.

2.
+/* log_min_messages */
+extern PGDLLIMPORT const char *const log_min_messages_process_types[];
+

Comment looks wrong.

3.
For cases where the process type is valid but the log level is unrecognized,
I suggest improving the error message for better clarity, e.g.:

	Unrecognized log level "bar" for process type "backend"

4.
The function name string_cmp feels too generic.
Could we consider a more specific name, for example list_log_min_message_cmp
or another more appropriate one?

> -- 
> Euler Taveira
> EDB   https://www.enterprisedb.com/
>
> [2. text/x-patch; v8-0001-log_min_messages-per-process-type.patch]...
>
> [3. text/x-patch; v8-0002-Assign-backend-type-earlier.patch]...
>
> [4. text/x-patch; v8-0003-fixup-log_min_messages-per-process-type.patch]...

-- 
Regards,
Japin Li
ChengDu WenWu Information Technology Co., Ltd.





^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2026-01-17 22:02  Alvaro Herrera <alvherre@alvh.no-ip.org>
  parent: Euler Taveira <euler@eulerto.com>
  3 siblings, 1 reply; 58+ messages in thread

From: Alvaro Herrera @ 2026-01-17 22:02 UTC (permalink / raw)
  To: Euler Taveira <euler@eulerto.com>; +Cc: japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org, Chao Li <li.evan.chao@gmail.com>

On 2026-Jan-15, Euler Taveira wrote:

> +	/*
> +	 * Use a stable representation of log_min_messages. The generic level is
> +	 * always the first element and the other elements (type:level) are sorted
> +	 * by process type.
> +	 */
> +	list_sort(elemlist, string_cmp);
> +	foreach(l, elemlist)
> +	{
> +		char	   *tok = (char *) lfirst(l);
> +
> +		if (strchr(tok, ':') == NULL)
> +		{
> +			elemlist = list_delete(elemlist, tok);
> +			elemlist = lcons(tok, elemlist);
> +			break;
> +		}
> +	}

> +static int
> +string_cmp(const ListCell *a, const ListCell *b)
> +{
> +	const char *s = lfirst(a);
> +	const char *t = lfirst(b);
> +
> +	return strcmp(s, t);
> +}

I'm not opposed to this idea, but I think it should be implemented not
by sorting and then moving the first element to the top of the list, but
instead by modifying the cmp function so that the desired order is
achieved directly.  So the cmp() should return -1 if element a has no
colon, or 1 if element b has no colon, otherwise return per strcmp.
That way you can remove the foreach() block above, which is icky.

-- 
Álvaro Herrera               48°01'N 7°57'E  —  https://www.EnterpriseDB.com/
"Computing is too important to be left to men." (Karen Spärck Jones)





^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2026-01-20 15:43  Alvaro Herrera <alvherre@alvh.no-ip.org>
  parent: Euler Taveira <euler@eulerto.com>
  0 siblings, 0 replies; 58+ messages in thread

From: Alvaro Herrera @ 2026-01-20 15:43 UTC (permalink / raw)
  To: Euler Taveira <euler@eulerto.com>; +Cc: japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org, Chao Li <li.evan.chao@gmail.com>

On 2026-Jan-15, Euler Taveira wrote:

> On Tue, Dec 9, 2025, at 1:30 PM, Alvaro Herrera wrote:
> > BTW another thing I realized while looking this over, is that we quite
> > uselessly transform the integer backend type to a string, pass it as a
> > string using the --forkchild= argument to the child process, then parse
> > the string back to an int to use as an array index.  It would be much
> > easier to just use the integer value everywhere, as the attached shows.
> >
> 
> It is a good simplification.

Thanks!  Pushed.

> There is just one oversight.
> 
> +   child_type = (BackendType) atoi(child_kind);
> +   if (child_type <= B_INVALID || child_type > BACKEND_NUM_TYPES)
>         elog(ERROR, "unknown child kind %s", child_kind);
> 
> It should be BACKEND_NUM_TYPES - 1.

Good catch, thanks.

-- 
Álvaro Herrera               48°01'N 7°57'E  —  https://www.EnterpriseDB.com/
"Digital and video cameras have this adjustment and film cameras don't for the
same reason dogs and cats lick themselves: because they can."   (Ken Rockwell)





^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2026-01-20 17:12  Alvaro Herrera <alvherre@alvh.no-ip.org>
  parent: Euler Taveira <euler@eulerto.com>
  3 siblings, 0 replies; 58+ messages in thread

From: Alvaro Herrera @ 2026-01-20 17:12 UTC (permalink / raw)
  To: Euler Taveira <euler@eulerto.com>; +Cc: japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org, Chao Li <li.evan.chao@gmail.com>

On 2026-Jan-15, Euler Taveira wrote:

> @@ -1275,10 +1279,16 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
>  	char	   *rawstring;
>  	List	   *elemlist;
>  	ListCell   *l;
> +	char	   *result;
> +	bool		first = true;
>  	int			newlevel[BACKEND_NUM_TYPES];
>  	bool		assigned[BACKEND_NUM_TYPES];
>  	int			genericlevel = -1;	/* -1 means not assigned */
>  
> +	result = (char *) guc_malloc(LOG, MAX_LMM_STR_LEN);
> +	if (!result)
> +		return false;
     ...
> +	foreach(l, elemlist)
> +	{
> +		char	   *tok = (char *) lfirst(l);
> +
> +		if (first)
> +		{
> +			strncpy(result, tok, MAX_LMM_STR_LEN);
> +			first = false;
> +		}
> +		else
> +		{
> +			strlcat(result, ", ", MAX_LMM_STR_LEN);
> +			strlcat(result, tok, MAX_LMM_STR_LEN);
> +		}
> +	}

BTW I think this whole MAX_LMM_STR_LEN thing is unnecessary.  It is
probably easy enough to use a StringInfo to create the value
iteratively, then once you know its final length do a guc_malloc() and
strcpy the value there.

-- 
Álvaro Herrera         PostgreSQL Developer  —  https://www.EnterpriseDB.com/
"The Gord often wonders why people threaten never to come back after they've
been told never to return" (www.actsofgord.com)





^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2026-01-21 14:51  Alvaro Herrera <alvherre@alvh.no-ip.org>
  parent: Euler Taveira <euler@eulerto.com>
  3 siblings, 0 replies; 58+ messages in thread

From: Alvaro Herrera @ 2026-01-21 14:51 UTC (permalink / raw)
  To: Euler Taveira <euler@eulerto.com>; +Cc: japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org, Chao Li <li.evan.chao@gmail.com>

On 2026-Jan-15, Euler Taveira wrote:

> @@ -586,6 +588,8 @@ SubPostmasterMain(int argc, char *argv[])
>  	IsPostmasterEnvironment = true;
>  	whereToSendOutput = DestNone;
>  
> +	MyBackendType = B_BACKEND;
> +
>  	/*
>  	 * Capture the end of process creation for logging. We don't include the
>  	 * time spent copying data from shared memory and setting up the backend.

BTW I think SubPostmasterMain() shouldn't do this.

-- 
Álvaro Herrera               48°01'N 7°57'E  —  https://www.EnterpriseDB.com/
"Curiosity is the hope that something wondrous waits just out of sight,
for we are surrounded by a garden of miracles"                       (Namron)
                           (Marjorie Liu and Sana Takeda, Monstress: Inferno)





^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2026-01-22 02:12  Euler Taveira <euler@eulerto.com>
  parent: Alvaro Herrera <alvherre@alvh.no-ip.org>
  0 siblings, 1 reply; 58+ messages in thread

From: Euler Taveira @ 2026-01-22 02:12 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org, "Chao Li" <li.evan.chao@gmail.com>

On Sat, Jan 17, 2026, at 7:02 PM, Alvaro Herrera wrote:
>
> I'm not opposed to this idea, but I think it should be implemented not
> by sorting and then moving the first element to the top of the list, but
> instead by modifying the cmp function so that the desired order is
> achieved directly.  So the cmp() should return -1 if element a has no
> colon, or 1 if element b has no colon, otherwise return per strcmp.
> That way you can remove the foreach() block above, which is icky.
>

Good idea. The v9 contains this implementation. I also use the StringInfo as
you suggested in the other email. The 0003 removed the wrong B_BACKEND
assignment pointed in a previous email. This new patchset contains some changes
suggested by Japin Li [1] (I didn't change the error message because it adds an
extra message to translate). There was a minor issue with a null-terminated
string that I fixed too.

PS> I didn't change the background worker case that I mentioned in [2].

[1] https://postgr.es/m/MEAPR01MB3031FA1986F3FC91481E28CCB68DA@MEAPR01MB3031.ausprd01.prod.outlook.com
[2] https://postgr.es/m/f20103f2-d960-4206-9969-c4a936524ab1@app.fastmail.com


-- 
Euler Taveira
EDB   https://www.enterprisedb.com/

Attachments:

  [text/x-patch] v9-0001-log_min_messages-per-process-type.patch (26.2K, ../../4d496ab8-35e7-4535-8ee1-ed2bb7262172@app.fastmail.com/2-v9-0001-log_min_messages-per-process-type.patch)
  download | inline diff:
From ea299f1a8b6a38e40826d7baf0dd7a6733197fb4 Mon Sep 17 00:00:00 2001
From: Euler Taveira <euler@eulerto.com>
Date: Tue, 4 Nov 2025 17:18:20 -0300
Subject: [PATCH v9 1/3] log_min_messages per process type

Change log_min_messages from a single element to a comma-separated list
of elements. Each element is type:level. The types are archiver,
autovacuum (includes launcher and workers), backend, bgworker, bgwriter,
checkpointer, ioworker, postmaster, syslogger, slotsyncworker, startup,
walreceiver, walsender, walsummarizer and walwriter. A single log level
should be part of this list and it is applied as a final step to the
process types that were not informed.

The old syntax (a single log level) is still accepted for backward
compatibility. In this case, it means the informed log level is applied
for all process types. The default log level is the same (WARNING) for
all process types.
---
 doc/src/sgml/config.sgml                      |  42 +++-
 src/backend/commands/extension.c              |   2 +-
 src/backend/commands/variable.c               | 206 ++++++++++++++++++
 src/backend/postmaster/launch_backend.c       |   2 +-
 src/backend/utils/error/elog.c                |   2 +-
 src/backend/utils/init/miscinit.c             |   2 +-
 src/backend/utils/misc/guc_parameters.dat     |  10 +-
 src/backend/utils/misc/guc_tables.c           |  21 +-
 src/backend/utils/misc/postgresql.conf.sample |   2 +
 src/include/postmaster/proctypelist.h         |  42 ++--
 src/include/utils/guc.h                       |   5 +-
 src/include/utils/guc_hooks.h                 |   2 +
 src/test/regress/expected/guc.out             |  54 +++++
 src/test/regress/sql/guc.sql                  |  18 ++
 14 files changed, 369 insertions(+), 41 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 5560b95ee60..cbc748a3636 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -7083,7 +7083,7 @@ local0.*    /var/log/postgresql
      <variablelist>
 
      <varlistentry id="guc-log-min-messages" xreflabel="log_min_messages">
-      <term><varname>log_min_messages</varname> (<type>enum</type>)
+      <term><varname>log_min_messages</varname> (<type>string</type>)
       <indexterm>
        <primary><varname>log_min_messages</varname> configuration parameter</primary>
       </indexterm>
@@ -7092,14 +7092,38 @@ local0.*    /var/log/postgresql
        <para>
         Controls which <link linkend="runtime-config-severity-levels">message
         levels</link> are written to the server log.
-        Valid values are <literal>DEBUG5</literal>, <literal>DEBUG4</literal>,
-        <literal>DEBUG3</literal>, <literal>DEBUG2</literal>, <literal>DEBUG1</literal>,
-        <literal>INFO</literal>, <literal>NOTICE</literal>, <literal>WARNING</literal>,
-        <literal>ERROR</literal>, <literal>LOG</literal>, <literal>FATAL</literal>, and
-        <literal>PANIC</literal>.  Each level includes all the levels that
-        follow it.  The later the level, the fewer messages are sent
-        to the log.  The default is <literal>WARNING</literal>.  Note that
-        <literal>LOG</literal> has a different rank here than in
+        Valid values are a comma-separated list of <literal>type:level</literal>
+        and a single <literal>level</literal>. The list allows it to use
+        different levels per process type. Only the single <literal>level</literal>
+        is mandatory (order does not matter) and it is assigned to the process
+        types that are not specified in the list.
+        Valid process types are listed in the table below, each corresponding to
+        either postmaster, an auxiliary process type or a backend.
+        <simplelist type="vert" columns="4">
+         <member><literal>archiver</literal></member>
+         <member><literal>autovacuum</literal></member>
+         <member><literal>backend</literal></member>
+         <member><literal>bgworker</literal></member>
+         <member><literal>bgwriter</literal></member>
+         <member><literal>checkpointer</literal></member>
+         <member><literal>ioworker</literal></member>
+         <member><literal>postmaster</literal></member>
+         <member><literal>syslogger</literal></member>
+         <member><literal>slotsyncworker</literal></member>
+         <member><literal>startup</literal></member>
+         <member><literal>walreceiver</literal></member>
+         <member><literal>walsender</literal></member>
+         <member><literal>walsummarizer</literal></member>
+         <member><literal>walwriter</literal></member>
+        </simplelist>
+        Valid <literal>level</literal> values are <literal>DEBUG5</literal>,
+        <literal>DEBUG4</literal>, <literal>DEBUG3</literal>, <literal>DEBUG2</literal>,
+        <literal>DEBUG1</literal>, <literal>INFO</literal>, <literal>NOTICE</literal>,
+        <literal>WARNING</literal>, <literal>ERROR</literal>, <literal>LOG</literal>,
+        <literal>FATAL</literal>, and <literal>PANIC</literal>.  Each level includes
+        all the levels that follow it.  The later the level, the fewer messages are sent
+        to the log.  The default is <literal>WARNING</literal> for all process types.
+        Note that <literal>LOG</literal> has a different rank here than in
         <xref linkend="guc-client-min-messages"/>.
         Only superusers and users with the appropriate <literal>SET</literal>
         privilege can change this setting.
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 596105ee078..688f1874f31 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1191,7 +1191,7 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
 		(void) set_config_option("client_min_messages", "warning",
 								 PGC_USERSET, PGC_S_SESSION,
 								 GUC_ACTION_SAVE, true, 0, false);
-	if (log_min_messages < WARNING)
+	if (log_min_messages[MyBackendType] < WARNING)
 		(void) set_config_option_ext("log_min_messages", "warning",
 									 PGC_SUSET, PGC_S_SESSION,
 									 BOOTSTRAP_SUPERUSERID,
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index 4440aff4925..99f40186533 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -34,6 +34,7 @@
 #include "utils/backend_status.h"
 #include "utils/datetime.h"
 #include "utils/fmgrprotos.h"
+#include "utils/guc.h"
 #include "utils/guc_hooks.h"
 #include "utils/snapmgr.h"
 #include "utils/syscache.h"
@@ -1272,3 +1273,208 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source)
 
 	return true;
 }
+
+/*
+ * GUC check_hook for log_min_messages
+ *
+ * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE
+ * is log_min_messages_process_types.  LEVEL is server_message_level_options. A
+ * single LEVEL element should be part of this list and it is applied as a
+ * final step to the process types that are not specified. For backward
+ * compatibility, the old syntax is still accepted and it means to apply the
+ * LEVEL for all process types.
+ */
+bool
+check_log_min_messages(char **newval, void **extra, GucSource source)
+{
+	char	   *rawstring;
+	List	   *elemlist;
+	int			newlevel[BACKEND_NUM_TYPES];
+	bool		assigned[BACKEND_NUM_TYPES];
+	int			genericlevel = -1;	/* -1 means not assigned */
+
+	/* Initialize the array. */
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+	{
+		newlevel[i] = WARNING;
+		assigned[i] = false;
+	}
+
+	/* Need a modifiable copy of string. */
+	rawstring = guc_strdup(LOG, *newval);
+
+	/* Parse string into list of identifiers. */
+	if (!SplitGUCList(rawstring, ',', &elemlist))
+	{
+		/* syntax error in list */
+		GUC_check_errdetail("List syntax is invalid.");
+		list_free(elemlist);
+		guc_free(rawstring);
+		return false;
+	}
+
+	/* Validate and assign log level and process type. */
+	foreach_ptr(char, tok, elemlist)
+	{
+		char	   *sep;
+		const struct config_enum_entry *entry;
+
+		/*
+		 * Check whether there is a process type following the log level. If
+		 * there is no separator, it means this is the generic log level. The
+		 * generic log level will be assigned to the process types that were
+		 * not informed.
+		 */
+		sep = strchr(tok, ':');
+		if (sep == NULL)
+		{
+			bool		found = false;
+
+			/* Reject duplicates for generic log level. */
+			if (genericlevel != -1)
+			{
+				GUC_check_errdetail("Generic log level was already assigned.");
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			/* Is the log level valid? */
+			for (entry = server_message_level_options; entry && entry->name; entry++)
+			{
+				if (pg_strcasecmp(entry->name, tok) == 0)
+				{
+					genericlevel = entry->val;
+					found = true;
+					break;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized log level: \"%s\".", tok);
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+		}
+		else
+		{
+			char	   *loglevel;
+			char	   *ptype;
+			bool		found = false;
+
+			ptype = guc_malloc(LOG, (sep - tok) + 1);
+			if (!ptype)
+			{
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+			memcpy(ptype, tok, sep - tok);
+			ptype[sep - tok] = '\0';
+			loglevel = sep + 1;
+
+			/* Is the log level valid? */
+			for (entry = server_message_level_options; entry && entry->name; entry++)
+			{
+				if (pg_strcasecmp(entry->name, loglevel) == 0)
+				{
+					found = true;
+					break;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel);
+				guc_free(ptype);
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			/*
+			 * Is the process type name valid? There might be multiple entries
+			 * per process type, don't bail out because it can assign the
+			 * value for multiple entries.
+			 */
+			found = false;
+			for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+			{
+				if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0)
+				{
+					/* Reject duplicates for a process type. */
+					if (assigned[i])
+					{
+						GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype);
+						guc_free(ptype);
+						guc_free(rawstring);
+						list_free(elemlist);
+						return false;
+					}
+
+					newlevel[i] = entry->val;
+					assigned[i] = true;
+					found = true;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype);
+				guc_free(ptype);
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			guc_free(ptype);
+		}
+	}
+
+	/*
+	 * The generic log level must be specified. It is the fallback value.
+	 */
+	if (genericlevel == -1)
+	{
+		GUC_check_errdetail("Generic log level was not defined.");
+		guc_free(rawstring);
+		list_free(elemlist);
+		return false;
+	}
+
+	/*
+	 * Apply the generic log level after all of the specific process types
+	 * have been assigned. Hence, it doesn't matter the order you specify the
+	 * generic log level, the final result will be the same.
+	 */
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+	{
+		if (!assigned[i])
+			newlevel[i] = genericlevel;
+	}
+
+	guc_free(rawstring);
+	list_free(elemlist);
+
+	/*
+	 * Pass back data for assign_log_min_messages to use.
+	 */
+	*extra = guc_malloc(LOG, BACKEND_NUM_TYPES * sizeof(int));
+	if (!*extra)
+		return false;
+	memcpy(*extra, newlevel, BACKEND_NUM_TYPES * sizeof(int));
+
+	return true;
+}
+
+/*
+ * GUC assign_hook for log_min_messages
+ */
+void
+assign_log_min_messages(const char *newval, void *extra)
+{
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+		log_min_messages[i] = ((int *) extra)[i];
+}
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index cea229ad6a4..756a0237deb 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -179,7 +179,7 @@ typedef struct
 } child_process_kind;
 
 static child_process_kind child_process_kinds[] = {
-#define PG_PROCTYPE(bktype, description, main_func, shmem_attach) \
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
 	[bktype] = {description, main_func, shmem_attach},
 #include "postmaster/proctypelist.h"
 #undef PG_PROCTYPE
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index aa530d3685e..59975309a4e 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -235,7 +235,7 @@ is_log_level_output(int elevel, int log_min_level)
 static inline bool
 should_output_to_server(int elevel)
 {
-	return is_log_level_output(elevel, log_min_messages);
+	return is_log_level_output(elevel, log_min_messages[MyBackendType]);
 }
 
 /*
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index 563f20374ff..efba20fa8b9 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType)
 
 	switch (backendType)
 	{
-#define PG_PROCTYPE(bktype, description, main_func, shmem_attach)	\
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages)	\
 		case bktype: backendDesc = description; break;
 #include "postmaster/proctypelist.h"
 #undef PG_PROCTYPE
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index f0260e6e412..3297c4bc30c 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -1686,12 +1686,14 @@
   options => 'server_message_level_options',
 },
 
-{ name => 'log_min_messages', type => 'enum', context => 'PGC_SUSET', group => 'LOGGING_WHEN',
+{ name => 'log_min_messages', type => 'string', context => 'PGC_SUSET', group => 'LOGGING_WHEN',
   short_desc => 'Sets the message levels that are logged.',
   long_desc => 'Each level includes all the levels that follow it. The later the level, the fewer messages are sent.',
-  variable => 'log_min_messages',
-  boot_val => 'WARNING',
-  options => 'server_message_level_options',
+  flags => 'GUC_LIST_INPUT',
+  variable => 'log_min_messages_string',
+  boot_val => '"WARNING"',
+  check_hook => 'check_log_min_messages',
+  assign_hook => 'assign_log_min_messages',
 },
 
 { name => 'log_parameter_max_length', type => 'int', context => 'PGC_SUSET', group => 'LOGGING_WHAT',
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 13c569d8790..0aebb9c8e24 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -146,7 +146,7 @@ static const struct config_enum_entry client_message_level_options[] = {
 	{NULL, 0, false}
 };
 
-static const struct config_enum_entry server_message_level_options[] = {
+const struct config_enum_entry server_message_level_options[] = {
 	{"debug5", DEBUG5, false},
 	{"debug4", DEBUG4, false},
 	{"debug3", DEBUG3, false},
@@ -537,7 +537,6 @@ static bool standard_conforming_strings = true;
 bool		current_role_is_superuser;
 
 int			log_min_error_statement = ERROR;
-int			log_min_messages = WARNING;
 int			client_min_messages = NOTICE;
 int			log_min_duration_sample = -1;
 int			log_min_duration_statement = -1;
@@ -595,6 +594,7 @@ static char *server_version_string;
 static int	server_version_num;
 static char *debug_io_direct_string;
 static char *restrict_nonsystem_relation_kind_string;
+static char *log_min_messages_string;
 
 #ifdef HAVE_SYSLOG
 #define	DEFAULT_SYSLOG_FACILITY LOG_LOCAL0
@@ -647,6 +647,23 @@ char	   *role_string;
 /* should be static, but guc.c needs to get at this */
 bool		in_hot_standby_guc;
 
+/*
+ * log_min_messages
+ */
+int			log_min_messages[] = {
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
+	[bktype] = log_min_messages,
+#include "postmaster/proctypelist.h"
+#undef PG_PROCTYPE
+};
+
+const char *const log_min_messages_process_types[] = {
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
+	[bktype] = bkcategory,
+#include "postmaster/proctypelist.h"
+#undef PG_PROCTYPE
+};
+
 
 /*
  * Displayable names for context types (enum GucContext)
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index c4f92fcdac8..ad0804ccb9f 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -541,6 +541,8 @@
                                         #   log
                                         #   fatal
                                         #   panic
+                                        #   and an optional comma-separated list
+                                        #   of type:level
 
 #log_min_error_statement = error        # values in order of decreasing detail:
                                         #   debug5
diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h
index 0b99eaabfd0..0ddd805f0e7 100644
--- a/src/include/postmaster/proctypelist.h
+++ b/src/include/postmaster/proctypelist.h
@@ -25,27 +25,27 @@
  */
 
 /*
- * List of process types (symbol, description, Main function, shmem_attach)
- * entries.
+ * List of process types (symbol, category, description, Main function,
+ * shmem_attach, message level) entries.
  */
 
 
-/* bktype, description, main_func, shmem_attach */
-PG_PROCTYPE(B_ARCHIVER, gettext_noop("archiver"), PgArchiverMain, true)
-PG_PROCTYPE(B_AUTOVAC_LAUNCHER, gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true)
-PG_PROCTYPE(B_AUTOVAC_WORKER, gettext_noop("autovacuum worker"), AutoVacWorkerMain, true)
-PG_PROCTYPE(B_BACKEND, gettext_noop("client backend"), BackendMain, true)
-PG_PROCTYPE(B_BG_WORKER, gettext_noop("background worker"), BackgroundWorkerMain, true)
-PG_PROCTYPE(B_BG_WRITER, gettext_noop("background writer"), BackgroundWriterMain, true)
-PG_PROCTYPE(B_CHECKPOINTER, gettext_noop("checkpointer"), CheckpointerMain, true)
-PG_PROCTYPE(B_DEAD_END_BACKEND, gettext_noop("dead-end client backend"), BackendMain, true)
-PG_PROCTYPE(B_INVALID, gettext_noop("unrecognized"), NULL, false)
-PG_PROCTYPE(B_IO_WORKER, gettext_noop("io worker"), IoWorkerMain, true)
-PG_PROCTYPE(B_LOGGER, gettext_noop("syslogger"), SysLoggerMain, false)
-PG_PROCTYPE(B_SLOTSYNC_WORKER, gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true)
-PG_PROCTYPE(B_STANDALONE_BACKEND, gettext_noop("standalone backend"), NULL, false)
-PG_PROCTYPE(B_STARTUP, gettext_noop("startup"), StartupProcessMain, true)
-PG_PROCTYPE(B_WAL_RECEIVER, gettext_noop("walreceiver"), WalReceiverMain, true)
-PG_PROCTYPE(B_WAL_SENDER, gettext_noop("walsender"), NULL, true)
-PG_PROCTYPE(B_WAL_SUMMARIZER, gettext_noop("walsummarizer"), WalSummarizerMain, true)
-PG_PROCTYPE(B_WAL_WRITER, gettext_noop("walwriter"), WalWriterMain, true)
+/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */
+PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING)
+PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING)
+PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING)
+PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING)
+PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING)
+PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING)
+PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING)
+PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING)
+PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING)
+PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING)
+PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING)
+PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING)
+PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING)
+PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING)
+PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING)
+PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING)
+PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING)
+PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING)
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index bf39878c43e..8acbdba7ff5 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -295,7 +295,7 @@ extern PGDLLIMPORT bool log_duration;
 extern PGDLLIMPORT int log_parameter_max_length;
 extern PGDLLIMPORT int log_parameter_max_length_on_error;
 extern PGDLLIMPORT int log_min_error_statement;
-extern PGDLLIMPORT int log_min_messages;
+extern PGDLLIMPORT int log_min_messages[];
 extern PGDLLIMPORT int client_min_messages;
 extern PGDLLIMPORT int log_min_duration_sample;
 extern PGDLLIMPORT int log_min_duration_statement;
@@ -329,6 +329,8 @@ extern PGDLLIMPORT bool trace_sort;
 extern PGDLLIMPORT bool optimize_bounded_sort;
 #endif
 
+extern PGDLLIMPORT const char *const log_min_messages_process_types[];
+
 /*
  * Declarations for options for enum values
  *
@@ -344,6 +346,7 @@ extern PGDLLIMPORT const struct config_enum_entry archive_mode_options[];
 extern PGDLLIMPORT const struct config_enum_entry dynamic_shared_memory_options[];
 extern PGDLLIMPORT const struct config_enum_entry io_method_options[];
 extern PGDLLIMPORT const struct config_enum_entry recovery_target_action_options[];
+extern PGDLLIMPORT const struct config_enum_entry server_message_level_options[];
 extern PGDLLIMPORT const struct config_enum_entry wal_level_options[];
 extern PGDLLIMPORT const struct config_enum_entry wal_sync_method_options[];
 
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index b6ecb0e769f..9c90670d9b8 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -177,5 +177,7 @@ extern void assign_wal_sync_method(int new_wal_sync_method, void *extra);
 extern bool check_synchronized_standby_slots(char **newval, void **extra,
 											 GucSource source);
 extern void assign_synchronized_standby_slots(const char *newval, void *extra);
+extern bool check_log_min_messages(char **newval, void **extra, GucSource source);
+extern void assign_log_min_messages(const char *newval, void *extra);
 
 #endif							/* GUC_HOOKS_H */
diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out
index d6fb879f500..3b8f44566b2 100644
--- a/src/test/regress/expected/guc.out
+++ b/src/test/regress/expected/guc.out
@@ -935,3 +935,57 @@ SELECT name FROM tab_settings_flags
 (0 rows)
 
 DROP TABLE tab_settings_flags;
+-- Test log_min_messages
+SET log_min_messages TO foo;  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "foo"
+DETAIL:  Unrecognized log level: "foo".
+SET log_min_messages TO fatal;
+SHOW log_min_messages;
+ log_min_messages 
+------------------
+ fatal
+(1 row)
+
+SET log_min_messages TO 'fatal';
+SHOW log_min_messages;
+ log_min_messages 
+------------------
+ fatal
+(1 row)
+
+SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1';  --fail
+ERROR:  invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1"
+DETAIL:  Generic log level was not defined.
+SET log_min_messages TO 'debug1, backend:error, fatal';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "debug1, backend:error, fatal"
+DETAIL:  Generic log level was already assigned.
+SET log_min_messages TO 'backend:error, debug1, backend:warning';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning"
+DETAIL:  Process type "backend" was already assigned.
+SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1"
+DETAIL:  Unrecognized process type: "foo".
+SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1"
+DETAIL:  Unrecognized log level: "bar".
+SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3';
+SHOW log_min_messages;
+                                        log_min_messages                                         
+-------------------------------------------------------------------------------------------------
+ backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3
+(1 row)
+
+SET log_min_messages TO 'warning, autovacuum:debug1';
+SHOW log_min_messages;
+      log_min_messages      
+----------------------------
+ warning, autovacuum:debug1
+(1 row)
+
+SET log_min_messages TO 'autovacuum:debug1, warning';
+SHOW log_min_messages;
+      log_min_messages      
+----------------------------
+ autovacuum:debug1, warning
+(1 row)
+
diff --git a/src/test/regress/sql/guc.sql b/src/test/regress/sql/guc.sql
index bafaf067e82..f7dfc909b42 100644
--- a/src/test/regress/sql/guc.sql
+++ b/src/test/regress/sql/guc.sql
@@ -377,3 +377,21 @@ SELECT name FROM tab_settings_flags
   WHERE no_reset AND NOT no_reset_all
   ORDER BY 1;
 DROP TABLE tab_settings_flags;
+
+-- Test log_min_messages
+SET log_min_messages TO foo;  -- fail
+SET log_min_messages TO fatal;
+SHOW log_min_messages;
+SET log_min_messages TO 'fatal';
+SHOW log_min_messages;
+SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1';  --fail
+SET log_min_messages TO 'debug1, backend:error, fatal';  -- fail
+SET log_min_messages TO 'backend:error, debug1, backend:warning';  -- fail
+SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1';  -- fail
+SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1';  -- fail
+SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3';
+SHOW log_min_messages;
+SET log_min_messages TO 'warning, autovacuum:debug1';
+SHOW log_min_messages;
+SET log_min_messages TO 'autovacuum:debug1, warning';
+SHOW log_min_messages;
-- 
2.39.5



  [text/x-patch] v9-0002-sort-log_min_messages-elements-per-process-type.patch (3.4K, ../../4d496ab8-35e7-4535-8ee1-ed2bb7262172@app.fastmail.com/3-v9-0002-sort-log_min_messages-elements-per-process-type.patch)
  download | inline diff:
From b1e7dc57578cd07fb96678ace2911676f43b6339 Mon Sep 17 00:00:00 2001
From: Euler Taveira <euler@eulerto.com>
Date: Wed, 14 Jan 2026 17:29:09 -0300
Subject: [PATCH v9 2/3] sort log_min_messages elements per process type

The generic log level is always the first element.
---
 src/backend/commands/variable.c   | 50 +++++++++++++++++++++++++++++++
 src/test/regress/expected/guc.out |  4 +--
 2 files changed, 52 insertions(+), 2 deletions(-)

diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index 99f40186533..a450254d1fa 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -42,6 +42,8 @@
 #include "utils/tzparser.h"
 #include "utils/varlena.h"
 
+static int	log_min_messages_cmp(const ListCell *a, const ListCell *b);
+
 /*
  * DATESTYLE
  */
@@ -1289,6 +1291,9 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 {
 	char	   *rawstring;
 	List	   *elemlist;
+	StringInfoData buf;
+	char	   *result;
+	bool		first = true;
 	int			newlevel[BACKEND_NUM_TYPES];
 	bool		assigned[BACKEND_NUM_TYPES];
 	int			genericlevel = -1;	/* -1 means not assigned */
@@ -1455,8 +1460,39 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 			newlevel[i] = genericlevel;
 	}
 
+	/*
+	 * Use a stable representation of log_min_messages. The generic level is
+	 * always the first element and the other elements (type:level) are sorted
+	 * by process type. See log_min_messages_cmp for details.
+	 */
+	list_sort(elemlist, log_min_messages_cmp);
+
+	initStringInfo(&buf);
+	foreach_ptr(char, tok, elemlist)
+	{
+		if (first)
+		{
+			appendStringInfoString(&buf, tok);
+			first = false;
+		}
+		else
+		{
+			appendStringInfo(&buf, ", %s", tok);
+		}
+	}
+
+	result = (char *) guc_malloc(LOG, buf.len + 1);
+	if (!result)
+		return false;
+	memcpy(result, buf.data, buf.len);
+	result[buf.len] = '\0';
+
+	guc_free(*newval);
+	*newval = result;
+
 	guc_free(rawstring);
 	list_free(elemlist);
+	pfree(buf.data);
 
 	/*
 	 * Pass back data for assign_log_min_messages to use.
@@ -1469,6 +1505,20 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 	return true;
 }
 
+static int
+log_min_messages_cmp(const ListCell *a, const ListCell *b)
+{
+	const char *s = lfirst(a);
+	const char *t = lfirst(b);
+
+	if (strchr(s, ':') == NULL)
+		return -1;
+	else if (strchr(t, ':') == NULL)
+		return 1;
+	else
+		return strcmp(s, t);
+}
+
 /*
  * GUC assign_hook for log_min_messages
  */
diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out
index 3b8f44566b2..9a6fd503009 100644
--- a/src/test/regress/expected/guc.out
+++ b/src/test/regress/expected/guc.out
@@ -972,7 +972,7 @@ SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:deb
 SHOW log_min_messages;
                                         log_min_messages                                         
 -------------------------------------------------------------------------------------------------
- backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3
+ fatal, archiver:debug2, autovacuum:debug1, backend:error, checkpointer:debug3, walsender:debug3
 (1 row)
 
 SET log_min_messages TO 'warning, autovacuum:debug1';
@@ -986,6 +986,6 @@ SET log_min_messages TO 'autovacuum:debug1, warning';
 SHOW log_min_messages;
       log_min_messages      
 ----------------------------
- autovacuum:debug1, warning
+ warning, autovacuum:debug1
 (1 row)
 
-- 
2.39.5



  [text/x-patch] v9-0003-Assign-backend-type-earlier.patch (8.4K, ../../4d496ab8-35e7-4535-8ee1-ed2bb7262172@app.fastmail.com/4-v9-0003-Assign-backend-type-earlier.patch)
  download | inline diff:
From 55d685515d9459d07863b8e9845ed9620c6eff21 Mon Sep 17 00:00:00 2001
From: Euler Taveira <euler@eulerto.com>
Date: Wed, 21 Jan 2026 10:42:02 -0300
Subject: [PATCH v9 3/3] Assign backend type earlier
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Instead of assigning the backend type into the Main function of each
postmaster child, do it earlier (after fork()). The backend type is
already known by postmaster_child_launch() before it calls fork(). This
reduces the time frame that MyBackendType is correct. (This is important
for log_min_messages per backend type that relies on a known
MyBackendType to decide if it writes a log message or not.)

Author: Euler Taveira <euler@eulerto.com>
Author: Álvaro Herrera <alvherre@kurilemu.de>
Discussion: https://www.postgresql.org/message-id/flat/e85c6671-1600-4112-8887-f97a8a5d07b2@app.fastmail.com
---
 src/backend/postmaster/autovacuum.c        | 2 --
 src/backend/postmaster/bgworker.c          | 1 -
 src/backend/postmaster/bgwriter.c          | 1 -
 src/backend/postmaster/checkpointer.c      | 1 -
 src/backend/postmaster/launch_backend.c    | 3 +++
 src/backend/postmaster/pgarch.c            | 1 -
 src/backend/postmaster/startup.c           | 1 -
 src/backend/postmaster/syslogger.c         | 1 -
 src/backend/postmaster/walsummarizer.c     | 1 -
 src/backend/postmaster/walwriter.c         | 1 -
 src/backend/replication/logical/slotsync.c | 2 --
 src/backend/replication/walreceiver.c      | 1 -
 src/backend/storage/aio/method_worker.c    | 1 -
 src/backend/utils/error/elog.c             | 7 ++++++-
 14 files changed, 9 insertions(+), 15 deletions(-)

diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 22379de1e31..6fde740465f 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -385,7 +385,6 @@ AutoVacLauncherMain(const void *startup_data, size_t startup_data_len)
 		PostmasterContext = NULL;
 	}
 
-	MyBackendType = B_AUTOVAC_LAUNCHER;
 	init_ps_display(NULL);
 
 	ereport(DEBUG1,
@@ -1398,7 +1397,6 @@ AutoVacWorkerMain(const void *startup_data, size_t startup_data_len)
 		PostmasterContext = NULL;
 	}
 
-	MyBackendType = B_AUTOVAC_WORKER;
 	init_ps_display(NULL);
 
 	Assert(GetProcessingMode() == InitProcessing);
diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c
index 65deabe91a7..5144d70095c 100644
--- a/src/backend/postmaster/bgworker.c
+++ b/src/backend/postmaster/bgworker.c
@@ -753,7 +753,6 @@ BackgroundWorkerMain(const void *startup_data, size_t startup_data_len)
 	}
 
 	MyBgworkerEntry = worker;
-	MyBackendType = B_BG_WORKER;
 	init_ps_display(worker->bgw_name);
 
 	Assert(GetProcessingMode() == InitProcessing);
diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c
index 80e3088fc7e..0956bd39a85 100644
--- a/src/backend/postmaster/bgwriter.c
+++ b/src/backend/postmaster/bgwriter.c
@@ -94,7 +94,6 @@ BackgroundWriterMain(const void *startup_data, size_t startup_data_len)
 
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_BG_WRITER;
 	AuxiliaryProcessMainCommon();
 
 	/*
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 6482c21b8f9..e03c19123bc 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -199,7 +199,6 @@ CheckpointerMain(const void *startup_data, size_t startup_data_len)
 
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_CHECKPOINTER;
 	AuxiliaryProcessMainCommon();
 
 	CheckpointerShmem->checkpointer_pid = MyProcPid;
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 756a0237deb..66b548d0e4d 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -224,6 +224,8 @@ postmaster_child_launch(BackendType child_type, int child_slot,
 	pid = fork_process();
 	if (pid == 0)				/* child */
 	{
+		MyBackendType = child_type;
+
 		/* Capture and transfer timings that may be needed for logging */
 		if (IsExternalConnectionBackend(child_type))
 		{
@@ -608,6 +610,7 @@ SubPostmasterMain(int argc, char *argv[])
 	child_type = (BackendType) atoi(child_kind);
 	if (child_type <= B_INVALID || child_type > BACKEND_NUM_TYPES - 1)
 		elog(ERROR, "unknown child kind %s", child_kind);
+	MyBackendType = child_type;
 
 	/* Read in the variables file */
 	read_backend_variables(argv[2], &startup_data, &startup_data_len);
diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c
index 1a20387c4bd..82731e452fc 100644
--- a/src/backend/postmaster/pgarch.c
+++ b/src/backend/postmaster/pgarch.c
@@ -222,7 +222,6 @@ PgArchiverMain(const void *startup_data, size_t startup_data_len)
 {
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_ARCHIVER;
 	AuxiliaryProcessMainCommon();
 
 	/*
diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c
index a1a4f65f9a9..cdbe53dd262 100644
--- a/src/backend/postmaster/startup.c
+++ b/src/backend/postmaster/startup.c
@@ -217,7 +217,6 @@ StartupProcessMain(const void *startup_data, size_t startup_data_len)
 {
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_STARTUP;
 	AuxiliaryProcessMainCommon();
 
 	/* Arrange to clean up at startup process exit */
diff --git a/src/backend/postmaster/syslogger.c b/src/backend/postmaster/syslogger.c
index 1c443b3d126..86c5e376b40 100644
--- a/src/backend/postmaster/syslogger.c
+++ b/src/backend/postmaster/syslogger.c
@@ -206,7 +206,6 @@ SysLoggerMain(const void *startup_data, size_t startup_data_len)
 
 	now = MyStartTime;
 
-	MyBackendType = B_LOGGER;
 	init_ps_display(NULL);
 
 	/*
diff --git a/src/backend/postmaster/walsummarizer.c b/src/backend/postmaster/walsummarizer.c
index c3d56c866d3..2d8f57099fd 100644
--- a/src/backend/postmaster/walsummarizer.c
+++ b/src/backend/postmaster/walsummarizer.c
@@ -234,7 +234,6 @@ WalSummarizerMain(const void *startup_data, size_t startup_data_len)
 
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_WAL_SUMMARIZER;
 	AuxiliaryProcessMainCommon();
 
 	ereport(DEBUG1,
diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c
index 38ec8a4c8c7..23e79a32345 100644
--- a/src/backend/postmaster/walwriter.c
+++ b/src/backend/postmaster/walwriter.c
@@ -94,7 +94,6 @@ WalWriterMain(const void *startup_data, size_t startup_data_len)
 
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_WAL_WRITER;
 	AuxiliaryProcessMainCommon();
 
 	/*
diff --git a/src/backend/replication/logical/slotsync.c b/src/backend/replication/logical/slotsync.c
index 73fc51ea53e..b53f888b804 100644
--- a/src/backend/replication/logical/slotsync.c
+++ b/src/backend/replication/logical/slotsync.c
@@ -1540,8 +1540,6 @@ ReplSlotSyncWorkerMain(const void *startup_data, size_t startup_data_len)
 
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_SLOTSYNC_WORKER;
-
 	init_ps_display(NULL);
 
 	Assert(GetProcessingMode() == InitProcessing);
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index a41453530a1..8dbd8cb2009 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -169,7 +169,6 @@ WalReceiverMain(const void *startup_data, size_t startup_data_len)
 
 	Assert(startup_data_len == 0);
 
-	MyBackendType = B_WAL_RECEIVER;
 	AuxiliaryProcessMainCommon();
 
 	/*
diff --git a/src/backend/storage/aio/method_worker.c b/src/backend/storage/aio/method_worker.c
index d7c144cd8f7..d9617c20e76 100644
--- a/src/backend/storage/aio/method_worker.c
+++ b/src/backend/storage/aio/method_worker.c
@@ -390,7 +390,6 @@ IoWorkerMain(const void *startup_data, size_t startup_data_len)
 	volatile int error_errno = 0;
 	char		cmd[128];
 
-	MyBackendType = B_IO_WORKER;
 	AuxiliaryProcessMainCommon();
 
 	pqsignal(SIGHUP, SignalHandlerForConfigReload);
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 59975309a4e..f35ec0429b2 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -2779,7 +2779,12 @@ get_backend_type_for_log(void)
 	if (MyProcPid == PostmasterPid)
 		backend_type_str = "postmaster";
 	else if (MyBackendType == B_BG_WORKER)
-		backend_type_str = MyBgworkerEntry->bgw_type;
+	{
+		if (MyBgworkerEntry && MyBgworkerEntry->bgw_type[0] != '\0')
+			backend_type_str = MyBgworkerEntry->bgw_type;
+		else
+			backend_type_str = "early bgworker";
+	}
 	else
 		backend_type_str = GetBackendTypeDesc(MyBackendType);
 
-- 
2.39.5



^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2026-01-30 19:27  surya poondla <suryapoondla4@gmail.com>
  parent: Euler Taveira <euler@eulerto.com>
  2 siblings, 1 reply; 58+ messages in thread

From: surya poondla @ 2026-01-30 19:27 UTC (permalink / raw)
  To: Euler Taveira <euler@eulerto.com>; +Cc: pgsql-hackers@lists.postgresql.org

Hi Euler and team,

This thread is really interesting and I think the proposal clearly improves
overall debuggability in Postgres.

I am still catching up on the full discussion and the latest v9 patchset,
but I plan to review the patches in detail and will follow-up with comments
or questions, once I am done.

Thanks for working on this and incorporating the feedback so far.

Regards,
Surya Poondla

^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2026-02-06 11:05  Alvaro Herrera <alvherre@alvh.no-ip.org>
  parent: Euler Taveira <euler@eulerto.com>
  0 siblings, 1 reply; 58+ messages in thread

From: Alvaro Herrera @ 2026-02-06 11:05 UTC (permalink / raw)
  To: Euler Taveira <euler@eulerto.com>; +Cc: japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org, Chao Li <li.evan.chao@gmail.com>

Here are some fixups for the main patch.  I include yours so that CI
will test this one.

* I found some errdetail messages unclear, so I reworded them.
Specifically you had
 ERROR:  invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning"
 DETAIL:  Process type "backend" was already assigned.
which is not completely incorrect but looks weird from the user
perspective.  I changed it to
 DETAIL:  Redundant log level specification for process type "backend".

Also,
 ERROR:  invalid value for parameter "log_min_messages": "debug1, backend:error, fatal"
 DETAIL:  Generic log level was already assigned.
I changed to
 DETAIL:  Redundant specification of default log level.

and added process type in this one:
 ERROR:  invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1"
 DETAIL:  Unrecognized log level: "bar".
changed to
 DETAIL:  Unrecognized log level for process type "checkpointer": "bar".
(This one can show an invalid process type if the level is also bogus.
I don't think that's a terrible problem.)

* I'm not fond of the term "generic", so I changed it to "default",
which IMO makes more sense from the user point of view.

* There seemed to be too much guc_mallocking[*] going on; for instance for
ptype.  I simplified that by just overwriting the : to a '\0', then we
can use the original string for pg_strcasecmp().  At the bottom of the
loop we restore the ':', so that the code below can reuse the original
values.  This is okay because we have already mallocked a copy for
SplitGUCList.

* There's no reason to add a WARNING to every process type in the
proctypelist.h table.  We can just set the values to WARNING in the
log_min_messages initializer in guc_tables.c.

* There's no reason AFAICS to have log_min_messages_process_types in
guc_tables.c; nobody else uses it.  I made it a local array of char*
inside the check_log_min_messages function.

* There's no need to initialize the newlevel[] array, since we're going
to assign values to every individual element at one point or another.
And for the assigned[] array, we don't need to set the value of each to
'false' in a loop; we can just initialize to '{0}' and the compiler will
do the needful.  With this we can remove the loop entirely.

* "guc_free()+list_free()+return false" the whole time in the various
failure places across the loop was tiresome.  I put them all in a single
place and jump there with a goto.

* in foreach() loops, tracking of a 'bool first' is unnecessary.  You
can just compare foreach_current_index() with 0.

* Rewrote docs and comments

-- 
Álvaro Herrera         PostgreSQL Developer  —  https://www.EnterpriseDB.com/

Attachments:

  [text/x-diff] v10-0001-log_min_messages-per-process-type.patch (27.4K, ../../202602061004.yyesvyagjux7@alvherre.pgsql/2-v10-0001-log_min_messages-per-process-type.patch)
  download | inline diff:
From a05d1e59324b7c88aeb52c009b4e03aaeafd2ffc Mon Sep 17 00:00:00 2001
From: Euler Taveira <euler@eulerto.com>
Date: Tue, 4 Nov 2025 17:18:20 -0300
Subject: [PATCH v10 1/2] log_min_messages per process type

Change log_min_messages from a single element to a comma-separated list
of elements. Each element is type:level. The types are archiver,
autovacuum (includes launcher and workers), backend, bgworker, bgwriter,
checkpointer, ioworker, postmaster, syslogger, slotsyncworker, startup,
walreceiver, walsender, walsummarizer and walwriter. A single log level
should be part of this list and it is applied as a final step to the
process types that were not informed.

The old syntax (a single log level) is still accepted for backward
compatibility. In this case, it means the informed log level is applied
for all process types. The default log level is the same (WARNING) for
all process types.
---
 doc/src/sgml/config.sgml                      |  42 ++-
 src/backend/commands/extension.c              |   2 +-
 src/backend/commands/variable.c               | 256 ++++++++++++++++++
 src/backend/postmaster/launch_backend.c       |   2 +-
 src/backend/utils/error/elog.c                |   2 +-
 src/backend/utils/init/miscinit.c             |   2 +-
 src/backend/utils/misc/guc_parameters.dat     |  10 +-
 src/backend/utils/misc/guc_tables.c           |  21 +-
 src/backend/utils/misc/postgresql.conf.sample |   2 +
 src/include/postmaster/proctypelist.h         |  42 +--
 src/include/utils/guc.h                       |   5 +-
 src/include/utils/guc_hooks.h                 |   2 +
 src/test/regress/expected/guc.out             |  54 ++++
 src/test/regress/sql/guc.sql                  |  18 ++
 14 files changed, 419 insertions(+), 41 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 5560b95ee60..cbc748a3636 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -7083,7 +7083,7 @@ local0.*    /var/log/postgresql
      <variablelist>
 
      <varlistentry id="guc-log-min-messages" xreflabel="log_min_messages">
-      <term><varname>log_min_messages</varname> (<type>enum</type>)
+      <term><varname>log_min_messages</varname> (<type>string</type>)
       <indexterm>
        <primary><varname>log_min_messages</varname> configuration parameter</primary>
       </indexterm>
@@ -7092,14 +7092,38 @@ local0.*    /var/log/postgresql
        <para>
         Controls which <link linkend="runtime-config-severity-levels">message
         levels</link> are written to the server log.
-        Valid values are <literal>DEBUG5</literal>, <literal>DEBUG4</literal>,
-        <literal>DEBUG3</literal>, <literal>DEBUG2</literal>, <literal>DEBUG1</literal>,
-        <literal>INFO</literal>, <literal>NOTICE</literal>, <literal>WARNING</literal>,
-        <literal>ERROR</literal>, <literal>LOG</literal>, <literal>FATAL</literal>, and
-        <literal>PANIC</literal>.  Each level includes all the levels that
-        follow it.  The later the level, the fewer messages are sent
-        to the log.  The default is <literal>WARNING</literal>.  Note that
-        <literal>LOG</literal> has a different rank here than in
+        Valid values are a comma-separated list of <literal>type:level</literal>
+        and a single <literal>level</literal>. The list allows it to use
+        different levels per process type. Only the single <literal>level</literal>
+        is mandatory (order does not matter) and it is assigned to the process
+        types that are not specified in the list.
+        Valid process types are listed in the table below, each corresponding to
+        either postmaster, an auxiliary process type or a backend.
+        <simplelist type="vert" columns="4">
+         <member><literal>archiver</literal></member>
+         <member><literal>autovacuum</literal></member>
+         <member><literal>backend</literal></member>
+         <member><literal>bgworker</literal></member>
+         <member><literal>bgwriter</literal></member>
+         <member><literal>checkpointer</literal></member>
+         <member><literal>ioworker</literal></member>
+         <member><literal>postmaster</literal></member>
+         <member><literal>syslogger</literal></member>
+         <member><literal>slotsyncworker</literal></member>
+         <member><literal>startup</literal></member>
+         <member><literal>walreceiver</literal></member>
+         <member><literal>walsender</literal></member>
+         <member><literal>walsummarizer</literal></member>
+         <member><literal>walwriter</literal></member>
+        </simplelist>
+        Valid <literal>level</literal> values are <literal>DEBUG5</literal>,
+        <literal>DEBUG4</literal>, <literal>DEBUG3</literal>, <literal>DEBUG2</literal>,
+        <literal>DEBUG1</literal>, <literal>INFO</literal>, <literal>NOTICE</literal>,
+        <literal>WARNING</literal>, <literal>ERROR</literal>, <literal>LOG</literal>,
+        <literal>FATAL</literal>, and <literal>PANIC</literal>.  Each level includes
+        all the levels that follow it.  The later the level, the fewer messages are sent
+        to the log.  The default is <literal>WARNING</literal> for all process types.
+        Note that <literal>LOG</literal> has a different rank here than in
         <xref linkend="guc-client-min-messages"/>.
         Only superusers and users with the appropriate <literal>SET</literal>
         privilege can change this setting.
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 596105ee078..688f1874f31 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1191,7 +1191,7 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
 		(void) set_config_option("client_min_messages", "warning",
 								 PGC_USERSET, PGC_S_SESSION,
 								 GUC_ACTION_SAVE, true, 0, false);
-	if (log_min_messages < WARNING)
+	if (log_min_messages[MyBackendType] < WARNING)
 		(void) set_config_option_ext("log_min_messages", "warning",
 									 PGC_SUSET, PGC_S_SESSION,
 									 BOOTSTRAP_SUPERUSERID,
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index 4440aff4925..a450254d1fa 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -34,6 +34,7 @@
 #include "utils/backend_status.h"
 #include "utils/datetime.h"
 #include "utils/fmgrprotos.h"
+#include "utils/guc.h"
 #include "utils/guc_hooks.h"
 #include "utils/snapmgr.h"
 #include "utils/syscache.h"
@@ -41,6 +42,8 @@
 #include "utils/tzparser.h"
 #include "utils/varlena.h"
 
+static int	log_min_messages_cmp(const ListCell *a, const ListCell *b);
+
 /*
  * DATESTYLE
  */
@@ -1272,3 +1275,256 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source)
 
 	return true;
 }
+
+/*
+ * GUC check_hook for log_min_messages
+ *
+ * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE
+ * is log_min_messages_process_types.  LEVEL is server_message_level_options. A
+ * single LEVEL element should be part of this list and it is applied as a
+ * final step to the process types that are not specified. For backward
+ * compatibility, the old syntax is still accepted and it means to apply the
+ * LEVEL for all process types.
+ */
+bool
+check_log_min_messages(char **newval, void **extra, GucSource source)
+{
+	char	   *rawstring;
+	List	   *elemlist;
+	StringInfoData buf;
+	char	   *result;
+	bool		first = true;
+	int			newlevel[BACKEND_NUM_TYPES];
+	bool		assigned[BACKEND_NUM_TYPES];
+	int			genericlevel = -1;	/* -1 means not assigned */
+
+	/* Initialize the array. */
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+	{
+		newlevel[i] = WARNING;
+		assigned[i] = false;
+	}
+
+	/* Need a modifiable copy of string. */
+	rawstring = guc_strdup(LOG, *newval);
+
+	/* Parse string into list of identifiers. */
+	if (!SplitGUCList(rawstring, ',', &elemlist))
+	{
+		/* syntax error in list */
+		GUC_check_errdetail("List syntax is invalid.");
+		list_free(elemlist);
+		guc_free(rawstring);
+		return false;
+	}
+
+	/* Validate and assign log level and process type. */
+	foreach_ptr(char, tok, elemlist)
+	{
+		char	   *sep;
+		const struct config_enum_entry *entry;
+
+		/*
+		 * Check whether there is a process type following the log level. If
+		 * there is no separator, it means this is the generic log level. The
+		 * generic log level will be assigned to the process types that were
+		 * not informed.
+		 */
+		sep = strchr(tok, ':');
+		if (sep == NULL)
+		{
+			bool		found = false;
+
+			/* Reject duplicates for generic log level. */
+			if (genericlevel != -1)
+			{
+				GUC_check_errdetail("Generic log level was already assigned.");
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			/* Is the log level valid? */
+			for (entry = server_message_level_options; entry && entry->name; entry++)
+			{
+				if (pg_strcasecmp(entry->name, tok) == 0)
+				{
+					genericlevel = entry->val;
+					found = true;
+					break;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized log level: \"%s\".", tok);
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+		}
+		else
+		{
+			char	   *loglevel;
+			char	   *ptype;
+			bool		found = false;
+
+			ptype = guc_malloc(LOG, (sep - tok) + 1);
+			if (!ptype)
+			{
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+			memcpy(ptype, tok, sep - tok);
+			ptype[sep - tok] = '\0';
+			loglevel = sep + 1;
+
+			/* Is the log level valid? */
+			for (entry = server_message_level_options; entry && entry->name; entry++)
+			{
+				if (pg_strcasecmp(entry->name, loglevel) == 0)
+				{
+					found = true;
+					break;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel);
+				guc_free(ptype);
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			/*
+			 * Is the process type name valid? There might be multiple entries
+			 * per process type, don't bail out because it can assign the
+			 * value for multiple entries.
+			 */
+			found = false;
+			for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+			{
+				if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0)
+				{
+					/* Reject duplicates for a process type. */
+					if (assigned[i])
+					{
+						GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype);
+						guc_free(ptype);
+						guc_free(rawstring);
+						list_free(elemlist);
+						return false;
+					}
+
+					newlevel[i] = entry->val;
+					assigned[i] = true;
+					found = true;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype);
+				guc_free(ptype);
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			guc_free(ptype);
+		}
+	}
+
+	/*
+	 * The generic log level must be specified. It is the fallback value.
+	 */
+	if (genericlevel == -1)
+	{
+		GUC_check_errdetail("Generic log level was not defined.");
+		guc_free(rawstring);
+		list_free(elemlist);
+		return false;
+	}
+
+	/*
+	 * Apply the generic log level after all of the specific process types
+	 * have been assigned. Hence, it doesn't matter the order you specify the
+	 * generic log level, the final result will be the same.
+	 */
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+	{
+		if (!assigned[i])
+			newlevel[i] = genericlevel;
+	}
+
+	/*
+	 * Use a stable representation of log_min_messages. The generic level is
+	 * always the first element and the other elements (type:level) are sorted
+	 * by process type. See log_min_messages_cmp for details.
+	 */
+	list_sort(elemlist, log_min_messages_cmp);
+
+	initStringInfo(&buf);
+	foreach_ptr(char, tok, elemlist)
+	{
+		if (first)
+		{
+			appendStringInfoString(&buf, tok);
+			first = false;
+		}
+		else
+		{
+			appendStringInfo(&buf, ", %s", tok);
+		}
+	}
+
+	result = (char *) guc_malloc(LOG, buf.len + 1);
+	if (!result)
+		return false;
+	memcpy(result, buf.data, buf.len);
+	result[buf.len] = '\0';
+
+	guc_free(*newval);
+	*newval = result;
+
+	guc_free(rawstring);
+	list_free(elemlist);
+	pfree(buf.data);
+
+	/*
+	 * Pass back data for assign_log_min_messages to use.
+	 */
+	*extra = guc_malloc(LOG, BACKEND_NUM_TYPES * sizeof(int));
+	if (!*extra)
+		return false;
+	memcpy(*extra, newlevel, BACKEND_NUM_TYPES * sizeof(int));
+
+	return true;
+}
+
+static int
+log_min_messages_cmp(const ListCell *a, const ListCell *b)
+{
+	const char *s = lfirst(a);
+	const char *t = lfirst(b);
+
+	if (strchr(s, ':') == NULL)
+		return -1;
+	else if (strchr(t, ':') == NULL)
+		return 1;
+	else
+		return strcmp(s, t);
+}
+
+/*
+ * GUC assign_hook for log_min_messages
+ */
+void
+assign_log_min_messages(const char *newval, void *extra)
+{
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+		log_min_messages[i] = ((int *) extra)[i];
+}
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 926fd6f2700..08553c0f024 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -178,7 +178,7 @@ typedef struct
 } child_process_kind;
 
 static child_process_kind child_process_kinds[] = {
-#define PG_PROCTYPE(bktype, description, main_func, shmem_attach) \
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
 	[bktype] = {description, main_func, shmem_attach},
 #include "postmaster/proctypelist.h"
 #undef PG_PROCTYPE
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index e6a4ef99059..4e9143b6e6f 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -235,7 +235,7 @@ is_log_level_output(int elevel, int log_min_level)
 static inline bool
 should_output_to_server(int elevel)
 {
-	return is_log_level_output(elevel, log_min_messages);
+	return is_log_level_output(elevel, log_min_messages[MyBackendType]);
 }
 
 /*
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index 563f20374ff..efba20fa8b9 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType)
 
 	switch (backendType)
 	{
-#define PG_PROCTYPE(bktype, description, main_func, shmem_attach)	\
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages)	\
 		case bktype: backendDesc = description; break;
 #include "postmaster/proctypelist.h"
 #undef PG_PROCTYPE
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index f0260e6e412..3297c4bc30c 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -1686,12 +1686,14 @@
   options => 'server_message_level_options',
 },
 
-{ name => 'log_min_messages', type => 'enum', context => 'PGC_SUSET', group => 'LOGGING_WHEN',
+{ name => 'log_min_messages', type => 'string', context => 'PGC_SUSET', group => 'LOGGING_WHEN',
   short_desc => 'Sets the message levels that are logged.',
   long_desc => 'Each level includes all the levels that follow it. The later the level, the fewer messages are sent.',
-  variable => 'log_min_messages',
-  boot_val => 'WARNING',
-  options => 'server_message_level_options',
+  flags => 'GUC_LIST_INPUT',
+  variable => 'log_min_messages_string',
+  boot_val => '"WARNING"',
+  check_hook => 'check_log_min_messages',
+  assign_hook => 'assign_log_min_messages',
 },
 
 { name => 'log_parameter_max_length', type => 'int', context => 'PGC_SUSET', group => 'LOGGING_WHAT',
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 13c569d8790..0aebb9c8e24 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -146,7 +146,7 @@ static const struct config_enum_entry client_message_level_options[] = {
 	{NULL, 0, false}
 };
 
-static const struct config_enum_entry server_message_level_options[] = {
+const struct config_enum_entry server_message_level_options[] = {
 	{"debug5", DEBUG5, false},
 	{"debug4", DEBUG4, false},
 	{"debug3", DEBUG3, false},
@@ -537,7 +537,6 @@ static bool standard_conforming_strings = true;
 bool		current_role_is_superuser;
 
 int			log_min_error_statement = ERROR;
-int			log_min_messages = WARNING;
 int			client_min_messages = NOTICE;
 int			log_min_duration_sample = -1;
 int			log_min_duration_statement = -1;
@@ -595,6 +594,7 @@ static char *server_version_string;
 static int	server_version_num;
 static char *debug_io_direct_string;
 static char *restrict_nonsystem_relation_kind_string;
+static char *log_min_messages_string;
 
 #ifdef HAVE_SYSLOG
 #define	DEFAULT_SYSLOG_FACILITY LOG_LOCAL0
@@ -647,6 +647,23 @@ char	   *role_string;
 /* should be static, but guc.c needs to get at this */
 bool		in_hot_standby_guc;
 
+/*
+ * log_min_messages
+ */
+int			log_min_messages[] = {
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
+	[bktype] = log_min_messages,
+#include "postmaster/proctypelist.h"
+#undef PG_PROCTYPE
+};
+
+const char *const log_min_messages_process_types[] = {
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
+	[bktype] = bkcategory,
+#include "postmaster/proctypelist.h"
+#undef PG_PROCTYPE
+};
+
 
 /*
  * Displayable names for context types (enum GucContext)
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index c4f92fcdac8..ad0804ccb9f 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -541,6 +541,8 @@
                                         #   log
                                         #   fatal
                                         #   panic
+                                        #   and an optional comma-separated list
+                                        #   of type:level
 
 #log_min_error_statement = error        # values in order of decreasing detail:
                                         #   debug5
diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h
index 0b99eaabfd0..0ddd805f0e7 100644
--- a/src/include/postmaster/proctypelist.h
+++ b/src/include/postmaster/proctypelist.h
@@ -25,27 +25,27 @@
  */
 
 /*
- * List of process types (symbol, description, Main function, shmem_attach)
- * entries.
+ * List of process types (symbol, category, description, Main function,
+ * shmem_attach, message level) entries.
  */
 
 
-/* bktype, description, main_func, shmem_attach */
-PG_PROCTYPE(B_ARCHIVER, gettext_noop("archiver"), PgArchiverMain, true)
-PG_PROCTYPE(B_AUTOVAC_LAUNCHER, gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true)
-PG_PROCTYPE(B_AUTOVAC_WORKER, gettext_noop("autovacuum worker"), AutoVacWorkerMain, true)
-PG_PROCTYPE(B_BACKEND, gettext_noop("client backend"), BackendMain, true)
-PG_PROCTYPE(B_BG_WORKER, gettext_noop("background worker"), BackgroundWorkerMain, true)
-PG_PROCTYPE(B_BG_WRITER, gettext_noop("background writer"), BackgroundWriterMain, true)
-PG_PROCTYPE(B_CHECKPOINTER, gettext_noop("checkpointer"), CheckpointerMain, true)
-PG_PROCTYPE(B_DEAD_END_BACKEND, gettext_noop("dead-end client backend"), BackendMain, true)
-PG_PROCTYPE(B_INVALID, gettext_noop("unrecognized"), NULL, false)
-PG_PROCTYPE(B_IO_WORKER, gettext_noop("io worker"), IoWorkerMain, true)
-PG_PROCTYPE(B_LOGGER, gettext_noop("syslogger"), SysLoggerMain, false)
-PG_PROCTYPE(B_SLOTSYNC_WORKER, gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true)
-PG_PROCTYPE(B_STANDALONE_BACKEND, gettext_noop("standalone backend"), NULL, false)
-PG_PROCTYPE(B_STARTUP, gettext_noop("startup"), StartupProcessMain, true)
-PG_PROCTYPE(B_WAL_RECEIVER, gettext_noop("walreceiver"), WalReceiverMain, true)
-PG_PROCTYPE(B_WAL_SENDER, gettext_noop("walsender"), NULL, true)
-PG_PROCTYPE(B_WAL_SUMMARIZER, gettext_noop("walsummarizer"), WalSummarizerMain, true)
-PG_PROCTYPE(B_WAL_WRITER, gettext_noop("walwriter"), WalWriterMain, true)
+/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */
+PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING)
+PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING)
+PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING)
+PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING)
+PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING)
+PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING)
+PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING)
+PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING)
+PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING)
+PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING)
+PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING)
+PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING)
+PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING)
+PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING)
+PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING)
+PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING)
+PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING)
+PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING)
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index bf39878c43e..8acbdba7ff5 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -295,7 +295,7 @@ extern PGDLLIMPORT bool log_duration;
 extern PGDLLIMPORT int log_parameter_max_length;
 extern PGDLLIMPORT int log_parameter_max_length_on_error;
 extern PGDLLIMPORT int log_min_error_statement;
-extern PGDLLIMPORT int log_min_messages;
+extern PGDLLIMPORT int log_min_messages[];
 extern PGDLLIMPORT int client_min_messages;
 extern PGDLLIMPORT int log_min_duration_sample;
 extern PGDLLIMPORT int log_min_duration_statement;
@@ -329,6 +329,8 @@ extern PGDLLIMPORT bool trace_sort;
 extern PGDLLIMPORT bool optimize_bounded_sort;
 #endif
 
+extern PGDLLIMPORT const char *const log_min_messages_process_types[];
+
 /*
  * Declarations for options for enum values
  *
@@ -344,6 +346,7 @@ extern PGDLLIMPORT const struct config_enum_entry archive_mode_options[];
 extern PGDLLIMPORT const struct config_enum_entry dynamic_shared_memory_options[];
 extern PGDLLIMPORT const struct config_enum_entry io_method_options[];
 extern PGDLLIMPORT const struct config_enum_entry recovery_target_action_options[];
+extern PGDLLIMPORT const struct config_enum_entry server_message_level_options[];
 extern PGDLLIMPORT const struct config_enum_entry wal_level_options[];
 extern PGDLLIMPORT const struct config_enum_entry wal_sync_method_options[];
 
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index b6ecb0e769f..9c90670d9b8 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -177,5 +177,7 @@ extern void assign_wal_sync_method(int new_wal_sync_method, void *extra);
 extern bool check_synchronized_standby_slots(char **newval, void **extra,
 											 GucSource source);
 extern void assign_synchronized_standby_slots(const char *newval, void *extra);
+extern bool check_log_min_messages(char **newval, void **extra, GucSource source);
+extern void assign_log_min_messages(const char *newval, void *extra);
 
 #endif							/* GUC_HOOKS_H */
diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out
index d6fb879f500..9a6fd503009 100644
--- a/src/test/regress/expected/guc.out
+++ b/src/test/regress/expected/guc.out
@@ -935,3 +935,57 @@ SELECT name FROM tab_settings_flags
 (0 rows)
 
 DROP TABLE tab_settings_flags;
+-- Test log_min_messages
+SET log_min_messages TO foo;  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "foo"
+DETAIL:  Unrecognized log level: "foo".
+SET log_min_messages TO fatal;
+SHOW log_min_messages;
+ log_min_messages 
+------------------
+ fatal
+(1 row)
+
+SET log_min_messages TO 'fatal';
+SHOW log_min_messages;
+ log_min_messages 
+------------------
+ fatal
+(1 row)
+
+SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1';  --fail
+ERROR:  invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1"
+DETAIL:  Generic log level was not defined.
+SET log_min_messages TO 'debug1, backend:error, fatal';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "debug1, backend:error, fatal"
+DETAIL:  Generic log level was already assigned.
+SET log_min_messages TO 'backend:error, debug1, backend:warning';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning"
+DETAIL:  Process type "backend" was already assigned.
+SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1"
+DETAIL:  Unrecognized process type: "foo".
+SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1"
+DETAIL:  Unrecognized log level: "bar".
+SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3';
+SHOW log_min_messages;
+                                        log_min_messages                                         
+-------------------------------------------------------------------------------------------------
+ fatal, archiver:debug2, autovacuum:debug1, backend:error, checkpointer:debug3, walsender:debug3
+(1 row)
+
+SET log_min_messages TO 'warning, autovacuum:debug1';
+SHOW log_min_messages;
+      log_min_messages      
+----------------------------
+ warning, autovacuum:debug1
+(1 row)
+
+SET log_min_messages TO 'autovacuum:debug1, warning';
+SHOW log_min_messages;
+      log_min_messages      
+----------------------------
+ warning, autovacuum:debug1
+(1 row)
+
diff --git a/src/test/regress/sql/guc.sql b/src/test/regress/sql/guc.sql
index bafaf067e82..f7dfc909b42 100644
--- a/src/test/regress/sql/guc.sql
+++ b/src/test/regress/sql/guc.sql
@@ -377,3 +377,21 @@ SELECT name FROM tab_settings_flags
   WHERE no_reset AND NOT no_reset_all
   ORDER BY 1;
 DROP TABLE tab_settings_flags;
+
+-- Test log_min_messages
+SET log_min_messages TO foo;  -- fail
+SET log_min_messages TO fatal;
+SHOW log_min_messages;
+SET log_min_messages TO 'fatal';
+SHOW log_min_messages;
+SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1';  --fail
+SET log_min_messages TO 'debug1, backend:error, fatal';  -- fail
+SET log_min_messages TO 'backend:error, debug1, backend:warning';  -- fail
+SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1';  -- fail
+SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1';  -- fail
+SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3';
+SHOW log_min_messages;
+SET log_min_messages TO 'warning, autovacuum:debug1';
+SHOW log_min_messages;
+SET log_min_messages TO 'autovacuum:debug1, warning';
+SHOW log_min_messages;
-- 
2.47.3

  [text/x-diff] v10-0002-review.patch (17.6K, ../../202602061004.yyesvyagjux7@alvherre.pgsql/3-v10-0002-review.patch)
  download | inline diff:
From 668c6e24e58f1fd9a75bb6040990d26f89a52513 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Herrera?= <alvherre@kurilemu.de>
Date: Fri, 6 Feb 2026 10:24:29 +0100
Subject: [PATCH v10 2/2] review

---
 doc/src/sgml/config.sgml                |  18 ++--
 src/backend/commands/variable.c         | 127 +++++++++++-------------
 src/backend/postmaster/launch_backend.c |   2 +-
 src/backend/utils/init/miscinit.c       |   2 +-
 src/backend/utils/misc/guc_tables.c     |  14 +--
 src/include/postmaster/proctypelist.h   |  38 +++----
 src/test/regress/expected/guc.out       |   8 +-
 7 files changed, 93 insertions(+), 116 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index cbc748a3636..984194ca758 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -7090,15 +7090,15 @@ local0.*    /var/log/postgresql
       </term>
       <listitem>
        <para>
-        Controls which <link linkend="runtime-config-severity-levels">message
-        levels</link> are written to the server log.
-        Valid values are a comma-separated list of <literal>type:level</literal>
-        and a single <literal>level</literal>. The list allows it to use
-        different levels per process type. Only the single <literal>level</literal>
-        is mandatory (order does not matter) and it is assigned to the process
-        types that are not specified in the list.
-        Valid process types are listed in the table below, each corresponding to
-        either postmaster, an auxiliary process type or a backend.
+        Controls which
+        <link linkend="runtime-config-severity-levels">message levels</link>
+        are written to the server log.  Valid values are a comma-separated
+        list of zero or more
+        <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal>
+        entries and exactly one mandatory
+        <literal><replaceable>level</replaceable></literal> entry,
+        which is the default for process types not listed.
+        Valid process types are listed in the table below.
         <simplelist type="vert" columns="4">
          <member><literal>archiver</literal></member>
          <member><literal>autovacuum</literal></member>
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index a450254d1fa..97b9a6b5964 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source)
 /*
  * GUC check_hook for log_min_messages
  *
- * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE
- * is log_min_messages_process_types.  LEVEL is server_message_level_options. A
- * single LEVEL element should be part of this list and it is applied as a
- * final step to the process types that are not specified. For backward
- * compatibility, the old syntax is still accepted and it means to apply the
- * LEVEL for all process types.
+ * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL
+ * elements.  For each element, TYPE corresponds to a bk_category value (see
+ * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls.
+ *
+ * In addition, there must be a single LEVEL element (with no TYPE part)
+ * which sets the default level for process types that aren't specified.
  */
 bool
 check_log_min_messages(char **newval, void **extra, GucSource source)
@@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 	List	   *elemlist;
 	StringInfoData buf;
 	char	   *result;
-	bool		first = true;
 	int			newlevel[BACKEND_NUM_TYPES];
-	bool		assigned[BACKEND_NUM_TYPES];
+	bool		assigned[BACKEND_NUM_TYPES] = {0};
 	int			genericlevel = -1;	/* -1 means not assigned */
 
-	/* Initialize the array. */
-	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
-	{
-		newlevel[i] = WARNING;
-		assigned[i] = false;
-	}
+	const char *const process_types[] = {
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \
+		[bktype] = bkcategory,
+#include "postmaster/proctypelist.h"
+#undef PG_PROCTYPE
+	};
 
 	/* Need a modifiable copy of string. */
 	rawstring = guc_strdup(LOG, *newval);
 
-	/* Parse string into list of identifiers. */
+	/* Parse the string into a list. */
 	if (!SplitGUCList(rawstring, ',', &elemlist))
 	{
 		/* syntax error in list */
@@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 	/* Validate and assign log level and process type. */
 	foreach_ptr(char, tok, elemlist)
 	{
-		char	   *sep;
-		const struct config_enum_entry *entry;
+		char	   *sep = strchr(tok, ':');
 
 		/*
-		 * Check whether there is a process type following the log level. If
-		 * there is no separator, it means this is the generic log level. The
-		 * generic log level will be assigned to the process types that were
-		 * not informed.
+		 * If there's no ':' separator in the entry, this is the default
+		 * value.  Otherwise it's a process type-specific entry.
 		 */
-		sep = strchr(tok, ':');
 		if (sep == NULL)
 		{
+			const struct config_enum_entry *entry;
 			bool		found = false;
 
 			/* Reject duplicates for generic log level. */
 			if (genericlevel != -1)
 			{
-				GUC_check_errdetail("Generic log level was already assigned.");
-				guc_free(rawstring);
-				list_free(elemlist);
-				return false;
+				GUC_check_errdetail("Redundant specification of default log level.");
+				goto lmm_fail;
 			}
 
 			/* Is the log level valid? */
@@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 			if (!found)
 			{
 				GUC_check_errdetail("Unrecognized log level: \"%s\".", tok);
-				guc_free(rawstring);
-				list_free(elemlist);
-				return false;
+				goto lmm_fail;
 			}
 		}
 		else
@@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 			char	   *loglevel;
 			char	   *ptype;
 			bool		found = false;
+			int			level;
+			const struct config_enum_entry *entry;
 
-			ptype = guc_malloc(LOG, (sep - tok) + 1);
-			if (!ptype)
-			{
-				guc_free(rawstring);
-				list_free(elemlist);
-				return false;
-			}
-			memcpy(ptype, tok, sep - tok);
-			ptype[sep - tok] = '\0';
+			ptype = tok;
 			loglevel = sep + 1;
+			*sep = '\0';
 
 			/* Is the log level valid? */
 			for (entry = server_message_level_options; entry && entry->name; entry++)
 			{
 				if (pg_strcasecmp(entry->name, loglevel) == 0)
 				{
+					level = entry->val;
 					found = true;
 					break;
 				}
@@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 
 			if (!found)
 			{
-				GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel);
-				guc_free(ptype);
-				guc_free(rawstring);
-				list_free(elemlist);
-				return false;
+				GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".",
+									ptype, loglevel);
+				goto lmm_fail;
 			}
 
-			/*
-			 * Is the process type name valid? There might be multiple entries
-			 * per process type, don't bail out because it can assign the
-			 * value for multiple entries.
-			 */
+			/* Is the process type name valid and unique? */
 			found = false;
 			for (int i = 0; i < BACKEND_NUM_TYPES; i++)
 			{
-				if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0)
+				if (pg_strcasecmp(process_types[i], ptype) == 0)
 				{
 					/* Reject duplicates for a process type. */
 					if (assigned[i])
 					{
-						GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype);
-						guc_free(ptype);
-						guc_free(rawstring);
-						list_free(elemlist);
-						return false;
+						GUC_check_errdetail("Redundant log level specification for process type \"%s\".",
+											ptype);
+						goto lmm_fail;
 					}
 
-					newlevel[i] = entry->val;
+					newlevel[i] = level;
 					assigned[i] = true;
 					found = true;
+					break;
 				}
 			}
 
 			if (!found)
 			{
 				GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype);
-				guc_free(ptype);
-				guc_free(rawstring);
-				list_free(elemlist);
-				return false;
+				goto lmm_fail;
 			}
 
-			guc_free(ptype);
+			/* Put the separator back in place */
+			*sep = ':';
 		}
+
+		/* all good */
+		continue;
+
+lmm_fail:
+		guc_free(rawstring);
+		list_free(elemlist);
+		return false;
 	}
 
 	/*
@@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 	 */
 	if (genericlevel == -1)
 	{
-		GUC_check_errdetail("Generic log level was not defined.");
+		GUC_check_errdetail("Default log level was not defined.");
 		guc_free(rawstring);
 		list_free(elemlist);
 		return false;
@@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 	}
 
 	/*
-	 * Use a stable representation of log_min_messages. The generic level is
-	 * always the first element and the other elements (type:level) are sorted
-	 * by process type. See log_min_messages_cmp for details.
+	 * To present a nice view to users, sort the output list by process type,
+	 * with the default value first.
 	 */
 	list_sort(elemlist, log_min_messages_cmp);
 
-	initStringInfo(&buf);
+	initStringInfoExt(&buf, strlen(rawstring) + 1);
 	foreach_ptr(char, tok, elemlist)
 	{
-		if (first)
-		{
+		if (foreach_current_index(tok) == 0)
 			appendStringInfoString(&buf, tok);
-			first = false;
-		}
 		else
-		{
 			appendStringInfo(&buf, ", %s", tok);
-		}
 	}
 
 	result = (char *) guc_malloc(LOG, buf.len + 1);
@@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 	return true;
 }
 
+/*
+ * list_sort() callback for check_log_min_messages.  The default element
+ * goes first; the rest are ordered by strcmp() of the process type.
+ */
 static int
 log_min_messages_cmp(const ListCell *a, const ListCell *b)
 {
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 08553c0f024..05b1feef3cf 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -178,7 +178,7 @@ typedef struct
 } child_process_kind;
 
 static child_process_kind child_process_kinds[] = {
-#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \
 	[bktype] = {description, main_func, shmem_attach},
 #include "postmaster/proctypelist.h"
 #undef PG_PROCTYPE
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index efba20fa8b9..03f6c8479f2 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType)
 
 	switch (backendType)
 	{
-#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages)	\
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach)	\
 		case bktype: backendDesc = description; break;
 #include "postmaster/proctypelist.h"
 #undef PG_PROCTYPE
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 0aebb9c8e24..53933f53b25 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -648,23 +648,15 @@ char	   *role_string;
 bool		in_hot_standby_guc;
 
 /*
- * log_min_messages
+ * set default log_min_messages to WARNING for all process types
  */
 int			log_min_messages[] = {
-#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
-	[bktype] = log_min_messages,
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \
+	[bktype] = WARNING,
 #include "postmaster/proctypelist.h"
 #undef PG_PROCTYPE
 };
 
-const char *const log_min_messages_process_types[] = {
-#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
-	[bktype] = bkcategory,
-#include "postmaster/proctypelist.h"
-#undef PG_PROCTYPE
-};
-
-
 /*
  * Displayable names for context types (enum GucContext)
  *
diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h
index 0ddd805f0e7..4e259e84c2d 100644
--- a/src/include/postmaster/proctypelist.h
+++ b/src/include/postmaster/proctypelist.h
@@ -30,22 +30,22 @@
  */
 
 
-/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */
-PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING)
-PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING)
-PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING)
-PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING)
-PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING)
-PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING)
-PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING)
-PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING)
-PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING)
-PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING)
-PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING)
-PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING)
-PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING)
-PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING)
-PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING)
-PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING)
-PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING)
-PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING)
+/* bktype, bkcategory, description, main_func, shmem_attach */
+PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true)
+PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true)
+PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true)
+PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true)
+PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true)
+PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true)
+PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true)
+PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true)
+PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false)
+PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true)
+PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false)
+PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true)
+PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false)
+PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true)
+PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true)
+PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true)
+PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true)
+PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true)
diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out
index 9a6fd503009..616b10f80d6 100644
--- a/src/test/regress/expected/guc.out
+++ b/src/test/regress/expected/guc.out
@@ -955,19 +955,19 @@ SHOW log_min_messages;
 
 SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1';  --fail
 ERROR:  invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1"
-DETAIL:  Generic log level was not defined.
+DETAIL:  Default log level was not defined.
 SET log_min_messages TO 'debug1, backend:error, fatal';  -- fail
 ERROR:  invalid value for parameter "log_min_messages": "debug1, backend:error, fatal"
-DETAIL:  Generic log level was already assigned.
+DETAIL:  Redundant specification of default log level.
 SET log_min_messages TO 'backend:error, debug1, backend:warning';  -- fail
 ERROR:  invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning"
-DETAIL:  Process type "backend" was already assigned.
+DETAIL:  Redundant log level specification for process type "backend".
 SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1';  -- fail
 ERROR:  invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1"
 DETAIL:  Unrecognized process type: "foo".
 SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1';  -- fail
 ERROR:  invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1"
-DETAIL:  Unrecognized log level: "bar".
+DETAIL:  Unrecognized log level for process type "checkpointer": "bar".
 SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3';
 SHOW log_min_messages;
                                         log_min_messages                                         
-- 
2.47.3

^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2026-02-06 15:45  Euler Taveira <euler@eulerto.com>
  parent: Alvaro Herrera <alvherre@alvh.no-ip.org>
  0 siblings, 2 replies; 58+ messages in thread

From: Euler Taveira @ 2026-02-06 15:45 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org, "Chao Li" <li.evan.chao@gmail.com>

On Fri, Feb 6, 2026, at 8:05 AM, Alvaro Herrera wrote:
> Here are some fixups for the main patch.  I include yours so that CI
> will test this one.
>

All suggestions seem good to me.

> * I'm not fond of the term "generic", so I changed it to "default",
> which IMO makes more sense from the user point of view.
>

I'm fine with "default" instead of "generic". However, you forgot to replace
"generic" in a few places. The attached patch fixes them all.

> * There seemed to be too much guc_mallocking[*] going on; for instance for
> ptype.  I simplified that by just overwriting the : to a '\0', then we
> can use the original string for pg_strcasecmp().  At the bottom of the
> loop we restore the ':', so that the code below can reuse the original
> values.  This is okay because we have already mallocked a copy for
> SplitGUCList.
>

Nice trick.

> * There's no reason to add a WARNING to every process type in the
> proctypelist.h table.  We can just set the values to WARNING in the
> log_min_messages initializer in guc_tables.c.
>

Ok.

> * There's no reason AFAICS to have log_min_messages_process_types in
> guc_tables.c; nobody else uses it.  I made it a local array of char*
> inside the check_log_min_messages function.
>

That's true.

> * There's no need to initialize the newlevel[] array, since we're going
> to assign values to every individual element at one point or another.
> And for the assigned[] array, we don't need to set the value of each to
> 'false' in a loop; we can just initialize to '{0}' and the compiler will
> do the needful.  With this we can remove the loop entirely.
>

Ok.

> * "guc_free()+list_free()+return false" the whole time in the various
> failure places across the loop was tiresome.  I put them all in a single
> place and jump there with a goto.
>

I tend to avoid goto. However, in this case, it seems it improves readability.

> * in foreach() loops, tracking of a 'bool first' is unnecessary.  You
> can just compare foreach_current_index() with 0.
>

Didn't know about foreach_current_index.

> * Rewrote docs and comments
>

Ok.

I attached the same patches that you shared plus the s/generic/default/ that I
said earlier for the sake of CI.


-- 
Euler Taveira
EDB   https://www.enterprisedb.com/

Attachments:

  [text/x-patch] v11-0001-log_min_messages-per-process-type.patch (27.4K, ../../88d8e984-ad63-48c1-86af-d50d7776942f@app.fastmail.com/2-v11-0001-log_min_messages-per-process-type.patch)
  download | inline diff:
From 8c1fb5c97cd3fd342120f8c44d34f7bdc18e8b74 Mon Sep 17 00:00:00 2001
From: Euler Taveira <euler@eulerto.com>
Date: Tue, 4 Nov 2025 17:18:20 -0300
Subject: [PATCH v11 1/3] log_min_messages per process type

Change log_min_messages from a single element to a comma-separated list
of elements. Each element is type:level. The types are archiver,
autovacuum (includes launcher and workers), backend, bgworker, bgwriter,
checkpointer, ioworker, postmaster, syslogger, slotsyncworker, startup,
walreceiver, walsender, walsummarizer and walwriter. A single log level
should be part of this list and it is applied as a final step to the
process types that were not informed.

The old syntax (a single log level) is still accepted for backward
compatibility. In this case, it means the informed log level is applied
for all process types. The default log level is the same (WARNING) for
all process types.
---
 doc/src/sgml/config.sgml                      |  42 ++-
 src/backend/commands/extension.c              |   2 +-
 src/backend/commands/variable.c               | 256 ++++++++++++++++++
 src/backend/postmaster/launch_backend.c       |   2 +-
 src/backend/utils/error/elog.c                |   2 +-
 src/backend/utils/init/miscinit.c             |   2 +-
 src/backend/utils/misc/guc_parameters.dat     |  10 +-
 src/backend/utils/misc/guc_tables.c           |  21 +-
 src/backend/utils/misc/postgresql.conf.sample |   2 +
 src/include/postmaster/proctypelist.h         |  42 +--
 src/include/utils/guc.h                       |   5 +-
 src/include/utils/guc_hooks.h                 |   2 +
 src/test/regress/expected/guc.out             |  54 ++++
 src/test/regress/sql/guc.sql                  |  18 ++
 14 files changed, 419 insertions(+), 41 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index f1af1505cf3..287816fb504 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -7120,7 +7120,7 @@ local0.*    /var/log/postgresql
      <variablelist>
 
      <varlistentry id="guc-log-min-messages" xreflabel="log_min_messages">
-      <term><varname>log_min_messages</varname> (<type>enum</type>)
+      <term><varname>log_min_messages</varname> (<type>string</type>)
       <indexterm>
        <primary><varname>log_min_messages</varname> configuration parameter</primary>
       </indexterm>
@@ -7129,14 +7129,38 @@ local0.*    /var/log/postgresql
        <para>
         Controls which <link linkend="runtime-config-severity-levels">message
         levels</link> are written to the server log.
-        Valid values are <literal>DEBUG5</literal>, <literal>DEBUG4</literal>,
-        <literal>DEBUG3</literal>, <literal>DEBUG2</literal>, <literal>DEBUG1</literal>,
-        <literal>INFO</literal>, <literal>NOTICE</literal>, <literal>WARNING</literal>,
-        <literal>ERROR</literal>, <literal>LOG</literal>, <literal>FATAL</literal>, and
-        <literal>PANIC</literal>.  Each level includes all the levels that
-        follow it.  The later the level, the fewer messages are sent
-        to the log.  The default is <literal>WARNING</literal>.  Note that
-        <literal>LOG</literal> has a different rank here than in
+        Valid values are a comma-separated list of <literal>type:level</literal>
+        and a single <literal>level</literal>. The list allows it to use
+        different levels per process type. Only the single <literal>level</literal>
+        is mandatory (order does not matter) and it is assigned to the process
+        types that are not specified in the list.
+        Valid process types are listed in the table below, each corresponding to
+        either postmaster, an auxiliary process type or a backend.
+        <simplelist type="vert" columns="4">
+         <member><literal>archiver</literal></member>
+         <member><literal>autovacuum</literal></member>
+         <member><literal>backend</literal></member>
+         <member><literal>bgworker</literal></member>
+         <member><literal>bgwriter</literal></member>
+         <member><literal>checkpointer</literal></member>
+         <member><literal>ioworker</literal></member>
+         <member><literal>postmaster</literal></member>
+         <member><literal>syslogger</literal></member>
+         <member><literal>slotsyncworker</literal></member>
+         <member><literal>startup</literal></member>
+         <member><literal>walreceiver</literal></member>
+         <member><literal>walsender</literal></member>
+         <member><literal>walsummarizer</literal></member>
+         <member><literal>walwriter</literal></member>
+        </simplelist>
+        Valid <literal>level</literal> values are <literal>DEBUG5</literal>,
+        <literal>DEBUG4</literal>, <literal>DEBUG3</literal>, <literal>DEBUG2</literal>,
+        <literal>DEBUG1</literal>, <literal>INFO</literal>, <literal>NOTICE</literal>,
+        <literal>WARNING</literal>, <literal>ERROR</literal>, <literal>LOG</literal>,
+        <literal>FATAL</literal>, and <literal>PANIC</literal>.  Each level includes
+        all the levels that follow it.  The later the level, the fewer messages are sent
+        to the log.  The default is <literal>WARNING</literal> for all process types.
+        Note that <literal>LOG</literal> has a different rank here than in
         <xref linkend="guc-client-min-messages"/>.
         Only superusers and users with the appropriate <literal>SET</literal>
         privilege can change this setting.
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 596105ee078..688f1874f31 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1191,7 +1191,7 @@ execute_extension_script(Oid extensionOid, ExtensionControlFile *control,
 		(void) set_config_option("client_min_messages", "warning",
 								 PGC_USERSET, PGC_S_SESSION,
 								 GUC_ACTION_SAVE, true, 0, false);
-	if (log_min_messages < WARNING)
+	if (log_min_messages[MyBackendType] < WARNING)
 		(void) set_config_option_ext("log_min_messages", "warning",
 									 PGC_SUSET, PGC_S_SESSION,
 									 BOOTSTRAP_SUPERUSERID,
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index 4440aff4925..a450254d1fa 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -34,6 +34,7 @@
 #include "utils/backend_status.h"
 #include "utils/datetime.h"
 #include "utils/fmgrprotos.h"
+#include "utils/guc.h"
 #include "utils/guc_hooks.h"
 #include "utils/snapmgr.h"
 #include "utils/syscache.h"
@@ -41,6 +42,8 @@
 #include "utils/tzparser.h"
 #include "utils/varlena.h"
 
+static int	log_min_messages_cmp(const ListCell *a, const ListCell *b);
+
 /*
  * DATESTYLE
  */
@@ -1272,3 +1275,256 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source)
 
 	return true;
 }
+
+/*
+ * GUC check_hook for log_min_messages
+ *
+ * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE
+ * is log_min_messages_process_types.  LEVEL is server_message_level_options. A
+ * single LEVEL element should be part of this list and it is applied as a
+ * final step to the process types that are not specified. For backward
+ * compatibility, the old syntax is still accepted and it means to apply the
+ * LEVEL for all process types.
+ */
+bool
+check_log_min_messages(char **newval, void **extra, GucSource source)
+{
+	char	   *rawstring;
+	List	   *elemlist;
+	StringInfoData buf;
+	char	   *result;
+	bool		first = true;
+	int			newlevel[BACKEND_NUM_TYPES];
+	bool		assigned[BACKEND_NUM_TYPES];
+	int			genericlevel = -1;	/* -1 means not assigned */
+
+	/* Initialize the array. */
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+	{
+		newlevel[i] = WARNING;
+		assigned[i] = false;
+	}
+
+	/* Need a modifiable copy of string. */
+	rawstring = guc_strdup(LOG, *newval);
+
+	/* Parse string into list of identifiers. */
+	if (!SplitGUCList(rawstring, ',', &elemlist))
+	{
+		/* syntax error in list */
+		GUC_check_errdetail("List syntax is invalid.");
+		list_free(elemlist);
+		guc_free(rawstring);
+		return false;
+	}
+
+	/* Validate and assign log level and process type. */
+	foreach_ptr(char, tok, elemlist)
+	{
+		char	   *sep;
+		const struct config_enum_entry *entry;
+
+		/*
+		 * Check whether there is a process type following the log level. If
+		 * there is no separator, it means this is the generic log level. The
+		 * generic log level will be assigned to the process types that were
+		 * not informed.
+		 */
+		sep = strchr(tok, ':');
+		if (sep == NULL)
+		{
+			bool		found = false;
+
+			/* Reject duplicates for generic log level. */
+			if (genericlevel != -1)
+			{
+				GUC_check_errdetail("Generic log level was already assigned.");
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			/* Is the log level valid? */
+			for (entry = server_message_level_options; entry && entry->name; entry++)
+			{
+				if (pg_strcasecmp(entry->name, tok) == 0)
+				{
+					genericlevel = entry->val;
+					found = true;
+					break;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized log level: \"%s\".", tok);
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+		}
+		else
+		{
+			char	   *loglevel;
+			char	   *ptype;
+			bool		found = false;
+
+			ptype = guc_malloc(LOG, (sep - tok) + 1);
+			if (!ptype)
+			{
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+			memcpy(ptype, tok, sep - tok);
+			ptype[sep - tok] = '\0';
+			loglevel = sep + 1;
+
+			/* Is the log level valid? */
+			for (entry = server_message_level_options; entry && entry->name; entry++)
+			{
+				if (pg_strcasecmp(entry->name, loglevel) == 0)
+				{
+					found = true;
+					break;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel);
+				guc_free(ptype);
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			/*
+			 * Is the process type name valid? There might be multiple entries
+			 * per process type, don't bail out because it can assign the
+			 * value for multiple entries.
+			 */
+			found = false;
+			for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+			{
+				if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0)
+				{
+					/* Reject duplicates for a process type. */
+					if (assigned[i])
+					{
+						GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype);
+						guc_free(ptype);
+						guc_free(rawstring);
+						list_free(elemlist);
+						return false;
+					}
+
+					newlevel[i] = entry->val;
+					assigned[i] = true;
+					found = true;
+				}
+			}
+
+			if (!found)
+			{
+				GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype);
+				guc_free(ptype);
+				guc_free(rawstring);
+				list_free(elemlist);
+				return false;
+			}
+
+			guc_free(ptype);
+		}
+	}
+
+	/*
+	 * The generic log level must be specified. It is the fallback value.
+	 */
+	if (genericlevel == -1)
+	{
+		GUC_check_errdetail("Generic log level was not defined.");
+		guc_free(rawstring);
+		list_free(elemlist);
+		return false;
+	}
+
+	/*
+	 * Apply the generic log level after all of the specific process types
+	 * have been assigned. Hence, it doesn't matter the order you specify the
+	 * generic log level, the final result will be the same.
+	 */
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+	{
+		if (!assigned[i])
+			newlevel[i] = genericlevel;
+	}
+
+	/*
+	 * Use a stable representation of log_min_messages. The generic level is
+	 * always the first element and the other elements (type:level) are sorted
+	 * by process type. See log_min_messages_cmp for details.
+	 */
+	list_sort(elemlist, log_min_messages_cmp);
+
+	initStringInfo(&buf);
+	foreach_ptr(char, tok, elemlist)
+	{
+		if (first)
+		{
+			appendStringInfoString(&buf, tok);
+			first = false;
+		}
+		else
+		{
+			appendStringInfo(&buf, ", %s", tok);
+		}
+	}
+
+	result = (char *) guc_malloc(LOG, buf.len + 1);
+	if (!result)
+		return false;
+	memcpy(result, buf.data, buf.len);
+	result[buf.len] = '\0';
+
+	guc_free(*newval);
+	*newval = result;
+
+	guc_free(rawstring);
+	list_free(elemlist);
+	pfree(buf.data);
+
+	/*
+	 * Pass back data for assign_log_min_messages to use.
+	 */
+	*extra = guc_malloc(LOG, BACKEND_NUM_TYPES * sizeof(int));
+	if (!*extra)
+		return false;
+	memcpy(*extra, newlevel, BACKEND_NUM_TYPES * sizeof(int));
+
+	return true;
+}
+
+static int
+log_min_messages_cmp(const ListCell *a, const ListCell *b)
+{
+	const char *s = lfirst(a);
+	const char *t = lfirst(b);
+
+	if (strchr(s, ':') == NULL)
+		return -1;
+	else if (strchr(t, ':') == NULL)
+		return 1;
+	else
+		return strcmp(s, t);
+}
+
+/*
+ * GUC assign_hook for log_min_messages
+ */
+void
+assign_log_min_messages(const char *newval, void *extra)
+{
+	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+		log_min_messages[i] = ((int *) extra)[i];
+}
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 926fd6f2700..08553c0f024 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -178,7 +178,7 @@ typedef struct
 } child_process_kind;
 
 static child_process_kind child_process_kinds[] = {
-#define PG_PROCTYPE(bktype, description, main_func, shmem_attach) \
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
 	[bktype] = {description, main_func, shmem_attach},
 #include "postmaster/proctypelist.h"
 #undef PG_PROCTYPE
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index e6a4ef99059..4e9143b6e6f 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -235,7 +235,7 @@ is_log_level_output(int elevel, int log_min_level)
 static inline bool
 should_output_to_server(int elevel)
 {
-	return is_log_level_output(elevel, log_min_messages);
+	return is_log_level_output(elevel, log_min_messages[MyBackendType]);
 }
 
 /*
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index 563f20374ff..efba20fa8b9 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType)
 
 	switch (backendType)
 	{
-#define PG_PROCTYPE(bktype, description, main_func, shmem_attach)	\
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages)	\
 		case bktype: backendDesc = description; break;
 #include "postmaster/proctypelist.h"
 #undef PG_PROCTYPE
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index c1f1603cd39..762b8efe6b0 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -1693,12 +1693,14 @@
   options => 'server_message_level_options',
 },
 
-{ name => 'log_min_messages', type => 'enum', context => 'PGC_SUSET', group => 'LOGGING_WHEN',
+{ name => 'log_min_messages', type => 'string', context => 'PGC_SUSET', group => 'LOGGING_WHEN',
   short_desc => 'Sets the message levels that are logged.',
   long_desc => 'Each level includes all the levels that follow it. The later the level, the fewer messages are sent.',
-  variable => 'log_min_messages',
-  boot_val => 'WARNING',
-  options => 'server_message_level_options',
+  flags => 'GUC_LIST_INPUT',
+  variable => 'log_min_messages_string',
+  boot_val => '"WARNING"',
+  check_hook => 'check_log_min_messages',
+  assign_hook => 'assign_log_min_messages',
 },
 
 { name => 'log_parameter_max_length', type => 'int', context => 'PGC_SUSET', group => 'LOGGING_WHAT',
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 5df3a36bf64..46a5cf13e4a 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -147,7 +147,7 @@ static const struct config_enum_entry client_message_level_options[] = {
 	{NULL, 0, false}
 };
 
-static const struct config_enum_entry server_message_level_options[] = {
+const struct config_enum_entry server_message_level_options[] = {
 	{"debug5", DEBUG5, false},
 	{"debug4", DEBUG4, false},
 	{"debug3", DEBUG3, false},
@@ -546,7 +546,6 @@ static bool standard_conforming_strings = true;
 bool		current_role_is_superuser;
 
 int			log_min_error_statement = ERROR;
-int			log_min_messages = WARNING;
 int			client_min_messages = NOTICE;
 int			log_min_duration_sample = -1;
 int			log_min_duration_statement = -1;
@@ -604,6 +603,7 @@ static char *server_version_string;
 static int	server_version_num;
 static char *debug_io_direct_string;
 static char *restrict_nonsystem_relation_kind_string;
+static char *log_min_messages_string;
 
 #ifdef HAVE_SYSLOG
 #define	DEFAULT_SYSLOG_FACILITY LOG_LOCAL0
@@ -656,6 +656,23 @@ char	   *role_string;
 /* should be static, but guc.c needs to get at this */
 bool		in_hot_standby_guc;
 
+/*
+ * log_min_messages
+ */
+int			log_min_messages[] = {
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
+	[bktype] = log_min_messages,
+#include "postmaster/proctypelist.h"
+#undef PG_PROCTYPE
+};
+
+const char *const log_min_messages_process_types[] = {
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
+	[bktype] = bkcategory,
+#include "postmaster/proctypelist.h"
+#undef PG_PROCTYPE
+};
+
 
 /*
  * Displayable names for context types (enum GucContext)
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 1ae594af843..a9798cc628d 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -545,6 +545,8 @@
                                         #   log
                                         #   fatal
                                         #   panic
+                                        #   and an optional comma-separated list
+                                        #   of type:level
 
 #log_min_error_statement = error        # values in order of decreasing detail:
                                         #   debug5
diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h
index 0b99eaabfd0..0ddd805f0e7 100644
--- a/src/include/postmaster/proctypelist.h
+++ b/src/include/postmaster/proctypelist.h
@@ -25,27 +25,27 @@
  */
 
 /*
- * List of process types (symbol, description, Main function, shmem_attach)
- * entries.
+ * List of process types (symbol, category, description, Main function,
+ * shmem_attach, message level) entries.
  */
 
 
-/* bktype, description, main_func, shmem_attach */
-PG_PROCTYPE(B_ARCHIVER, gettext_noop("archiver"), PgArchiverMain, true)
-PG_PROCTYPE(B_AUTOVAC_LAUNCHER, gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true)
-PG_PROCTYPE(B_AUTOVAC_WORKER, gettext_noop("autovacuum worker"), AutoVacWorkerMain, true)
-PG_PROCTYPE(B_BACKEND, gettext_noop("client backend"), BackendMain, true)
-PG_PROCTYPE(B_BG_WORKER, gettext_noop("background worker"), BackgroundWorkerMain, true)
-PG_PROCTYPE(B_BG_WRITER, gettext_noop("background writer"), BackgroundWriterMain, true)
-PG_PROCTYPE(B_CHECKPOINTER, gettext_noop("checkpointer"), CheckpointerMain, true)
-PG_PROCTYPE(B_DEAD_END_BACKEND, gettext_noop("dead-end client backend"), BackendMain, true)
-PG_PROCTYPE(B_INVALID, gettext_noop("unrecognized"), NULL, false)
-PG_PROCTYPE(B_IO_WORKER, gettext_noop("io worker"), IoWorkerMain, true)
-PG_PROCTYPE(B_LOGGER, gettext_noop("syslogger"), SysLoggerMain, false)
-PG_PROCTYPE(B_SLOTSYNC_WORKER, gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true)
-PG_PROCTYPE(B_STANDALONE_BACKEND, gettext_noop("standalone backend"), NULL, false)
-PG_PROCTYPE(B_STARTUP, gettext_noop("startup"), StartupProcessMain, true)
-PG_PROCTYPE(B_WAL_RECEIVER, gettext_noop("walreceiver"), WalReceiverMain, true)
-PG_PROCTYPE(B_WAL_SENDER, gettext_noop("walsender"), NULL, true)
-PG_PROCTYPE(B_WAL_SUMMARIZER, gettext_noop("walsummarizer"), WalSummarizerMain, true)
-PG_PROCTYPE(B_WAL_WRITER, gettext_noop("walwriter"), WalWriterMain, true)
+/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */
+PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING)
+PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING)
+PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING)
+PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING)
+PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING)
+PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING)
+PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING)
+PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING)
+PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING)
+PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING)
+PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING)
+PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING)
+PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING)
+PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING)
+PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING)
+PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING)
+PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING)
+PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING)
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index bf39878c43e..8acbdba7ff5 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -295,7 +295,7 @@ extern PGDLLIMPORT bool log_duration;
 extern PGDLLIMPORT int log_parameter_max_length;
 extern PGDLLIMPORT int log_parameter_max_length_on_error;
 extern PGDLLIMPORT int log_min_error_statement;
-extern PGDLLIMPORT int log_min_messages;
+extern PGDLLIMPORT int log_min_messages[];
 extern PGDLLIMPORT int client_min_messages;
 extern PGDLLIMPORT int log_min_duration_sample;
 extern PGDLLIMPORT int log_min_duration_statement;
@@ -329,6 +329,8 @@ extern PGDLLIMPORT bool trace_sort;
 extern PGDLLIMPORT bool optimize_bounded_sort;
 #endif
 
+extern PGDLLIMPORT const char *const log_min_messages_process_types[];
+
 /*
  * Declarations for options for enum values
  *
@@ -344,6 +346,7 @@ extern PGDLLIMPORT const struct config_enum_entry archive_mode_options[];
 extern PGDLLIMPORT const struct config_enum_entry dynamic_shared_memory_options[];
 extern PGDLLIMPORT const struct config_enum_entry io_method_options[];
 extern PGDLLIMPORT const struct config_enum_entry recovery_target_action_options[];
+extern PGDLLIMPORT const struct config_enum_entry server_message_level_options[];
 extern PGDLLIMPORT const struct config_enum_entry wal_level_options[];
 extern PGDLLIMPORT const struct config_enum_entry wal_sync_method_options[];
 
diff --git a/src/include/utils/guc_hooks.h b/src/include/utils/guc_hooks.h
index b6ecb0e769f..9c90670d9b8 100644
--- a/src/include/utils/guc_hooks.h
+++ b/src/include/utils/guc_hooks.h
@@ -177,5 +177,7 @@ extern void assign_wal_sync_method(int new_wal_sync_method, void *extra);
 extern bool check_synchronized_standby_slots(char **newval, void **extra,
 											 GucSource source);
 extern void assign_synchronized_standby_slots(const char *newval, void *extra);
+extern bool check_log_min_messages(char **newval, void **extra, GucSource source);
+extern void assign_log_min_messages(const char *newval, void *extra);
 
 #endif							/* GUC_HOOKS_H */
diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out
index d6fb879f500..9a6fd503009 100644
--- a/src/test/regress/expected/guc.out
+++ b/src/test/regress/expected/guc.out
@@ -935,3 +935,57 @@ SELECT name FROM tab_settings_flags
 (0 rows)
 
 DROP TABLE tab_settings_flags;
+-- Test log_min_messages
+SET log_min_messages TO foo;  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "foo"
+DETAIL:  Unrecognized log level: "foo".
+SET log_min_messages TO fatal;
+SHOW log_min_messages;
+ log_min_messages 
+------------------
+ fatal
+(1 row)
+
+SET log_min_messages TO 'fatal';
+SHOW log_min_messages;
+ log_min_messages 
+------------------
+ fatal
+(1 row)
+
+SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1';  --fail
+ERROR:  invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1"
+DETAIL:  Generic log level was not defined.
+SET log_min_messages TO 'debug1, backend:error, fatal';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "debug1, backend:error, fatal"
+DETAIL:  Generic log level was already assigned.
+SET log_min_messages TO 'backend:error, debug1, backend:warning';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning"
+DETAIL:  Process type "backend" was already assigned.
+SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1"
+DETAIL:  Unrecognized process type: "foo".
+SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1';  -- fail
+ERROR:  invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1"
+DETAIL:  Unrecognized log level: "bar".
+SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3';
+SHOW log_min_messages;
+                                        log_min_messages                                         
+-------------------------------------------------------------------------------------------------
+ fatal, archiver:debug2, autovacuum:debug1, backend:error, checkpointer:debug3, walsender:debug3
+(1 row)
+
+SET log_min_messages TO 'warning, autovacuum:debug1';
+SHOW log_min_messages;
+      log_min_messages      
+----------------------------
+ warning, autovacuum:debug1
+(1 row)
+
+SET log_min_messages TO 'autovacuum:debug1, warning';
+SHOW log_min_messages;
+      log_min_messages      
+----------------------------
+ warning, autovacuum:debug1
+(1 row)
+
diff --git a/src/test/regress/sql/guc.sql b/src/test/regress/sql/guc.sql
index bafaf067e82..f7dfc909b42 100644
--- a/src/test/regress/sql/guc.sql
+++ b/src/test/regress/sql/guc.sql
@@ -377,3 +377,21 @@ SELECT name FROM tab_settings_flags
   WHERE no_reset AND NOT no_reset_all
   ORDER BY 1;
 DROP TABLE tab_settings_flags;
+
+-- Test log_min_messages
+SET log_min_messages TO foo;  -- fail
+SET log_min_messages TO fatal;
+SHOW log_min_messages;
+SET log_min_messages TO 'fatal';
+SHOW log_min_messages;
+SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1';  --fail
+SET log_min_messages TO 'debug1, backend:error, fatal';  -- fail
+SET log_min_messages TO 'backend:error, debug1, backend:warning';  -- fail
+SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1';  -- fail
+SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1';  -- fail
+SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3';
+SHOW log_min_messages;
+SET log_min_messages TO 'warning, autovacuum:debug1';
+SHOW log_min_messages;
+SET log_min_messages TO 'autovacuum:debug1, warning';
+SHOW log_min_messages;
-- 
2.39.5



  [text/x-patch] v11-0002-review.patch (17.6K, ../../88d8e984-ad63-48c1-86af-d50d7776942f@app.fastmail.com/3-v11-0002-review.patch)
  download | inline diff:
From d8a6a1824a0f93d5418dceb06fc692bb66b62446 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Herrera?= <alvherre@kurilemu.de>
Date: Fri, 6 Feb 2026 10:24:29 +0100
Subject: [PATCH v11 2/3] review

---
 doc/src/sgml/config.sgml                |  18 ++--
 src/backend/commands/variable.c         | 127 +++++++++++-------------
 src/backend/postmaster/launch_backend.c |   2 +-
 src/backend/utils/init/miscinit.c       |   2 +-
 src/backend/utils/misc/guc_tables.c     |  14 +--
 src/include/postmaster/proctypelist.h   |  38 +++----
 src/test/regress/expected/guc.out       |   8 +-
 7 files changed, 93 insertions(+), 116 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 287816fb504..aeed21b1548 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -7127,15 +7127,15 @@ local0.*    /var/log/postgresql
       </term>
       <listitem>
        <para>
-        Controls which <link linkend="runtime-config-severity-levels">message
-        levels</link> are written to the server log.
-        Valid values are a comma-separated list of <literal>type:level</literal>
-        and a single <literal>level</literal>. The list allows it to use
-        different levels per process type. Only the single <literal>level</literal>
-        is mandatory (order does not matter) and it is assigned to the process
-        types that are not specified in the list.
-        Valid process types are listed in the table below, each corresponding to
-        either postmaster, an auxiliary process type or a backend.
+        Controls which
+        <link linkend="runtime-config-severity-levels">message levels</link>
+        are written to the server log.  Valid values are a comma-separated
+        list of zero or more
+        <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal>
+        entries and exactly one mandatory
+        <literal><replaceable>level</replaceable></literal> entry,
+        which is the default for process types not listed.
+        Valid process types are listed in the table below.
         <simplelist type="vert" columns="4">
          <member><literal>archiver</literal></member>
          <member><literal>autovacuum</literal></member>
diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index a450254d1fa..97b9a6b5964 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -1279,12 +1279,12 @@ check_standard_conforming_strings(bool *newval, void **extra, GucSource source)
 /*
  * GUC check_hook for log_min_messages
  *
- * The parsing consists of a comma-separated list of TYPE:LEVEL elements. TYPE
- * is log_min_messages_process_types.  LEVEL is server_message_level_options. A
- * single LEVEL element should be part of this list and it is applied as a
- * final step to the process types that are not specified. For backward
- * compatibility, the old syntax is still accepted and it means to apply the
- * LEVEL for all process types.
+ * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL
+ * elements.  For each element, TYPE corresponds to a bk_category value (see
+ * postmaster/proctypelist.h), and LEVEL is one of log_min_message_lvls.
+ *
+ * In addition, there must be a single LEVEL element (with no TYPE part)
+ * which sets the default level for process types that aren't specified.
  */
 bool
 check_log_min_messages(char **newval, void **extra, GucSource source)
@@ -1293,22 +1293,21 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 	List	   *elemlist;
 	StringInfoData buf;
 	char	   *result;
-	bool		first = true;
 	int			newlevel[BACKEND_NUM_TYPES];
-	bool		assigned[BACKEND_NUM_TYPES];
+	bool		assigned[BACKEND_NUM_TYPES] = {0};
 	int			genericlevel = -1;	/* -1 means not assigned */
 
-	/* Initialize the array. */
-	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
-	{
-		newlevel[i] = WARNING;
-		assigned[i] = false;
-	}
+	const char *const process_types[] = {
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \
+		[bktype] = bkcategory,
+#include "postmaster/proctypelist.h"
+#undef PG_PROCTYPE
+	};
 
 	/* Need a modifiable copy of string. */
 	rawstring = guc_strdup(LOG, *newval);
 
-	/* Parse string into list of identifiers. */
+	/* Parse the string into a list. */
 	if (!SplitGUCList(rawstring, ',', &elemlist))
 	{
 		/* syntax error in list */
@@ -1321,27 +1320,22 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 	/* Validate and assign log level and process type. */
 	foreach_ptr(char, tok, elemlist)
 	{
-		char	   *sep;
-		const struct config_enum_entry *entry;
+		char	   *sep = strchr(tok, ':');
 
 		/*
-		 * Check whether there is a process type following the log level. If
-		 * there is no separator, it means this is the generic log level. The
-		 * generic log level will be assigned to the process types that were
-		 * not informed.
+		 * If there's no ':' separator in the entry, this is the default
+		 * value.  Otherwise it's a process type-specific entry.
 		 */
-		sep = strchr(tok, ':');
 		if (sep == NULL)
 		{
+			const struct config_enum_entry *entry;
 			bool		found = false;
 
 			/* Reject duplicates for generic log level. */
 			if (genericlevel != -1)
 			{
-				GUC_check_errdetail("Generic log level was already assigned.");
-				guc_free(rawstring);
-				list_free(elemlist);
-				return false;
+				GUC_check_errdetail("Redundant specification of default log level.");
+				goto lmm_fail;
 			}
 
 			/* Is the log level valid? */
@@ -1358,9 +1352,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 			if (!found)
 			{
 				GUC_check_errdetail("Unrecognized log level: \"%s\".", tok);
-				guc_free(rawstring);
-				list_free(elemlist);
-				return false;
+				goto lmm_fail;
 			}
 		}
 		else
@@ -1368,23 +1360,19 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 			char	   *loglevel;
 			char	   *ptype;
 			bool		found = false;
+			int			level;
+			const struct config_enum_entry *entry;
 
-			ptype = guc_malloc(LOG, (sep - tok) + 1);
-			if (!ptype)
-			{
-				guc_free(rawstring);
-				list_free(elemlist);
-				return false;
-			}
-			memcpy(ptype, tok, sep - tok);
-			ptype[sep - tok] = '\0';
+			ptype = tok;
 			loglevel = sep + 1;
+			*sep = '\0';
 
 			/* Is the log level valid? */
 			for (entry = server_message_level_options; entry && entry->name; entry++)
 			{
 				if (pg_strcasecmp(entry->name, loglevel) == 0)
 				{
+					level = entry->val;
 					found = true;
 					break;
 				}
@@ -1392,50 +1380,49 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 
 			if (!found)
 			{
-				GUC_check_errdetail("Unrecognized log level: \"%s\".", loglevel);
-				guc_free(ptype);
-				guc_free(rawstring);
-				list_free(elemlist);
-				return false;
+				GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".",
+									ptype, loglevel);
+				goto lmm_fail;
 			}
 
-			/*
-			 * Is the process type name valid? There might be multiple entries
-			 * per process type, don't bail out because it can assign the
-			 * value for multiple entries.
-			 */
+			/* Is the process type name valid and unique? */
 			found = false;
 			for (int i = 0; i < BACKEND_NUM_TYPES; i++)
 			{
-				if (pg_strcasecmp(log_min_messages_process_types[i], ptype) == 0)
+				if (pg_strcasecmp(process_types[i], ptype) == 0)
 				{
 					/* Reject duplicates for a process type. */
 					if (assigned[i])
 					{
-						GUC_check_errdetail("Process type \"%s\" was already assigned.", ptype);
-						guc_free(ptype);
-						guc_free(rawstring);
-						list_free(elemlist);
-						return false;
+						GUC_check_errdetail("Redundant log level specification for process type \"%s\".",
+											ptype);
+						goto lmm_fail;
 					}
 
-					newlevel[i] = entry->val;
+					newlevel[i] = level;
 					assigned[i] = true;
 					found = true;
+					break;
 				}
 			}
 
 			if (!found)
 			{
 				GUC_check_errdetail("Unrecognized process type: \"%s\".", ptype);
-				guc_free(ptype);
-				guc_free(rawstring);
-				list_free(elemlist);
-				return false;
+				goto lmm_fail;
 			}
 
-			guc_free(ptype);
+			/* Put the separator back in place */
+			*sep = ':';
 		}
+
+		/* all good */
+		continue;
+
+lmm_fail:
+		guc_free(rawstring);
+		list_free(elemlist);
+		return false;
 	}
 
 	/*
@@ -1443,7 +1430,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 	 */
 	if (genericlevel == -1)
 	{
-		GUC_check_errdetail("Generic log level was not defined.");
+		GUC_check_errdetail("Default log level was not defined.");
 		guc_free(rawstring);
 		list_free(elemlist);
 		return false;
@@ -1461,24 +1448,18 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 	}
 
 	/*
-	 * Use a stable representation of log_min_messages. The generic level is
-	 * always the first element and the other elements (type:level) are sorted
-	 * by process type. See log_min_messages_cmp for details.
+	 * To present a nice view to users, sort the output list by process type,
+	 * with the default value first.
 	 */
 	list_sort(elemlist, log_min_messages_cmp);
 
-	initStringInfo(&buf);
+	initStringInfoExt(&buf, strlen(rawstring) + 1);
 	foreach_ptr(char, tok, elemlist)
 	{
-		if (first)
-		{
+		if (foreach_current_index(tok) == 0)
 			appendStringInfoString(&buf, tok);
-			first = false;
-		}
 		else
-		{
 			appendStringInfo(&buf, ", %s", tok);
-		}
 	}
 
 	result = (char *) guc_malloc(LOG, buf.len + 1);
@@ -1505,6 +1486,10 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 	return true;
 }
 
+/*
+ * list_sort() callback for check_log_min_messages.  The default element
+ * goes first; the rest are ordered by strcmp() of the process type.
+ */
 static int
 log_min_messages_cmp(const ListCell *a, const ListCell *b)
 {
diff --git a/src/backend/postmaster/launch_backend.c b/src/backend/postmaster/launch_backend.c
index 08553c0f024..05b1feef3cf 100644
--- a/src/backend/postmaster/launch_backend.c
+++ b/src/backend/postmaster/launch_backend.c
@@ -178,7 +178,7 @@ typedef struct
 } child_process_kind;
 
 static child_process_kind child_process_kinds[] = {
-#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \
 	[bktype] = {description, main_func, shmem_attach},
 #include "postmaster/proctypelist.h"
 #undef PG_PROCTYPE
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index efba20fa8b9..03f6c8479f2 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -266,7 +266,7 @@ GetBackendTypeDesc(BackendType backendType)
 
 	switch (backendType)
 	{
-#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages)	\
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach)	\
 		case bktype: backendDesc = description; break;
 #include "postmaster/proctypelist.h"
 #undef PG_PROCTYPE
diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c
index 46a5cf13e4a..741fce8dede 100644
--- a/src/backend/utils/misc/guc_tables.c
+++ b/src/backend/utils/misc/guc_tables.c
@@ -657,23 +657,15 @@ char	   *role_string;
 bool		in_hot_standby_guc;
 
 /*
- * log_min_messages
+ * set default log_min_messages to WARNING for all process types
  */
 int			log_min_messages[] = {
-#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
-	[bktype] = log_min_messages,
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \
+	[bktype] = WARNING,
 #include "postmaster/proctypelist.h"
 #undef PG_PROCTYPE
 };
 
-const char *const log_min_messages_process_types[] = {
-#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach, log_min_messages) \
-	[bktype] = bkcategory,
-#include "postmaster/proctypelist.h"
-#undef PG_PROCTYPE
-};
-
-
 /*
  * Displayable names for context types (enum GucContext)
  *
diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h
index 0ddd805f0e7..4e259e84c2d 100644
--- a/src/include/postmaster/proctypelist.h
+++ b/src/include/postmaster/proctypelist.h
@@ -30,22 +30,22 @@
  */
 
 
-/* bktype, bkcategory, description, main_func, shmem_attach, log_min_messages */
-PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true, WARNING)
-PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true, WARNING)
-PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true, WARNING)
-PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true, WARNING)
-PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true, WARNING)
-PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true, WARNING)
-PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true, WARNING)
-PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true, WARNING)
-PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false, WARNING)
-PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true, WARNING)
-PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false, WARNING)
-PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true, WARNING)
-PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false, WARNING)
-PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true, WARNING)
-PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true, WARNING)
-PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true, WARNING)
-PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true, WARNING)
-PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true, WARNING)
+/* bktype, bkcategory, description, main_func, shmem_attach */
+PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true)
+PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true)
+PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true)
+PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true)
+PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true)
+PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true)
+PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true)
+PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true)
+PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false)
+PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true)
+PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false)
+PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true)
+PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false)
+PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true)
+PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true)
+PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true)
+PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true)
+PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true)
diff --git a/src/test/regress/expected/guc.out b/src/test/regress/expected/guc.out
index 9a6fd503009..616b10f80d6 100644
--- a/src/test/regress/expected/guc.out
+++ b/src/test/regress/expected/guc.out
@@ -955,19 +955,19 @@ SHOW log_min_messages;
 
 SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1';  --fail
 ERROR:  invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1"
-DETAIL:  Generic log level was not defined.
+DETAIL:  Default log level was not defined.
 SET log_min_messages TO 'debug1, backend:error, fatal';  -- fail
 ERROR:  invalid value for parameter "log_min_messages": "debug1, backend:error, fatal"
-DETAIL:  Generic log level was already assigned.
+DETAIL:  Redundant specification of default log level.
 SET log_min_messages TO 'backend:error, debug1, backend:warning';  -- fail
 ERROR:  invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning"
-DETAIL:  Process type "backend" was already assigned.
+DETAIL:  Redundant log level specification for process type "backend".
 SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1';  -- fail
 ERROR:  invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1"
 DETAIL:  Unrecognized process type: "foo".
 SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1';  -- fail
 ERROR:  invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1"
-DETAIL:  Unrecognized log level: "bar".
+DETAIL:  Unrecognized log level for process type "checkpointer": "bar".
 SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3';
 SHOW log_min_messages;
                                         log_min_messages                                         
-- 
2.39.5



  [text/x-patch] v11-0003-fixup-review.patch (2.4K, ../../88d8e984-ad63-48c1-86af-d50d7776942f@app.fastmail.com/4-v11-0003-fixup-review.patch)
  download | inline diff:
From ff64f521873c5f75ae5813c4441d55404c1854f8 Mon Sep 17 00:00:00 2001
From: Euler Taveira <euler@eulerto.com>
Date: Fri, 6 Feb 2026 12:42:21 -0300
Subject: [PATCH v11 3/3] fixup! review

---
 src/backend/commands/variable.c | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c
index 97b9a6b5964..8ea6f6f57e6 100644
--- a/src/backend/commands/variable.c
+++ b/src/backend/commands/variable.c
@@ -1295,7 +1295,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 	char	   *result;
 	int			newlevel[BACKEND_NUM_TYPES];
 	bool		assigned[BACKEND_NUM_TYPES] = {0};
-	int			genericlevel = -1;	/* -1 means not assigned */
+	int			defaultlevel = -1;	/* -1 means not assigned */
 
 	const char *const process_types[] = {
 #define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \
@@ -1331,8 +1331,8 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 			const struct config_enum_entry *entry;
 			bool		found = false;
 
-			/* Reject duplicates for generic log level. */
-			if (genericlevel != -1)
+			/* Reject duplicates for default log level. */
+			if (defaultlevel != -1)
 			{
 				GUC_check_errdetail("Redundant specification of default log level.");
 				goto lmm_fail;
@@ -1343,7 +1343,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 			{
 				if (pg_strcasecmp(entry->name, tok) == 0)
 				{
-					genericlevel = entry->val;
+					defaultlevel = entry->val;
 					found = true;
 					break;
 				}
@@ -1426,9 +1426,9 @@ lmm_fail:
 	}
 
 	/*
-	 * The generic log level must be specified. It is the fallback value.
+	 * The default log level must be specified. It is the fallback value.
 	 */
-	if (genericlevel == -1)
+	if (defaultlevel == -1)
 	{
 		GUC_check_errdetail("Default log level was not defined.");
 		guc_free(rawstring);
@@ -1437,14 +1437,14 @@ lmm_fail:
 	}
 
 	/*
-	 * Apply the generic log level after all of the specific process types
+	 * Apply the default log level after all of the specific process types
 	 * have been assigned. Hence, it doesn't matter the order you specify the
-	 * generic log level, the final result will be the same.
+	 * default log level, the final result will be the same.
 	 */
 	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
 	{
 		if (!assigned[i])
-			newlevel[i] = genericlevel;
+			newlevel[i] = defaultlevel;
 	}
 
 	/*
-- 
2.39.5



^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2026-02-06 20:28  surya poondla <suryapoondla4@gmail.com>
  parent: surya poondla <suryapoondla4@gmail.com>
  0 siblings, 1 reply; 58+ messages in thread

From: surya poondla @ 2026-02-06 20:28 UTC (permalink / raw)
  To: Euler Taveira <euler@eulerto.com>; +Cc: pgsql-hackers@lists.postgresql.org

Hi Euler,

I am still reviewing the latest patch (v11).

Quick clarification on how this feature interacts with existing per-session
and per-role/database GUC settings:
If I set log_min_messages = 'warning, backend:error' at the cluster level:
  1. Can an individual session still override this with SET
log_min_messages = DEBUG1?
  2. Do role/database-level settings (e.g., ALTER ROLE alice SET
log_min_messages = DEBUG1) still work as expected?

I assume both would override the cluster-level backend:error setting
following standard GUC precedence rules, but wanted to confirm.

Regards,
Surya Poondla

^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2026-02-07 01:05  Euler Taveira <euler@eulerto.com>
  parent: surya poondla <suryapoondla4@gmail.com>
  0 siblings, 0 replies; 58+ messages in thread

From: Euler Taveira @ 2026-02-07 01:05 UTC (permalink / raw)
  To: surya poondla <suryapoondla4@gmail.com>; +Cc: pgsql-hackers@lists.postgresql.org

On Fri, Feb 6, 2026, at 5:28 PM, surya poondla wrote:
>
> Quick clarification on how this feature interacts with existing 
> per-session and per-role/database GUC settings:
> If I set log_min_messages = 'warning, backend:error' at the cluster 
> level:
>   1. Can an individual session still override this with SET 
> log_min_messages = DEBUG1?
>   2. Do role/database-level settings (e.g., ALTER ROLE alice SET 
> log_min_messages = DEBUG1) still work as expected?
>

Yes. Yes.

> I assume both would override the cluster-level backend:error setting 
> following standard GUC precedence rules, but wanted to confirm.
>

This feature doesn't change the GUC context.

postgres=# show log_min_messages;
    log_min_messages    
------------------------
 warning, backend:error
(1 row)

another terminal:

(gdb) p log_min_messages[0]@18
$16 = {19, 21, 19 <repeats 16 times>}

8<-----------------------------------------------------------------8<

postgres=# set log_min_messages to debug1; 
SET

another terminal:

(gdb) p log_min_messages[0]@18
$17 = {14 <repeats 18 times>}

8<-----------------------------------------------------------------8<

postgres=# reset log_min_messages;
RESET

another terminal:

(gdb) p log_min_messages[0]@18
$18 = {19, 21, 19 <repeats 16 times>}


-- 
Euler Taveira
EDB   https://www.enterprisedb.com/





^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2026-02-09 03:41  Chao Li <li.evan.chao@gmail.com>
  parent: Euler Taveira <euler@eulerto.com>
  1 sibling, 2 replies; 58+ messages in thread

From: Chao Li @ 2026-02-09 03:41 UTC (permalink / raw)
  To: Euler Taveira <euler@eulerto.com>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org



> On Feb 6, 2026, at 23:45, Euler Taveira <euler@eulerto.com> wrote:
> 
> On Fri, Feb 6, 2026, at 8:05 AM, Alvaro Herrera wrote:
>> Here are some fixups for the main patch.  I include yours so that CI
>> will test this one.
>> 
> 
> All suggestions seem good to me.
> 
>> * I'm not fond of the term "generic", so I changed it to "default",
>> which IMO makes more sense from the user point of view.
>> 
> 
> I'm fine with "default" instead of "generic". However, you forgot to replace
> "generic" in a few places. The attached patch fixes them all.
> 
>> * There seemed to be too much guc_mallocking[*] going on; for instance for
>> ptype.  I simplified that by just overwriting the : to a '\0', then we
>> can use the original string for pg_strcasecmp().  At the bottom of the
>> loop we restore the ':', so that the code below can reuse the original
>> values.  This is okay because we have already mallocked a copy for
>> SplitGUCList.
>> 
> 
> Nice trick.
> 
>> * There's no reason to add a WARNING to every process type in the
>> proctypelist.h table.  We can just set the values to WARNING in the
>> log_min_messages initializer in guc_tables.c.
>> 
> 
> Ok.
> 
>> * There's no reason AFAICS to have log_min_messages_process_types in
>> guc_tables.c; nobody else uses it.  I made it a local array of char*
>> inside the check_log_min_messages function.
>> 
> 
> That's true.
> 
>> * There's no need to initialize the newlevel[] array, since we're going
>> to assign values to every individual element at one point or another.
>> And for the assigned[] array, we don't need to set the value of each to
>> 'false' in a loop; we can just initialize to '{0}' and the compiler will
>> do the needful.  With this we can remove the loop entirely.
>> 
> 
> Ok.
> 
>> * "guc_free()+list_free()+return false" the whole time in the various
>> failure places across the loop was tiresome.  I put them all in a single
>> place and jump there with a goto.
>> 
> 
> I tend to avoid goto. However, in this case, it seems it improves readability.
> 
>> * in foreach() loops, tracking of a 'bool first' is unnecessary.  You
>> can just compare foreach_current_index() with 0.
>> 
> 
> Didn't know about foreach_current_index.
> 
>> * Rewrote docs and comments
>> 
> 
> Ok.
> 
> I attached the same patches that you shared plus the s/generic/default/ that I
> said earlier for the sake of CI.
> 
> 
> -- 
> Euler Taveira
> EDB   https://www.enterprisedb.com/<v11-0001-log_min_messages-per-process-type.patch><v11-0002-re...;

Hi Euler,

Thanks for updating the patch. I briefly played with v11 and overall it looks solid, but I ran into one usability concern.

While it’s reasonable to configure a full comma-separated list in postgresql.conf, it seems there’s currently no convenient way for a superuser to change the log level of a single process type. For example:

```
evantest=# alter system set log_min_messages='backend:debug4';
ERROR:  invalid value for parameter "log_min_messages": "backend:debug4"
DETAIL:  Default log level was not defined.
```

This feels a bit inconvenient in practice. Intuitively, it would be nice if setting a single type:level could be merged into the existing log_min_messages value, rather than requiring users to restate the entire configuration.

I dug into this a bit and noticed that, while check_log_min_messages() already normalizes and sorts the value (and could in theory merge with the existing setting), the current ALTER SYSTEM path doesn’t preserve the transformed value returned by the check hook. In AlterSystemSetConfigFile(), the newval produced by parse_and_validate_value() is effectively discarded for string GUCs:

```
	if (!parse_and_validate_value(record, value,
								  PGC_S_FILE, ERROR,
								  &newval, &newextra))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("invalid value for parameter \"%s\": \"%s\"",
						name, value)));

	if (record->vartype == PGC_STRING && newval.stringval != NULL)
		guc_free(newval.stringval);
```

So even if the check hook tried to be smarter, the infrastructure doesn’t currently allow it.

There’s also a related regression in the interactive case. Before this patch, when debugging via psql, a superuser could easily do:
```
# set log_min_messages = debug5
```

to temporarily increase verbosity for the current backend. With the new syntax, doing the equivalent now requires copying the entire log_min_messages string and modifying just one part, which feels noticeably heavier for quick debugging.

Do you think it would make sense to support setting a specific process type’s log level more directly (either via merging semantics or some other mechanism)?

Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/









^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2026-02-09 12:40  Alvaro Herrera <alvherre@alvh.no-ip.org>
  parent: Chao Li <li.evan.chao@gmail.com>
  1 sibling, 0 replies; 58+ messages in thread

From: Alvaro Herrera @ 2026-02-09 12:40 UTC (permalink / raw)
  To: Chao Li <li.evan.chao@gmail.com>; +Cc: Euler Taveira <euler@eulerto.com>; japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org

On 2026-Feb-09, Chao Li wrote:

> While it’s reasonable to configure a full comma-separated list in
> postgresql.conf, it seems there’s currently no convenient way for a
> superuser to change the log level of a single process type. For
> example:
> 
> ```
> evantest=# alter system set log_min_messages='backend:debug4';
> ERROR:  invalid value for parameter "log_min_messages": "backend:debug4"
> DETAIL:  Default log level was not defined.
> ```
> 
> This feels a bit inconvenient in practice. Intuitively, it would be
> nice if setting a single type:level could be merged into the existing
> log_min_messages value, rather than requiring users to restate the
> entire configuration.

This has been discussed before and no satisfactory solution has been
found.  You're welcome to spend time thinking on this; see e.g.
https://postgr.es/m/20200928033924.GA998042@toroid.org where not only
the details were discussed, but also some links to other threads about
the same idea were given.

-- 
Álvaro Herrera               48°01'N 7°57'E  —  https://www.EnterpriseDB.com/





^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2026-02-09 12:50  Alvaro Herrera <alvherre@alvh.no-ip.org>
  parent: Chao Li <li.evan.chao@gmail.com>
  1 sibling, 1 reply; 58+ messages in thread

From: Alvaro Herrera @ 2026-02-09 12:50 UTC (permalink / raw)
  To: Chao Li <li.evan.chao@gmail.com>; +Cc: Euler Taveira <euler@eulerto.com>; japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org

On 2026-Feb-09, Chao Li wrote:

> There’s also a related regression in the interactive case. Before this
> patch, when debugging via psql, a superuser could easily do:
>
> ```
> # set log_min_messages = debug5
> ```
> 
> to temporarily increase verbosity for the current backend. With the
> new syntax, doing the equivalent now requires copying the entire
> log_min_messages string and modifying just one part, which feels
> noticeably heavier for quick debugging.

Nonsense.  You can still do the above, and it has the same behavior as
before.

-- 
Álvaro Herrera         PostgreSQL Developer  —  https://www.EnterpriseDB.com/
"Curiosity is the hope that something wondrous waits just out of sight,
for we are surrounded by a garden of miracles"                       (Namron)
                           (Marjorie Liu and Sana Takeda, Monstress: Inferno)





^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2026-02-09 12:58  Alvaro Herrera <alvherre@alvh.no-ip.org>
  parent: Euler Taveira <euler@eulerto.com>
  1 sibling, 1 reply; 58+ messages in thread

From: Alvaro Herrera @ 2026-02-09 12:58 UTC (permalink / raw)
  To: Euler Taveira <euler@eulerto.com>; +Cc: japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org, Chao Li <li.evan.chao@gmail.com>

On 2026-Feb-06, Euler Taveira wrote:

> On Fri, Feb 6, 2026, at 8:05 AM, Alvaro Herrera wrote:

> > * I'm not fond of the term "generic", so I changed it to "default",
> > which IMO makes more sense from the user point of view.
> 
> I'm fine with "default" instead of "generic". However, you forgot to replace
> "generic" in a few places. The attached patch fixes them all.

I just pushed this, and somehow I forgot to squash this into the commit.
I don't think it matters terribly much though, so I'm going to hang onto
this for a while in case some bug is found.

I made some more changes though.  Notably, I reworded some things in the
docs that appeared ungrammatical and added a simple example; added the
list of process types to postgresql.conf.sample; and moved the GUC
check/assign hooks to elog.c together with the other elog-affecting
GUCs, as I didn't think they made much sense in variable.c.

Another thing, which is actually quite significant, is that I fixed a
bug I had introduced on Friday without realizing: I had added a "break"
in the loop that searches for process types.  But we mustn't break
there!  Otherwise we fail to set the level for process types that share
the same process "category" with others, such as "backend" and
"autovacuum".  So I removed the break and added a comment about it.
It's sad that no tests broke when I introduced that bug, but I think it
would be difficult to cover this.

Thanks

-- 
Álvaro Herrera         PostgreSQL Developer  —  https://www.EnterpriseDB.com/
"That sort of implies that there are Emacs keystrokes which aren't obscure.
I've been using it daily for 2 years now and have yet to discover any key
sequence which makes any sense."                        (Paul Thomas)





^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2026-02-09 13:01  Chao Li <li.evan.chao@gmail.com>
  parent: Alvaro Herrera <alvherre@alvh.no-ip.org>
  0 siblings, 0 replies; 58+ messages in thread

From: Chao Li @ 2026-02-09 13:01 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: Euler Taveira <euler@eulerto.com>; japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org



> On Feb 9, 2026, at 20:50, Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:
> 
> On 2026-Feb-09, Chao Li wrote:
> 
>> There’s also a related regression in the interactive case. Before this
>> patch, when debugging via psql, a superuser could easily do:
>> 
>> ```
>> # set log_min_messages = debug5
>> ```
>> 
>> to temporarily increase verbosity for the current backend. With the
>> new syntax, doing the equivalent now requires copying the entire
>> log_min_messages string and modifying just one part, which feels
>> noticeably heavier for quick debugging.
> 
> Nonsense.  You can still do the above, and it has the same behavior as
> before.
> 

Ah, yes, for the SET scenario, it sets the log level for all types, so the backend type gets the value, which ends up with the same behavior as before.

Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/









^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2026-02-09 13:03  Alvaro Herrera <alvherre@alvh.no-ip.org>
  parent: Alvaro Herrera <alvherre@alvh.no-ip.org>
  0 siblings, 2 replies; 58+ messages in thread

From: Alvaro Herrera @ 2026-02-09 13:03 UTC (permalink / raw)
  To: Euler Taveira <euler@eulerto.com>; +Cc: japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org, Chao Li <li.evan.chao@gmail.com>

On 2026-Feb-09, Alvaro Herrera wrote:

> I just pushed this, and somehow I forgot to squash this into the commit.
> I don't think it matters terribly much though, so I'm going to hang onto
> this for a while in case some bug is found.

Euler mentioned offlist that I also forgot to remove
log_min_messages_process_types from guc.h.  So that gives us this patch
for now.


diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 129906e2daa..59315e94e3e 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -2190,7 +2190,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 	char	   *result;
 	int			newlevel[BACKEND_NUM_TYPES];
 	bool		assigned[BACKEND_NUM_TYPES] = {0};
-	int			genericlevel = -1;	/* -1 means not assigned */
+	int			defaultlevel = -1;	/* -1 means not assigned */
 
 	const char *const process_types[] = {
 #define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \
@@ -2228,8 +2228,8 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 			const struct config_enum_entry *entry;
 			bool		found;
 
-			/* Reject duplicates for generic log level. */
-			if (genericlevel != -1)
+			/* Reject duplicates for default log level. */
+			if (defaultlevel != -1)
 			{
 				GUC_check_errdetail("Redundant specification of default log level.");
 				goto lmm_fail;
@@ -2241,7 +2241,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 			{
 				if (pg_strcasecmp(entry->name, elem) == 0)
 				{
-					genericlevel = entry->val;
+					defaultlevel = entry->val;
 					found = true;
 					break;
 				}
@@ -2331,9 +2331,9 @@ lmm_fail:
 	}
 
 	/*
-	 * The generic log level must be specified. It is the fallback value.
+	 * The default log level must be specified. It is the fallback value.
 	 */
-	if (genericlevel == -1)
+	if (defaultlevel == -1)
 	{
 		GUC_check_errdetail("Default log level was not defined.");
 		guc_free(rawstring);
@@ -2345,7 +2345,7 @@ lmm_fail:
 	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
 	{
 		if (!assigned[i])
-			newlevel[i] = genericlevel;
+			newlevel[i] = defaultlevel;
 	}
 
 	/*
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index 8acbdba7ff5..c46203fabfe 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -329,8 +329,6 @@ extern PGDLLIMPORT bool trace_sort;
 extern PGDLLIMPORT bool optimize_bounded_sort;
 #endif
 
-extern PGDLLIMPORT const char *const log_min_messages_process_types[];
-
 /*
  * Declarations for options for enum values
  *

-- 
Álvaro Herrera               48°01'N 7°57'E  —  https://www.EnterpriseDB.com/
"I suspect most samba developers are already technically insane...
Of course, since many of them are Australians, you can't tell." (L. Torvalds)





^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2026-02-09 13:52  =?ISO-8859-1?B?emVuZ21hbg==?= <zengman@halodbtech.com>
  parent: Alvaro Herrera <alvherre@alvh.no-ip.org>
  1 sibling, 1 reply; 58+ messages in thread

From: zengman @ 2026-02-09 13:52 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: pgsql-hackers <pgsql-hackers@lists.postgresql.org>

> Euler mentioned offlist that I also forgot to remove
> log_min_messages_process_types from guc.h.  So that gives us this patch
> for now.

Hi, 

I just noticed that `log_min_messages_process_types` is not used in the latest commit and should be removed.
I also noticed that `message level` in the comments below should be deleted as well.

```
diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h
index 4e259e84c2d..229308ec137 100644
--- a/src/include/postmaster/proctypelist.h
+++ b/src/include/postmaster/proctypelist.h
@@ -25,8 +25,7 @@
  */
 
 /*
- * List of process types (symbol, category, description, Main function,
- * shmem_attach, message level) entries.
+ * List of process types (symbol, category, description, Main function, shmem_attach) entries.
  */
```

--
regards,
Man Zeng

^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* RE: log_min_messages per backend type
@ 2026-02-09 14:05  Shinoda, Noriyoshi (PSD Japan FSI) <noriyoshi.shinoda@hpe.com>
  parent: =?ISO-8859-1?B?emVuZ21hbg==?= <zengman@halodbtech.com>
  0 siblings, 0 replies; 58+ messages in thread

From: Shinoda, Noriyoshi (PSD Japan FSI) @ 2026-02-09 14:05 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: pgsql-hackers <pgsql-hackers@lists.postgresql.org>; zengman <zengman@halodbtech.com>

Hi, 
> I just pushed this, and somehow I forgot to squash this into the commit.
Thanks to the developers and committers.

The attached small patch changes the process names in config.sgml to alphabetical order. 
postgresql.conf.sample already has that.

Regards,
Noriyoshi Shinoda

-----Original Message-----
From: zengman <zengman@halodbtech.com> 
Sent: Monday, February 9, 2026 10:52 PM
To: Alvaro Herrera <alvherre@alvh.no-ip.org>
Cc: pgsql-hackers <pgsql-hackers@lists.postgresql.org>
Subject: Re: log_min_messages per backend type

> Euler mentioned offlist that I also forgot to remove 
> log_min_messages_process_types from guc.h.  So that gives us this 
> patch for now.

Hi, 

I just noticed that `log_min_messages_process_types` is not used in the latest commit and should be removed.
I also noticed that `message level` in the comments below should be deleted as well.

```
diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h
index 4e259e84c2d..229308ec137 100644
--- a/src/include/postmaster/proctypelist.h
+++ b/src/include/postmaster/proctypelist.h
@@ -25,8 +25,7 @@
  */
 
 /*
- * List of process types (symbol, category, description, Main function,
- * shmem_attach, message level) entries.
+ * List of process types (symbol, category, description, Main function, shmem_attach) entries.
  */
```

--
regards,
Man Zeng

Attachments:

  [application/octet-stream] log_min_messages_doc_v1.diff (791B, ../../LV8PR84MB3787E0206D4F69AF4331BB94EE65A@LV8PR84MB3787.NAMPRD84.PROD.OUTLOOK.COM/2-log_min_messages_doc_v1.diff)
  download | inline diff:
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 37342986969..61d39fd3366 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -7145,9 +7145,9 @@ local0.*    /var/log/postgresql
          <member><literal>checkpointer</literal></member>
          <member><literal>ioworker</literal></member>
          <member><literal>postmaster</literal></member>
-         <member><literal>syslogger</literal></member>
          <member><literal>slotsyncworker</literal></member>
          <member><literal>startup</literal></member>
+         <member><literal>syslogger</literal></member>
          <member><literal>walreceiver</literal></member>
          <member><literal>walsender</literal></member>
          <member><literal>walsummarizer</literal></member>


^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2026-02-10 22:05  Euler Taveira <euler@eulerto.com>
  parent: Alvaro Herrera <alvherre@alvh.no-ip.org>
  1 sibling, 1 reply; 58+ messages in thread

From: Euler Taveira @ 2026-02-10 22:05 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org, "Chao Li" <li.evan.chao@gmail.com>

On Mon, Feb 9, 2026, at 10:03 AM, Alvaro Herrera wrote:
> On 2026-Feb-09, Alvaro Herrera wrote:
>
>> I just pushed this, and somehow I forgot to squash this into the commit.
>> I don't think it matters terribly much though, so I'm going to hang onto
>> this for a while in case some bug is found.
>
> Euler mentioned offlist that I also forgot to remove
> log_min_messages_process_types from guc.h.  So that gives us this patch
> for now.
>

I expanded this patch a bit with fixes proposed by Man Zeng and Noriyoshi
Shinoda. Besides these changes, I noticed that

* default value in uppercase: since we changed the vartype from enum to string
  and enum maps DEBUG1 to "debug1" (see server_message_level_options), it
  should use the lowercase string to keep the same output as previous versions.
  There is no bug here. It is just a matter of keeping the same output.
* use single quote: since we changed the vartype from enum to string, we should
  add single quotes. If you don't add single quotes, it works (for a single
  default log level) -- as exercised in the tests. However, as soon as the user
  adds an extra element it fails. This avoids confusion.

$ psql
psql (19devel)
Type "help" for help.

postgres=# show log_min_messages;
 log_min_messages 
------------------
 WARNING
(1 row)

postgres=# set log_min_messages to debug1;
SET
postgres=# show log_min_messages;
 log_min_messages 
------------------
 debug1
(1 row)

postgres=# set log_min_messages to 'debug1';
SET
postgres=# show log_min_messages;
 log_min_messages 
------------------
 debug1
(1 row)

postgres=# set log_min_messages to warning, backend:error;
ERROR:  syntax error at or near ":"
LINE 1: set log_min_messages to warning, backend:error;
                                                ^
postgres=# set log_min_messages to 'warning, backend:error';
SET
postgres=# show log_min_messages;
    log_min_messages    
------------------------
 warning, backend:error
(1 row)


-- 
Euler Taveira
EDB   https://www.enterprisedb.com/

Attachments:

  [text/x-patch] 0001-Minor-cleanups-in-log_min_messages.patch (5.6K, ../../39c31ea3-489d-4333-9275-abf763acd71c@app.fastmail.com/2-0001-Minor-cleanups-in-log_min_messages.patch)
  download | inline diff:
From a9984718bed8f2fd7e6ba3c096a07349948a0598 Mon Sep 17 00:00:00 2001
From: Euler Taveira <euler@eulerto.com>
Date: Tue, 10 Feb 2026 11:39:56 -0300
Subject: [PATCH] Minor cleanups in log_min_messages

Fix some issues in commit 38e0190ced7. Remove an unused variable. Use
"default log level" consistently. Keep the process types in alphabetical
order.  Since log_min_messages type was changed from enum to string, it
is a good idea to use single quotes. Although it works for a single
default log level, it will fail as soon as the user tries to add an
extra element to the value. Using single quotes minimizes the chance of
confusion. Use lowercase string for the default value to keep the same
output as previous versions.
---
 doc/src/sgml/config.sgml                      |  2 +-
 src/backend/utils/error/elog.c                | 14 +++++++-------
 src/backend/utils/misc/guc_parameters.dat     |  2 +-
 src/backend/utils/misc/postgresql.conf.sample |  2 +-
 src/include/postmaster/proctypelist.h         |  2 +-
 src/include/utils/guc.h                       |  2 --
 6 files changed, 11 insertions(+), 13 deletions(-)

diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 37342986969..61d39fd3366 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -7145,9 +7145,9 @@ local0.*    /var/log/postgresql
          <member><literal>checkpointer</literal></member>
          <member><literal>ioworker</literal></member>
          <member><literal>postmaster</literal></member>
-         <member><literal>syslogger</literal></member>
          <member><literal>slotsyncworker</literal></member>
          <member><literal>startup</literal></member>
+         <member><literal>syslogger</literal></member>
          <member><literal>walreceiver</literal></member>
          <member><literal>walsender</literal></member>
          <member><literal>walsummarizer</literal></member>
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 129906e2daa..59315e94e3e 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -2190,7 +2190,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 	char	   *result;
 	int			newlevel[BACKEND_NUM_TYPES];
 	bool		assigned[BACKEND_NUM_TYPES] = {0};
-	int			genericlevel = -1;	/* -1 means not assigned */
+	int			defaultlevel = -1;	/* -1 means not assigned */
 
 	const char *const process_types[] = {
 #define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \
@@ -2228,8 +2228,8 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 			const struct config_enum_entry *entry;
 			bool		found;
 
-			/* Reject duplicates for generic log level. */
-			if (genericlevel != -1)
+			/* Reject duplicates for default log level. */
+			if (defaultlevel != -1)
 			{
 				GUC_check_errdetail("Redundant specification of default log level.");
 				goto lmm_fail;
@@ -2241,7 +2241,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 			{
 				if (pg_strcasecmp(entry->name, elem) == 0)
 				{
-					genericlevel = entry->val;
+					defaultlevel = entry->val;
 					found = true;
 					break;
 				}
@@ -2331,9 +2331,9 @@ lmm_fail:
 	}
 
 	/*
-	 * The generic log level must be specified. It is the fallback value.
+	 * The default log level must be specified. It is the fallback value.
 	 */
-	if (genericlevel == -1)
+	if (defaultlevel == -1)
 	{
 		GUC_check_errdetail("Default log level was not defined.");
 		guc_free(rawstring);
@@ -2345,7 +2345,7 @@ lmm_fail:
 	for (int i = 0; i < BACKEND_NUM_TYPES; i++)
 	{
 		if (!assigned[i])
-			newlevel[i] = genericlevel;
+			newlevel[i] = defaultlevel;
 	}
 
 	/*
diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat
index 762b8efe6b0..4b8bd57d1e7 100644
--- a/src/backend/utils/misc/guc_parameters.dat
+++ b/src/backend/utils/misc/guc_parameters.dat
@@ -1698,7 +1698,7 @@
   long_desc => 'Each level includes all the levels that follow it. The later the level, the fewer messages are sent.',
   flags => 'GUC_LIST_INPUT',
   variable => 'log_min_messages_string',
-  boot_val => '"WARNING"',
+  boot_val => '"warning"',
   check_hook => 'check_log_min_messages',
   assign_hook => 'assign_log_min_messages',
 },
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 6e82c8e055d..12183f6996f 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -532,7 +532,7 @@
 
 # - When to Log -
 
-#log_min_messages = warning             # comma-separated list of
+#log_min_messages = 'warning'           # comma-separated list of
                                         # process_type:level entries, plus
                                         # one freestanding level as default.
                                         # Valid process types are:
diff --git a/src/include/postmaster/proctypelist.h b/src/include/postmaster/proctypelist.h
index 4e259e84c2d..feac19ba207 100644
--- a/src/include/postmaster/proctypelist.h
+++ b/src/include/postmaster/proctypelist.h
@@ -26,7 +26,7 @@
 
 /*
  * List of process types (symbol, category, description, Main function,
- * shmem_attach, message level) entries.
+ * shmem_attach) entries.
  */
 
 
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index 8acbdba7ff5..c46203fabfe 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -329,8 +329,6 @@ extern PGDLLIMPORT bool trace_sort;
 extern PGDLLIMPORT bool optimize_bounded_sort;
 #endif
 
-extern PGDLLIMPORT const char *const log_min_messages_process_types[];
-
 /*
  * Declarations for options for enum values
  *
-- 
2.39.5



^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2026-02-11 05:21  Chao Li <li.evan.chao@gmail.com>
  parent: Euler Taveira <euler@eulerto.com>
  0 siblings, 1 reply; 58+ messages in thread

From: Chao Li @ 2026-02-11 05:21 UTC (permalink / raw)
  To: Euler Taveira <euler@eulerto.com>; +Cc: Alvaro Herrera <alvherre@alvh.no-ip.org>; japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org



> On Feb 11, 2026, at 06:05, Euler Taveira <euler@eulerto.com> wrote:
> 
> On Mon, Feb 9, 2026, at 10:03 AM, Alvaro Herrera wrote:
>> On 2026-Feb-09, Alvaro Herrera wrote:
>> 
>>> I just pushed this, and somehow I forgot to squash this into the commit.
>>> I don't think it matters terribly much though, so I'm going to hang onto
>>> this for a while in case some bug is found.
>> 
>> Euler mentioned offlist that I also forgot to remove
>> log_min_messages_process_types from guc.h.  So that gives us this patch
>> for now.
>> 
> 
> I expanded this patch a bit with fixes proposed by Man Zeng and Noriyoshi
> Shinoda. Besides these changes, I noticed that
> 
> * default value in uppercase: since we changed the vartype from enum to string
>  and enum maps DEBUG1 to "debug1" (see server_message_level_options), it
>  should use the lowercase string to keep the same output as previous versions.
>  There is no bug here. It is just a matter of keeping the same output.
> * use single quote: since we changed the vartype from enum to string, we should
>  add single quotes. If you don't add single quotes, it works (for a single
>  default log level) -- as exercised in the tests. However, as soon as the user
>  adds an extra element it fails. This avoids confusion.
> 
> $ psql
> psql (19devel)
> Type "help" for help.
> 
> postgres=# show log_min_messages;
> log_min_messages 
> ------------------
> WARNING
> (1 row)
> 
> postgres=# set log_min_messages to debug1;
> SET
> postgres=# show log_min_messages;
> log_min_messages 
> ------------------
> debug1
> (1 row)
> 
> postgres=# set log_min_messages to 'debug1';
> SET
> postgres=# show log_min_messages;
> log_min_messages 
> ------------------
> debug1
> (1 row)
> 
> postgres=# set log_min_messages to warning, backend:error;
> ERROR:  syntax error at or near ":"
> LINE 1: set log_min_messages to warning, backend:error;
>                                                ^
> postgres=# set log_min_messages to 'warning, backend:error';
> SET
> postgres=# show log_min_messages;
>    log_min_messages    
> ------------------------
> warning, backend:error
> (1 row)
> 
> 
> -- 
> Euler Taveira
> EDB   https://www.enterprisedb.com/<0001-Minor-cleanups-in-log_min_messages.patch;

This cleanup patch looks good to me.

Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/









^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2026-07-20 12:15  Alvaro Herrera <alvherre@alvh.no-ip.org>
  parent: Chao Li <li.evan.chao@gmail.com>
  0 siblings, 2 replies; 58+ messages in thread

From: Alvaro Herrera @ 2026-07-20 12:15 UTC (permalink / raw)
  To: Chao Li <li.evan.chao@gmail.com>; +Cc: Euler Taveira <euler@eulerto.com>; japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org

Hello,

Here's a couple of trivial cleanups of this code.  0001 simplifies code
and removes a duplicate by creating a small subroutine; 0002 also
simplifies memory freeing a tad, by freeing allocations as soon as
no longer needed instead of waiting and being forced to duplicate the
frees in several places.

This is two commits here just for ease of review, but I'd probably push
as one commit (and without backpatch).

-- 
Álvaro Herrera        Breisgau, Deutschland  —  https://www.EnterpriseDB.com/
"World domination is proceeding according to plan"        (Andrew Morton)

Attachments:

  [text/x-diff] 0001-simplify-coding-in-check_log_min_messages.patch (3.3K, ../../al4PzUn6y82eP-t1@alvherre.pgsql/2-0001-simplify-coding-in-check_log_min_messages.patch)
  download | inline diff:
From 4a781c3cc700f455705994dbc36c34355defcd31 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Herrera?= <alvherre@kurilemu.de>
Date: Tue, 17 Feb 2026 16:33:18 +0100
Subject: [PATCH 1/2] simplify coding in check_log_min_messages

---
 src/backend/utils/error/elog.c | 47 ++++++++++++++--------------------
 1 file changed, 19 insertions(+), 28 deletions(-)

diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index b9d2c96b97a..80d4eef755a 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -193,6 +193,7 @@ static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
 static void backtrace_cleanup(int code, Datum arg);
 static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
 static void FreeErrorDataContents(ErrorData *edata);
+static int	parse_message_level(const char *level);
 static int	log_min_messages_cmp(const ListCell *a, const ListCell *b);
 static void write_console(const char *line, int len);
 static const char *process_log_prefix_padding(const char *p, int *ppadding);
@@ -2403,9 +2404,6 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 		 */
 		if (sep == NULL)
 		{
-			const struct config_enum_entry *entry;
-			bool		found;
-
 			/* Reject duplicates for default log level. */
 			if (defaultlevel != -1)
 			{
@@ -2414,18 +2412,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 			}
 
 			/* Validate the log level */
-			found = false;
-			for (entry = server_message_level_options; entry && entry->name; entry++)
-			{
-				if (pg_strcasecmp(entry->name, elem) == 0)
-				{
-					defaultlevel = entry->val;
-					found = true;
-					break;
-				}
-			}
-
-			if (!found)
+			if ((defaultlevel = parse_message_level(elem)) == 0)
 			{
 				GUC_check_errdetail("Unrecognized log level: \"%s\".", elem);
 				goto lmm_fail;
@@ -2437,7 +2424,6 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 			char	   *ptype = elem;
 			bool		found;
 			int			level;
-			const struct config_enum_entry *entry;
 
 			/*
 			 * Temporarily clobber the ':' with a string terminator, so that
@@ -2446,18 +2432,7 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 			*sep = '\0';
 
 			/* Validate the log level */
-			found = false;
-			for (entry = server_message_level_options; entry && entry->name; entry++)
-			{
-				if (pg_strcasecmp(entry->name, loglevel) == 0)
-				{
-					level = entry->val;
-					found = true;
-					break;
-				}
-			}
-
-			if (!found)
+			if ((level = parse_message_level(loglevel)) == 0)
 			{
 				GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".",
 									ptype, loglevel);
@@ -2566,6 +2541,22 @@ lmm_fail:
 	return true;
 }
 
+/*
+ * Recognize the given string as a message level (for log_min_messages), and
+ * return its value if successful.  Otherwise, return 0.
+ */
+static int
+parse_message_level(const char *level)
+{
+	const struct config_enum_entry *entry;
+
+	for (entry = server_message_level_options; entry && entry->name; entry++)
+		if (pg_strcasecmp(entry->name, level) == 0)
+			return entry->val;
+
+	return 0;
+}
+
 /*
  * list_sort() callback for check_log_min_messages.  The default element
  * goes first; the rest are ordered by strcmp() of the process type.
-- 
2.47.3

  [text/x-diff] 0002-memory-free-simplify.patch (1.9K, ../../al4PzUn6y82eP-t1@alvherre.pgsql/3-0002-memory-free-simplify.patch)
  download | inline diff:
From 6f31ae2fb2506f05c0a9bb9da3a88c1266d5f457 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Herrera?= <alvherre@kurilemu.de>
Date: Sun, 5 Jul 2026 14:10:18 +0200
Subject: [PATCH 2/2] memory free simplify

---
 src/backend/utils/error/elog.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 80d4eef755a..7d229279dbe 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -2364,6 +2364,7 @@ bool
 check_log_min_messages(char **newval, void **extra, GucSource source)
 {
 	char	   *rawstring;
+	int			rawstring_len;
 	List	   *elemlist;
 	StringInfoData buf;
 	char	   *result;
@@ -2392,6 +2393,8 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 		guc_free(rawstring);
 		return false;
 	}
+	rawstring_len = strlen(rawstring);
+	guc_free(rawstring);
 
 	/* Validate and assign log level and process type. */
 	foreach_ptr(char, elem, elemlist)
@@ -2478,7 +2481,6 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
 		continue;
 
 lmm_fail:
-		guc_free(rawstring);
 		list_free(elemlist);
 		return false;
 	}
@@ -2489,7 +2491,6 @@ lmm_fail:
 	if (defaultlevel == -1)
 	{
 		GUC_check_errdetail("Default log level was not defined.");
-		guc_free(rawstring);
 		list_free(elemlist);
 		return false;
 	}
@@ -2507,7 +2508,7 @@ lmm_fail:
 	 */
 	list_sort(elemlist, log_min_messages_cmp);
 
-	initStringInfoExt(&buf, strlen(rawstring) + 1);
+	initStringInfoExt(&buf, rawstring_len + 1);
 	foreach_ptr(char, elem, elemlist)
 	{
 		if (foreach_current_index(elem) == 0)
@@ -2515,6 +2516,7 @@ lmm_fail:
 		else
 			appendStringInfo(&buf, ", %s", elem);
 	}
+	list_free(elemlist);
 
 	result = guc_strdup(LOG, buf.data);
 	if (!result)
@@ -2526,8 +2528,6 @@ lmm_fail:
 	guc_free(*newval);
 	*newval = result;
 
-	guc_free(rawstring);
-	list_free(elemlist);
 	pfree(buf.data);
 
 	/*
-- 
2.47.3

^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2026-07-20 13:55  Fujii Masao <masao.fujii@gmail.com>
  parent: Alvaro Herrera <alvherre@alvh.no-ip.org>
  1 sibling, 0 replies; 58+ messages in thread

From: Fujii Masao @ 2026-07-20 13:55 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: Chao Li <li.evan.chao@gmail.com>; Euler Taveira <euler@eulerto.com>; japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org

On Mon, Jul 20, 2026 at 9:15 PM Alvaro Herrera <alvherre@alvh.no-ip.org> wrote:
>
> Hello,
>
> Here's a couple of trivial cleanups of this code.  0001 simplifies code
> and removes a duplicate by creating a small subroutine; 0002 also
> simplifies memory freeing a tad, by freeing allocations as soon as
> no longer needed instead of waiting and being forced to duplicate the
> frees in several places.

+ rawstring_len = strlen(rawstring);
+ guc_free(rawstring);

SplitGUCList() stores pointers into rawstring in elemlist, so
after guc_free(rawstring), the subsequent foreach over elemlist,etc
still seem to dereference freed memory. So rawstring should remain
alive until we're done using all the elements of elemlist?

BTW, with the patches, I saw the following initdb failure:

    running bootstrap script ... LOG:  invalid value for parameter
"log_min_messages": "warning"
    DETAIL:  Unrecognized log level: "".
    FATAL:  failed to initialize log_min_messages to "warning"
    child process exited with exit code 1
    initdb: removing data directory "data"

Regards,

-- 
Fujii Masao






^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2026-07-21 09:39  Jonathan Gonzalez V. <jonathan.abdiel@gmail.com>
  parent: Alvaro Herrera <alvherre@alvh.no-ip.org>
  1 sibling, 1 reply; 58+ messages in thread

From: Jonathan Gonzalez V. @ 2026-07-21 09:39 UTC (permalink / raw)
  To: Alvaro Herrera <alvherre@alvh.no-ip.org>; +Cc: Chao Li <li.evan.chao@gmail.com>; Euler Taveira <euler@eulerto.com>; japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org

Hello!


Alvaro Herrera <alvherre@alvh.no-ip.org> writes:
> Hello,
>
> Here's a couple of trivial cleanups of this code.  0001 simplifies code
> and removes a duplicate by creating a small subroutine; 0002 also
> simplifies memory freeing a tad, by freeing allocations as soon as
> no longer needed instead of waiting and being forced to duplicate the
> frees in several places.
>
> This is two commits here just for ease of review, but I'd probably push
> as one commit (and without backpatch).

The first commit passed without problems and works like a charm, but the
second one is breaking the detection of the messages here:

@@ -2392,6 +2393,8 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
                guc_free(rawstring);
                return false;
        }
+       rawstring_len = strlen(rawstring);
+       guc_free(rawstring);


The rawstring is being reference in the elemlist, so the free is making
the string empty when is going to be used later:

(gdb) print rawstring
$17 = 0x555556f85e08 ""
(gdb) print *elemlist->elements
$18 = {
  ptr_value = 0x555556f85e08,
  int_value = 1459117576,
  oid_value = 1459117576,
  xid_value = 1459117576
}

Regards!
--
Jonathan Gonzalez V.
EDB
https://www.enterprisedb.com






^ permalink  raw  reply  [nested|flat] 58+ messages in thread

* Re: log_min_messages per backend type
@ 2026-07-21 13:07  Alvaro Herrera <alvherre@alvh.no-ip.org>
  parent: Jonathan Gonzalez V. <jonathan.abdiel@gmail.com>
  0 siblings, 0 replies; 58+ messages in thread

From: Alvaro Herrera @ 2026-07-21 13:07 UTC (permalink / raw)
  To: Jonathan Gonzalez V. <jonathan.abdiel@gmail.com>; +Cc: Chao Li <li.evan.chao@gmail.com>; Euler Taveira <euler@eulerto.com>; japin <japinli@hotmail.com>; Andres Freund <andres@anarazel.de>; pgsql-hackers@lists.postgresql.org

On 2026-Jul-21, Jonathan Gonzalez V. wrote:

> but the
> second one is breaking the detection of the messages here:
> 
> @@ -2392,6 +2393,8 @@ check_log_min_messages(char **newval, void **extra, GucSource source)
>                 guc_free(rawstring);
>                 return false;
>         }
> +       rawstring_len = strlen(rawstring);
> +       guc_free(rawstring);
> 
> 
> The rawstring is being reference in the elemlist, so the free is making
> the string empty when is going to be used later:

Yeah, the second one is clearly busted.  Please consider it withdrawn.

-- 
Álvaro Herrera               48°01'N 7°57'E  —  https://www.EnterpriseDB.com/
"After a quick R of TFM, all I can say is HOLY CR** THAT IS COOL! PostgreSQL was
amazing when I first started using it at 7.2, and I'm continually astounded by
learning new features and techniques made available by the continuing work of
the development team."
Berend Tober, http://archives.postgresql.org/pgsql-hackers/2007-08/msg01009.php






^ permalink  raw  reply  [nested|flat] 58+ messages in thread


end of thread, other threads:[~2026-07-21 13:07 UTC | newest]

Thread overview: 58+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2024-12-17 19:14 log_min_messages per backend type Euler Taveira <euler@eulerto.com>
2024-12-19 04:13 ` Kirill Reshke <reshkekirill@gmail.com>
2024-12-19 13:34   ` Euler Taveira <euler@eulerto.com>
2025-02-05 18:51 ` Álvaro Herrera <alvherre@alvh.no-ip.org>
2025-03-05 00:33   ` Euler Taveira <euler@eulerto.com>
2025-03-05 16:40     ` Andrew Dunstan <andrew@dunslane.net>
2025-03-06 13:20       ` Matheus Alcantara <matheusssilv97@gmail.com>
2025-03-06 21:33       ` Euler Taveira <euler@eulerto.com>
2025-03-06 02:53     ` Fujii Masao <masao.fujii@oss.nttdata.com>
2025-03-06 14:33       ` Euler Taveira <euler@eulerto.com>
2025-03-06 13:33     ` Andres Freund <andres@anarazel.de>
2025-07-31 14:19       ` Euler Taveira <euler@eulerto.com>
2025-07-31 16:22         ` Japin Li <japinli@hotmail.com>
2025-07-31 16:51           ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2025-07-31 23:34             ` Japin Li <japinli@hotmail.com>
2025-10-05 14:18               ` Euler Taveira <euler@eulerto.com>
2025-11-06 13:09                 ` Euler Taveira <euler@eulerto.com>
2025-11-06 14:53                   ` Chao Li <li.evan.chao@gmail.com>
2025-11-26 02:15                     ` Euler Taveira <euler@eulerto.com>
2025-11-06 16:01                   ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2025-11-18 23:38                     ` Euler Taveira <euler@eulerto.com>
2025-11-19 10:44                       ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2025-11-21 14:13                         ` Euler Taveira <euler@eulerto.com>
2025-11-26 02:11                           ` Euler Taveira <euler@eulerto.com>
2025-12-09 08:58                             ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2025-12-09 16:30                               ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2026-01-15 13:50                                 ` Euler Taveira <euler@eulerto.com>
2026-01-20 15:43                                   ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2025-12-09 18:24                               ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2025-12-10 02:00                                 ` Chao Li <li.evan.chao@gmail.com>
2025-12-11 01:57                                   ` Euler Taveira <euler@eulerto.com>
2025-12-11 02:11                                     ` Chao Li <li.evan.chao@gmail.com>
2026-01-15 16:07                                 ` Euler Taveira <euler@eulerto.com>
2026-01-16 05:54                                   ` Japin Li <japinli@hotmail.com>
2026-01-17 22:02                                   ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2026-01-22 02:12                                     ` Euler Taveira <euler@eulerto.com>
2026-02-06 11:05                                       ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2026-02-06 15:45                                         ` Euler Taveira <euler@eulerto.com>
2026-02-09 03:41                                           ` Chao Li <li.evan.chao@gmail.com>
2026-02-09 12:40                                             ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2026-02-09 12:50                                             ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2026-02-09 13:01                                               ` Chao Li <li.evan.chao@gmail.com>
2026-02-09 12:58                                           ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2026-02-09 13:03                                             ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2026-02-09 13:52                                               ` =?ISO-8859-1?B?emVuZ21hbg==?= <zengman@halodbtech.com>
2026-02-09 14:05                                                 ` Shinoda, Noriyoshi (PSD Japan FSI) <noriyoshi.shinoda@hpe.com>
2026-02-10 22:05                                               ` Euler Taveira <euler@eulerto.com>
2026-02-11 05:21                                                 ` Chao Li <li.evan.chao@gmail.com>
2026-07-20 12:15                                                   ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2026-07-20 13:55                                                     ` Fujii Masao <masao.fujii@gmail.com>
2026-07-21 09:39                                                     ` Jonathan Gonzalez V. <jonathan.abdiel@gmail.com>
2026-07-21 13:07                                                       ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2026-01-20 17:12                                   ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2026-01-21 14:51                                   ` Alvaro Herrera <alvherre@alvh.no-ip.org>
2025-08-01 05:53         ` Japin Li <japinli@hotmail.com>
2026-01-30 19:27 ` surya poondla <suryapoondla4@gmail.com>
2026-02-06 20:28   ` surya poondla <suryapoondla4@gmail.com>
2026-02-07 01:05     ` Euler Taveira <euler@eulerto.com>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox