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