agora inbox for [email protected]help / color / mirror / Atom feed
[PATCH v7 1/3] log_min_messages per process type 253+ messages / 2 participants [nested] [flat]
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v7 1/3] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --grg2fqqi6tkdt5me Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v7-0002-Assign-backend-type-earlier.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v10 1/2] log_min_messages per process type @ 2025-11-04 20:18 Euler Taveira <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Euler Taveira @ 2025-11-04 20:18 UTC (permalink / raw) 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 --r3qaph7b72ke2fw4 Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v10-0002-review.patch" ^ permalink raw reply [nested|flat] 253+ messages in thread
* [PATCH v54 5/5] Reserve replication slots specifically for REPACK @ 2026-04-01 17:54 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 253+ messages in thread From: Álvaro Herrera @ 2026-04-01 17:54 UTC (permalink / raw) This allows REPACK to not interfere with other operations that use replication slots. This eases configurability. --- doc/src/sgml/config.sgml | 16 ++++ doc/src/sgml/ref/repack.sgml | 6 +- src/backend/commands/repack.c | 2 +- src/backend/commands/repack_worker.c | 7 +- src/backend/replication/logical/launcher.c | 2 +- src/backend/replication/logical/logical.c | 8 +- .../replication/logical/logicalfuncs.c | 2 +- src/backend/replication/logical/slotsync.c | 5 +- src/backend/replication/slot.c | 86 ++++++++++++------- src/backend/replication/slotfuncs.c | 19 ++-- src/backend/replication/walsender.c | 9 +- src/backend/utils/misc/guc_parameters.dat | 8 ++ src/backend/utils/misc/postgresql.conf.sample | 2 + src/include/replication/logical.h | 3 +- src/include/replication/slot.h | 5 +- 15 files changed, 117 insertions(+), 63 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index d3fea738ca3..a90d163e416 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -4638,6 +4638,22 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows </listitem> </varlistentry> + <varlistentry id="guc-max-repack-replication-slots" xreflabel="max_repack_replication_slots"> + <term><varname>max_repack_replication_slots</varname> (<type>integer</type>) + <indexterm> + <primary><varname>max_repack_replication_slots</varname> configuration parameter</primary> + </indexterm> + </term> + <listitem> + <para> + Specifies the maximum number of replication slots for use of + the <command>REPACK</command> command. The default is 5. + This parameter can only be set at server start. + </para> + </listitem> + </varlistentry> + + <varlistentry id="guc-max-replication-slots" xreflabel="max_replication_slots"> <term><varname>max_replication_slots</varname> (<type>integer</type>) <indexterm> diff --git a/doc/src/sgml/ref/repack.sgml b/doc/src/sgml/ref/repack.sgml index e993dfb3108..c532a39ee07 100644 --- a/doc/src/sgml/ref/repack.sgml +++ b/doc/src/sgml/ref/repack.sgml @@ -293,9 +293,9 @@ REPACK [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] USING <listitem> <para> - The <link linkend="guc-max-replication-slots"><varname>max_replication_slots</varname></link> - configuration parameter does not allow for creation of an additional - replication slot. + The <link linkend="guc-max-repack-replication-slots"><varname>max_repack_replication_slots</varname></link> + configuration parameter does not allow for the creation of an + additional replication slot. </para> </listitem> </itemizedlist> diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index d1be6c60e88..ec3a2cd441d 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -3382,7 +3382,7 @@ start_repack_decoding_worker(Oid relid) ereport(ERROR, errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED), errmsg("out of background worker slots"), - errhint("You might need to increase \"%s\".", "max_worker_processes")); + errhint("You might need to increase \"%s\".", "max_repack_replication_slots")); decoding_worker->seg = seg; decoding_worker->error_mqh = mqh; diff --git a/src/backend/commands/repack_worker.c b/src/backend/commands/repack_worker.c index c85166ba849..39cbb0252f4 100644 --- a/src/backend/commands/repack_worker.c +++ b/src/backend/commands/repack_worker.c @@ -223,7 +223,7 @@ repack_setup_logical_decoding(Oid relid) * Make sure we can use logical decoding. */ CheckSlotPermissions(); - CheckLogicalDecodingRequirements(); + CheckLogicalDecodingRequirements(true); /* * A single backend should not execute multiple REPACK commands at a time, @@ -232,8 +232,8 @@ repack_setup_logical_decoding(Oid relid) * RS_TEMPORARY so that the slot gets cleaned up on ERROR. */ snprintf(NameStr(slotname), NAMEDATALEN, "repack_%d", MyProcPid); - ReplicationSlotCreate(NameStr(slotname), true, RS_TEMPORARY, false, false, - false); + ReplicationSlotCreate(NameStr(slotname), true, RS_TEMPORARY, false, true, + false, false); EnsureLogicalDecodingEnabled(); @@ -244,6 +244,7 @@ repack_setup_logical_decoding(Oid relid) ctx = CreateInitDecodingContext(REPL_PLUGIN_NAME, NIL, true, + true, InvalidXLogRecPtr, XL_ROUTINE(.page_read = read_local_xlog_page, .segment_open = wal_segment_open, diff --git a/src/backend/replication/logical/launcher.c b/src/backend/replication/logical/launcher.c index 09964198550..d83125afd0d 100644 --- a/src/backend/replication/logical/launcher.c +++ b/src/backend/replication/logical/launcher.c @@ -1575,7 +1575,7 @@ CreateConflictDetectionSlot(void) errmsg("creating replication conflict detection slot")); ReplicationSlotCreate(CONFLICT_DETECTION_SLOT, false, RS_PERSISTENT, false, - false, false); + false, false, false); init_conflict_slot_xmin(); } diff --git a/src/backend/replication/logical/logical.c b/src/backend/replication/logical/logical.c index dc46a372ead..06efcaa60b2 100644 --- a/src/backend/replication/logical/logical.c +++ b/src/backend/replication/logical/logical.c @@ -108,9 +108,9 @@ static void LoadOutputPlugin(OutputPluginCallbacks *callbacks, const char *plugi * decoding. */ void -CheckLogicalDecodingRequirements(void) +CheckLogicalDecodingRequirements(bool repack) { - CheckSlotRequirements(); + CheckSlotRequirements(repack); /* * NB: Adding a new requirement likely means that RestoreSlotFromDisk() @@ -308,6 +308,7 @@ StartupDecodingContext(List *output_plugin_options, * output_plugin_options -- contains options passed to the output plugin * need_full_snapshot -- if true, must obtain a snapshot able to read all * tables; if false, one that can read only catalogs is acceptable. + * for_repack -- if true, we're going to be decoding for REPACK. * restart_lsn -- if given as invalid, it's this routine's responsibility to * mark WAL as reserved by setting a convenient restart_lsn for the slot. * Otherwise, we set for decoding to start from the given LSN without @@ -328,6 +329,7 @@ LogicalDecodingContext * CreateInitDecodingContext(const char *plugin, List *output_plugin_options, bool need_full_snapshot, + bool for_repack, XLogRecPtr restart_lsn, XLogReaderRoutine *xl_routine, LogicalOutputPluginWriterPrepareWrite prepare_write, @@ -344,7 +346,7 @@ CreateInitDecodingContext(const char *plugin, * On a standby, this check is also required while creating the slot. * Check the comments in the function. */ - CheckLogicalDecodingRequirements(); + CheckLogicalDecodingRequirements(for_repack); /* shorter lines... */ slot = MyReplicationSlot; diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c index 9760818941d..512013b0ef0 100644 --- a/src/backend/replication/logical/logicalfuncs.c +++ b/src/backend/replication/logical/logicalfuncs.c @@ -115,7 +115,7 @@ pg_logical_slot_get_changes_guts(FunctionCallInfo fcinfo, bool confirm, bool bin CheckSlotPermissions(); - CheckLogicalDecodingRequirements(); + CheckLogicalDecodingRequirements(false); if (PG_ARGISNULL(0)) ereport(ERROR, diff --git a/src/backend/replication/logical/slotsync.c b/src/backend/replication/logical/slotsync.c index e75db69e3f6..28c89c5e10d 100644 --- a/src/backend/replication/logical/slotsync.c +++ b/src/backend/replication/logical/slotsync.c @@ -425,7 +425,7 @@ get_local_synced_slots(void) LWLockAcquire(ReplicationSlotControlLock, LW_SHARED); - for (int i = 0; i < max_replication_slots; i++) + for (int i = 0; i < max_replication_slots + max_repack_replication_slots; i++) { ReplicationSlot *s = &ReplicationSlotCtl->replication_slots[i]; @@ -814,6 +814,7 @@ synchronize_one_slot(RemoteSlot *remote_slot, Oid remote_dbid, */ ReplicationSlotCreate(remote_slot->name, true, RS_TEMPORARY, remote_slot->two_phase, + false, remote_slot->failover, true); @@ -1691,7 +1692,7 @@ update_synced_slots_inactive_since(void) LWLockAcquire(ReplicationSlotControlLock, LW_SHARED); - for (int i = 0; i < max_replication_slots; i++) + for (int i = 0; i < max_replication_slots + max_repack_replication_slots; i++) { ReplicationSlot *s = &ReplicationSlotCtl->replication_slots[i]; diff --git a/src/backend/replication/slot.c b/src/backend/replication/slot.c index 9533515a63b..47679161b67 100644 --- a/src/backend/replication/slot.c +++ b/src/backend/replication/slot.c @@ -151,6 +151,8 @@ ReplicationSlot *MyReplicationSlot = NULL; /* GUC variables */ int max_replication_slots = 10; /* the maximum number of replication * slots */ +int max_repack_replication_slots = 5; /* the maximum number of slots + * for REPACK */ /* * Invalidate replication slots that have remained idle longer than this @@ -188,14 +190,15 @@ static void SaveSlotToPath(ReplicationSlot *slot, const char *dir, int elevel); Size ReplicationSlotsShmemSize(void) { + int totalslots = max_replication_slots + max_repack_replication_slots; Size size = 0; - if (max_replication_slots == 0) + if (totalslots == 0) return size; size = offsetof(ReplicationSlotCtlData, replication_slots); size = add_size(size, - mul_size(max_replication_slots, sizeof(ReplicationSlot))); + mul_size(totalslots, sizeof(ReplicationSlot))); return size; } @@ -208,7 +211,7 @@ ReplicationSlotsShmemInit(void) { bool found; - if (max_replication_slots == 0) + if (max_replication_slots + max_repack_replication_slots == 0) return; ReplicationSlotCtl = (ReplicationSlotCtlData *) @@ -222,7 +225,7 @@ ReplicationSlotsShmemInit(void) /* First time through, so initialize */ MemSet(ReplicationSlotCtl, 0, ReplicationSlotsShmemSize()); - for (i = 0; i < max_replication_slots; i++) + for (i = 0; i < max_replication_slots + max_repack_replication_slots; i++) { ReplicationSlot *slot = &ReplicationSlotCtl->replication_slots[i]; @@ -372,6 +375,7 @@ IsSlotForConflictCheck(const char *name) * db_specific: logical decoding is db specific; if the slot is going to * be used for that pass true, otherwise false. * two_phase: If enabled, allows decoding of prepared transactions. + * repack: If true, use a slot from the pool for REPACK. * failover: If enabled, allows the slot to be synced to standbys so * that logical replication can be resumed after failover. * synced: True if the slot is synchronized from the primary server. @@ -379,10 +383,11 @@ IsSlotForConflictCheck(const char *name) void ReplicationSlotCreate(const char *name, bool db_specific, ReplicationSlotPersistency persistency, - bool two_phase, bool failover, bool synced) + bool two_phase, bool repack, bool failover, bool synced) { ReplicationSlot *slot = NULL; - int i; + int startpoint, + endpoint; Assert(MyReplicationSlot == NULL); @@ -431,12 +436,16 @@ ReplicationSlotCreate(const char *name, bool db_specific, LWLockAcquire(ReplicationSlotAllocationLock, LW_EXCLUSIVE); /* - * Check for name collision, and identify an allocatable slot. We need to - * hold ReplicationSlotControlLock in shared mode for this, so that nobody - * else can change the in_use flags while we're looking at them. + * Check for name collision (across the whole array), and identify an + * allocatable slot (in the array slice specific to our current use case: + * either general, or REPACK only). We need to hold + * ReplicationSlotControlLock in shared mode for this, so that nobody else + * can change the in_use flags while we're looking at them. */ LWLockAcquire(ReplicationSlotControlLock, LW_SHARED); - for (i = 0; i < max_replication_slots; i++) + startpoint = !repack ? 0 : max_replication_slots; + endpoint = max_replication_slots + (repack ? max_repack_replication_slots : 0); + for (int i = 0; i < max_replication_slots + max_repack_replication_slots; i++) { ReplicationSlot *s = &ReplicationSlotCtl->replication_slots[i]; @@ -444,7 +453,9 @@ ReplicationSlotCreate(const char *name, bool db_specific, ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("replication slot \"%s\" already exists", name))); - if (!s->in_use && slot == NULL) + + if (i >= startpoint && i < endpoint && + !s->in_use && slot == NULL) slot = s; } LWLockRelease(ReplicationSlotControlLock); @@ -454,7 +465,8 @@ ReplicationSlotCreate(const char *name, bool db_specific, ereport(ERROR, (errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED), errmsg("all replication slots are in use"), - errhint("Free one or increase \"max_replication_slots\"."))); + errhint("Free one or increase \"%s\".", + repack ? "max_repack_replication_slots" : "max_replication_slots"))); /* * Since this slot is not in use, nobody should be looking at any part of @@ -547,7 +559,7 @@ SearchNamedReplicationSlot(const char *name, bool need_lock) if (need_lock) LWLockAcquire(ReplicationSlotControlLock, LW_SHARED); - for (i = 0; i < max_replication_slots; i++) + for (i = 0; i < max_replication_slots + max_repack_replication_slots; i++) { ReplicationSlot *s = &ReplicationSlotCtl->replication_slots[i]; @@ -575,7 +587,8 @@ int ReplicationSlotIndex(ReplicationSlot *slot) { Assert(slot >= ReplicationSlotCtl->replication_slots && - slot < ReplicationSlotCtl->replication_slots + max_replication_slots); + slot < ReplicationSlotCtl->replication_slots + + (max_replication_slots + max_repack_replication_slots)); return slot - ReplicationSlotCtl->replication_slots; } @@ -869,7 +882,7 @@ ReplicationSlotCleanup(bool synced_only) restart: found_valid_logicalslot = false; LWLockAcquire(ReplicationSlotControlLock, LW_SHARED); - for (i = 0; i < max_replication_slots; i++) + for (i = 0; i < max_replication_slots + max_repack_replication_slots; i++) { ReplicationSlot *s = &ReplicationSlotCtl->replication_slots[i]; @@ -1251,7 +1264,7 @@ ReplicationSlotsComputeRequiredXmin(bool already_locked) if (!already_locked) LWLockAcquire(ReplicationSlotControlLock, LW_SHARED); - for (i = 0; i < max_replication_slots; i++) + for (i = 0; i < max_replication_slots + max_repack_replication_slots; i++) { ReplicationSlot *s = &ReplicationSlotCtl->replication_slots[i]; TransactionId effective_xmin; @@ -1306,7 +1319,7 @@ ReplicationSlotsComputeRequiredLSN(void) Assert(ReplicationSlotCtl != NULL); LWLockAcquire(ReplicationSlotControlLock, LW_SHARED); - for (i = 0; i < max_replication_slots; i++) + for (i = 0; i < max_replication_slots + max_repack_replication_slots; i++) { ReplicationSlot *s = &ReplicationSlotCtl->replication_slots[i]; XLogRecPtr restart_lsn; @@ -1373,12 +1386,12 @@ ReplicationSlotsComputeLogicalRestartLSN(void) XLogRecPtr result = InvalidXLogRecPtr; int i; - if (max_replication_slots <= 0) + if (max_replication_slots + max_repack_replication_slots <= 0) return InvalidXLogRecPtr; LWLockAcquire(ReplicationSlotControlLock, LW_SHARED); - for (i = 0; i < max_replication_slots; i++) + for (i = 0; i < max_replication_slots + max_repack_replication_slots; i++) { ReplicationSlot *s; XLogRecPtr restart_lsn; @@ -1453,11 +1466,11 @@ ReplicationSlotsCountDBSlots(Oid dboid, int *nslots, int *nactive) *nslots = *nactive = 0; - if (max_replication_slots <= 0) + if (max_replication_slots + max_repack_replication_slots <= 0) return false; LWLockAcquire(ReplicationSlotControlLock, LW_SHARED); - for (i = 0; i < max_replication_slots; i++) + for (i = 0; i < max_replication_slots + max_repack_replication_slots; i++) { ReplicationSlot *s; @@ -1514,13 +1527,13 @@ ReplicationSlotsDropDBSlots(Oid dboid) bool found_valid_logicalslot; bool dropped = false; - if (max_replication_slots <= 0) + if (max_replication_slots + max_repack_replication_slots <= 0) return; restart: found_valid_logicalslot = false; LWLockAcquire(ReplicationSlotControlLock, LW_SHARED); - for (i = 0; i < max_replication_slots; i++) + for (i = 0; i < max_replication_slots + max_repack_replication_slots; i++) { ReplicationSlot *s; char *slotname; @@ -1617,11 +1630,11 @@ CheckLogicalSlotExists(void) { bool found = false; - if (max_replication_slots <= 0) + if (max_replication_slots + max_repack_replication_slots <= 0) return false; LWLockAcquire(ReplicationSlotControlLock, LW_SHARED); - for (int i = 0; i < max_replication_slots; i++) + for (int i = 0; i < max_replication_slots + max_repack_replication_slots; i++) { ReplicationSlot *s; bool invalidated; @@ -1655,17 +1668,24 @@ CheckLogicalSlotExists(void) * slots. */ void -CheckSlotRequirements(void) +CheckSlotRequirements(bool repack) { /* * NB: Adding a new requirement likely means that RestoreSlotFromDisk() * needs the same check. */ - if (max_replication_slots == 0) + if (!repack && max_replication_slots == 0) ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("replication slots can only be used if \"max_replication_slots\" > 0"))); + errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("replication slots can only be used if \"%s\" > 0", + "max_replication_slots")); + + if (repack && max_repack_replication_slots == 0) + ereport(ERROR, + errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("REPACK can only be used if \"%s\" > 0", + "max_repack_replication_slots")); if (wal_level < WAL_LEVEL_REPLICA) ereport(ERROR, @@ -2216,7 +2236,7 @@ InvalidateObsoleteReplicationSlots(uint32 possible_causes, Assert(!(possible_causes & RS_INVAL_WAL_REMOVED) || oldestSegno > 0); Assert(possible_causes != RS_INVAL_NONE); - if (max_replication_slots == 0) + if (max_replication_slots == 0 && max_repack_replication_slots == 0) return invalidated; XLogSegNoOffsetToRecPtr(oldestSegno, 0, wal_segment_size, oldestLSN); @@ -2224,7 +2244,7 @@ InvalidateObsoleteReplicationSlots(uint32 possible_causes, restart: found_valid_logicalslot = false; LWLockAcquire(ReplicationSlotControlLock, LW_SHARED); - for (int i = 0; i < max_replication_slots; i++) + for (int i = 0; i < max_replication_slots + max_repack_replication_slots; i++) { ReplicationSlot *s = &ReplicationSlotCtl->replication_slots[i]; bool released_lock = false; @@ -2329,7 +2349,7 @@ CheckPointReplicationSlots(bool is_shutdown) */ LWLockAcquire(ReplicationSlotAllocationLock, LW_SHARED); - for (i = 0; i < max_replication_slots; i++) + for (i = 0; i < max_replication_slots + max_repack_replication_slots; i++) { ReplicationSlot *s = &ReplicationSlotCtl->replication_slots[i]; char path[MAXPGPATH]; @@ -2430,7 +2450,7 @@ StartupReplicationSlots(void) FreeDir(replication_dir); /* currently no slots exist, we're done. */ - if (max_replication_slots <= 0) + if (max_replication_slots + max_repack_replication_slots <= 0) return; /* Now that we have recovered all the data, compute replication xmin */ diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c index 9f5e4f998fe..16fbd383735 100644 --- a/src/backend/replication/slotfuncs.c +++ b/src/backend/replication/slotfuncs.c @@ -53,7 +53,7 @@ create_physical_replication_slot(char *name, bool immediately_reserve, /* acquire replication slot, this will check for conflicting names */ ReplicationSlotCreate(name, false, temporary ? RS_TEMPORARY : RS_PERSISTENT, false, - false, false); + false, false, false); if (immediately_reserve) { @@ -90,7 +90,7 @@ pg_create_physical_replication_slot(PG_FUNCTION_ARGS) CheckSlotPermissions(); - CheckSlotRequirements(); + CheckSlotRequirements(false); create_physical_replication_slot(NameStr(*name), immediately_reserve, @@ -146,7 +146,7 @@ create_logical_replication_slot(char *name, char *plugin, */ ReplicationSlotCreate(name, true, temporary ? RS_TEMPORARY : RS_EPHEMERAL, two_phase, - failover, false); + false, failover, false); /* * Ensure the logical decoding is enabled before initializing the logical @@ -164,6 +164,7 @@ create_logical_replication_slot(char *name, char *plugin, */ ctx = CreateInitDecodingContext(plugin, NIL, false, /* just catalogs is OK */ + false, /* not repack */ restart_lsn, XL_ROUTINE(.page_read = read_local_xlog_page, .segment_open = wal_segment_open, @@ -203,7 +204,7 @@ pg_create_logical_replication_slot(PG_FUNCTION_ARGS) CheckSlotPermissions(); - CheckLogicalDecodingRequirements(); + CheckLogicalDecodingRequirements(false); create_logical_replication_slot(NameStr(*name), NameStr(*plugin), @@ -240,7 +241,7 @@ pg_drop_replication_slot(PG_FUNCTION_ARGS) CheckSlotPermissions(); - CheckSlotRequirements(); + CheckSlotRequirements(false); ReplicationSlotDrop(NameStr(*name), true); @@ -270,7 +271,7 @@ pg_get_replication_slots(PG_FUNCTION_ARGS) currlsn = GetXLogWriteRecPtr(); LWLockAcquire(ReplicationSlotControlLock, LW_SHARED); - for (slotno = 0; slotno < max_replication_slots; slotno++) + for (slotno = 0; slotno < max_replication_slots + max_repack_replication_slots; slotno++) { ReplicationSlot *slot = &ReplicationSlotCtl->replication_slots[slotno]; ReplicationSlot slot_contents; @@ -648,9 +649,9 @@ copy_replication_slot(FunctionCallInfo fcinfo, bool logical_slot) CheckSlotPermissions(); if (logical_slot) - CheckLogicalDecodingRequirements(); + CheckLogicalDecodingRequirements(false); else - CheckSlotRequirements(); + CheckSlotRequirements(false); LWLockAcquire(ReplicationSlotControlLock, LW_SHARED); @@ -665,7 +666,7 @@ copy_replication_slot(FunctionCallInfo fcinfo, bool logical_slot) * managed to create the new slot, we advance the new slot's restart_lsn * to the source slot's updated restart_lsn the second time we lock it. */ - for (int i = 0; i < max_replication_slots; i++) + for (int i = 0; i < max_replication_slots + max_repack_replication_slots; i++) { ReplicationSlot *s = &ReplicationSlotCtl->replication_slots[i]; diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c index 2bb3f34dc6d..9d7d675fa96 100644 --- a/src/backend/replication/walsender.c +++ b/src/backend/replication/walsender.c @@ -1220,7 +1220,7 @@ CreateReplicationSlot(CreateReplicationSlotCmd *cmd) { ReplicationSlotCreate(cmd->slotname, false, cmd->temporary ? RS_TEMPORARY : RS_PERSISTENT, - false, false, false); + false, false, false, false); if (reserve_wal) { @@ -1240,7 +1240,7 @@ CreateReplicationSlot(CreateReplicationSlotCmd *cmd) Assert(cmd->kind == REPLICATION_KIND_LOGICAL); - CheckLogicalDecodingRequirements(); + CheckLogicalDecodingRequirements(false); /* * Initially create persistent slot as ephemeral - that allows us to @@ -1251,7 +1251,7 @@ CreateReplicationSlot(CreateReplicationSlotCmd *cmd) */ ReplicationSlotCreate(cmd->slotname, true, cmd->temporary ? RS_TEMPORARY : RS_EPHEMERAL, - two_phase, failover, false); + two_phase, false, failover, false); /* * Do options check early so that we can bail before calling the @@ -1309,6 +1309,7 @@ CreateReplicationSlot(CreateReplicationSlotCmd *cmd) Assert(IsLogicalDecodingEnabled()); ctx = CreateInitDecodingContext(cmd->plugin, NIL, need_full_snapshot, + false, InvalidXLogRecPtr, XL_ROUTINE(.page_read = logical_read_xlog_page, .segment_open = WalSndSegmentOpen, @@ -1466,7 +1467,7 @@ StartLogicalReplication(StartReplicationCmd *cmd) QueryCompletion qc; /* make sure that our requirements are still fulfilled */ - CheckLogicalDecodingRequirements(); + CheckLogicalDecodingRequirements(false); Assert(!MyReplicationSlot); diff --git a/src/backend/utils/misc/guc_parameters.dat b/src/backend/utils/misc/guc_parameters.dat index a315c4ab8ab..bf82b8f6048 100644 --- a/src/backend/utils/misc/guc_parameters.dat +++ b/src/backend/utils/misc/guc_parameters.dat @@ -2071,6 +2071,14 @@ max => 'MAX_BACKENDS', }, +{ name => 'max_repack_replication_slots', type => 'int', context => 'PGC_POSTMASTER', group => 'REPLICATION_SENDING', + short_desc => 'Sets the maximum number of replication slots for use by REPACK.', + variable => 'max_repack_replication_slots', + boot_val => '5', + min => '0', + max => 'MAX_BACKENDS', +}, + /* see max_wal_senders */ { name => 'max_replication_slots', type => 'int', context => 'PGC_POSTMASTER', group => 'REPLICATION_SENDING', short_desc => 'Sets the maximum number of simultaneously defined replication slots.', diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index 6d0337853e0..fb75990609e 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -348,6 +348,8 @@ # (change requires restart) #max_replication_slots = 10 # max number of replication slots # (change requires restart) +#max_repack_replication_slots = 5 # max number of replication slots for REPACK + # (change requires restart) #wal_keep_size = 0 # in megabytes; 0 disables #max_slot_wal_keep_size = -1 # in megabytes; -1 disables #idle_replication_slot_timeout = 0 # in seconds; 0 disables diff --git a/src/include/replication/logical.h b/src/include/replication/logical.h index bc9d4ece672..bc075b16741 100644 --- a/src/include/replication/logical.h +++ b/src/include/replication/logical.h @@ -115,11 +115,12 @@ typedef struct LogicalDecodingContext } LogicalDecodingContext; -extern void CheckLogicalDecodingRequirements(void); +extern void CheckLogicalDecodingRequirements(bool repack); extern LogicalDecodingContext *CreateInitDecodingContext(const char *plugin, List *output_plugin_options, bool need_full_snapshot, + bool for_repack, XLogRecPtr restart_lsn, XLogReaderRoutine *xl_routine, LogicalOutputPluginWriterPrepareWrite prepare_write, diff --git a/src/include/replication/slot.h b/src/include/replication/slot.h index 4b4709f6e2c..489af7d8d6c 100644 --- a/src/include/replication/slot.h +++ b/src/include/replication/slot.h @@ -324,6 +324,7 @@ extern PGDLLIMPORT ReplicationSlot *MyReplicationSlot; /* GUCs */ extern PGDLLIMPORT int max_replication_slots; +extern PGDLLIMPORT int max_repack_replication_slots; extern PGDLLIMPORT char *synchronized_standby_slots; extern PGDLLIMPORT int idle_replication_slot_timeout_secs; @@ -334,7 +335,7 @@ extern void ReplicationSlotsShmemInit(void); /* management of individual slots */ extern void ReplicationSlotCreate(const char *name, bool db_specific, ReplicationSlotPersistency persistency, - bool two_phase, bool failover, + bool two_phase, bool repack, bool failover, bool synced); extern void ReplicationSlotPersist(void); extern void ReplicationSlotDrop(const char *name, bool nowait); @@ -377,7 +378,7 @@ extern void ReplicationSlotDropAtPubNode(WalReceiverConn *wrconn, char *slotname extern void StartupReplicationSlots(void); extern void CheckPointReplicationSlots(bool is_shutdown); -extern void CheckSlotRequirements(void); +extern void CheckSlotRequirements(bool repack); extern void CheckSlotPermissions(void); extern ReplicationSlotInvalidationCause GetSlotInvalidationCause(const char *cause_name); -- 2.47.3 --cdhh4t7ukb6tnjoq-- ^ permalink raw reply [nested|flat] 253+ messages in thread
end of thread, other threads:[~2026-04-01 17:54 UTC | newest] Thread overview: 253+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v7 1/3] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2025-11-04 20:18 [PATCH v10 1/2] log_min_messages per process type Euler Taveira <[email protected]> 2026-04-01 17:54 [PATCH v54 5/5] Reserve replication slots specifically for REPACK Álvaro Herrera <[email protected]>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox