<variablelist>
<varlistentry id="guc-log-min-messages" xreflabel="log_min_messages">
- <term><varname>log_min_messages</varname> (<type>enum</type>)
+ <term><varname>log_min_messages</varname> (<type>string</type>)
<indexterm>
<primary><varname>log_min_messages</varname> configuration parameter</primary>
</indexterm>
</term>
<listitem>
<para>
- Controls which <link linkend="runtime-config-severity-levels">message
- levels</link> are written to the server log.
- Valid values are <literal>DEBUG5</literal>, <literal>DEBUG4</literal>,
- <literal>DEBUG3</literal>, <literal>DEBUG2</literal>, <literal>DEBUG1</literal>,
- <literal>INFO</literal>, <literal>NOTICE</literal>, <literal>WARNING</literal>,
- <literal>ERROR</literal>, <literal>LOG</literal>, <literal>FATAL</literal>, and
- <literal>PANIC</literal>. Each level includes all the levels that
- follow it. The later the level, the fewer messages are sent
- to the log. The default is <literal>WARNING</literal>. Note that
- <literal>LOG</literal> has a different rank here than in
+ Controls which
+ <link linkend="runtime-config-severity-levels">message levels</link>
+ are written to the server log. The value is a comma-separated
+ list of zero or more
+ <literal><replaceable>process type</replaceable>:<replaceable>level</replaceable></literal>
+ entries and exactly one mandatory
+ <literal><replaceable>level</replaceable></literal> entry,
+ which becomes the default for process types not listed.
+ Valid process types are listed in the table below.
+ <simplelist type="vert" columns="4">
+ <member><literal>archiver</literal></member>
+ <member><literal>autovacuum</literal></member>
+ <member><literal>backend</literal></member>
+ <member><literal>bgworker</literal></member>
+ <member><literal>bgwriter</literal></member>
+ <member><literal>checkpointer</literal></member>
+ <member><literal>ioworker</literal></member>
+ <member><literal>postmaster</literal></member>
+ <member><literal>syslogger</literal></member>
+ <member><literal>slotsyncworker</literal></member>
+ <member><literal>startup</literal></member>
+ <member><literal>walreceiver</literal></member>
+ <member><literal>walsender</literal></member>
+ <member><literal>walsummarizer</literal></member>
+ <member><literal>walwriter</literal></member>
+ </simplelist>
+ Valid <literal>level</literal> values are <literal>DEBUG5</literal>,
+ <literal>DEBUG4</literal>, <literal>DEBUG3</literal>, <literal>DEBUG2</literal>,
+ <literal>DEBUG1</literal>, <literal>INFO</literal>, <literal>NOTICE</literal>,
+ <literal>WARNING</literal>, <literal>ERROR</literal>, <literal>LOG</literal>,
+ <literal>FATAL</literal>, and <literal>PANIC</literal>. Each level includes
+ all the levels that follow it. The later the level, the fewer messages are sent
+ to the log. The default is <literal>WARNING</literal>, which
+ applies that level to all process types.
+ Note that <literal>LOG</literal> has a different rank here than in
<xref linkend="guc-client-min-messages"/>.
Only superusers and users with the appropriate <literal>SET</literal>
privilege can change this setting.
</para>
+ <para>
+ Example: To log <literal>walsender</literal> and <literal>autovacuum</literal>
+ at level <literal>DEBUG1</literal> and everything else at <literal>ERROR</literal>,
+ set <varname>log_min_messages</varname> to <literal>error, walsender:debug1, autovacuum:debug1</literal>.
+ </para>
</listitem>
</varlistentry>
(void) set_config_option("client_min_messages", "warning",
PGC_USERSET, PGC_S_SESSION,
GUC_ACTION_SAVE, true, 0, false);
- if (log_min_messages < WARNING)
+ if (log_min_messages[MyBackendType] < WARNING)
(void) set_config_option_ext("log_min_messages", "warning",
PGC_SUSET, PGC_S_SESSION,
BOOTSTRAP_SUPERUSERID,
} child_process_kind;
static child_process_kind child_process_kinds[] = {
-#define PG_PROCTYPE(bktype, description, main_func, shmem_attach) \
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \
[bktype] = {description, main_func, shmem_attach},
#include "postmaster/proctypelist.h"
#undef PG_PROCTYPE
static pg_noinline void set_backtrace(ErrorData *edata, int num_skip);
static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str);
static void FreeErrorDataContents(ErrorData *edata);
+static int log_min_messages_cmp(const ListCell *a, const ListCell *b);
static void write_console(const char *line, int len);
static const char *process_log_prefix_padding(const char *p, int *ppadding);
static void log_line_prefix(StringInfo buf, ErrorData *edata);
static inline bool
should_output_to_server(int elevel)
{
- return is_log_level_output(elevel, log_min_messages);
+ return is_log_level_output(elevel, log_min_messages[MyBackendType]);
}
/*
}
+/*
+ * GUC check_hook for log_min_messages
+ *
+ * This value is parsed as a comma-separated list of zero or more TYPE:LEVEL
+ * elements. For each element, TYPE corresponds to a bk_category value (see
+ * postmaster/proctypelist.h); LEVEL is one of server_message_level_options.
+ *
+ * In addition, there must be a single LEVEL element (with no TYPE part)
+ * which sets the default level for process types that aren't specified.
+ */
+bool
+check_log_min_messages(char **newval, void **extra, GucSource source)
+{
+ char *rawstring;
+ List *elemlist;
+ StringInfoData buf;
+ char *result;
+ int newlevel[BACKEND_NUM_TYPES];
+ bool assigned[BACKEND_NUM_TYPES] = {0};
+ int genericlevel = -1; /* -1 means not assigned */
+
+ const char *const process_types[] = {
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \
+ [bktype] = bkcategory,
+#include "postmaster/proctypelist.h"
+#undef PG_PROCTYPE
+ };
+
+ /* Need a modifiable copy of string. */
+ rawstring = guc_strdup(LOG, *newval);
+ if (rawstring == NULL)
+ return false;
+
+ /* Parse the string into a list. */
+ if (!SplitGUCList(rawstring, ',', &elemlist))
+ {
+ /* syntax error in list */
+ 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, elem, elemlist)
+ {
+ char *sep = strchr(elem, ':');
+
+ /*
+ * If there's no ':' separator in the entry, this is the default log
+ * level. Otherwise it's a process type-specific entry.
+ */
+ if (sep == NULL)
+ {
+ const struct config_enum_entry *entry;
+ bool found;
+
+ /* Reject duplicates for generic log level. */
+ if (genericlevel != -1)
+ {
+ GUC_check_errdetail("Redundant specification of default log level.");
+ goto lmm_fail;
+ }
+
+ /* Validate the log level */
+ found = false;
+ for (entry = server_message_level_options; entry && entry->name; entry++)
+ {
+ if (pg_strcasecmp(entry->name, elem) == 0)
+ {
+ genericlevel = entry->val;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ GUC_check_errdetail("Unrecognized log level: \"%s\".", elem);
+ goto lmm_fail;
+ }
+ }
+ else
+ {
+ char *loglevel = sep + 1;
+ char *ptype = elem;
+ bool found;
+ int level;
+ const struct config_enum_entry *entry;
+
+ /*
+ * Temporarily clobber the ':' with a string terminator, so that
+ * we can validate it. We restore this at the bottom.
+ */
+ *sep = '\0';
+
+ /* Validate the log level */
+ found = false;
+ for (entry = server_message_level_options; entry && entry->name; entry++)
+ {
+ if (pg_strcasecmp(entry->name, loglevel) == 0)
+ {
+ level = entry->val;
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ GUC_check_errdetail("Unrecognized log level for process type \"%s\": \"%s\".",
+ ptype, loglevel);
+ goto lmm_fail;
+ }
+
+ /* Is the process type name valid and unique? */
+ found = false;
+ for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+ {
+ if (pg_strcasecmp(process_types[i], ptype) == 0)
+ {
+ /* Reject duplicates for a process type. */
+ if (assigned[i])
+ {
+ GUC_check_errdetail("Redundant log level specification for process type \"%s\".",
+ ptype);
+ goto lmm_fail;
+ }
+
+ newlevel[i] = level;
+ assigned[i] = true;
+ found = true;
+
+ /*
+ * note: we must keep looking! some process types appear
+ * multiple times in proctypelist.h.
+ */
+ }
+ }
+
+ if (!found)
+ {
+ GUC_check_errdetail("Unrecognized process type \"%s\".", ptype);
+ goto lmm_fail;
+ }
+
+ /* Put the separator back in place */
+ *sep = ':';
+ }
+
+ /* all good */
+ continue;
+
+lmm_fail:
+ guc_free(rawstring);
+ list_free(elemlist);
+ return false;
+ }
+
+ /*
+ * The generic log level must be specified. It is the fallback value.
+ */
+ if (genericlevel == -1)
+ {
+ GUC_check_errdetail("Default log level was not defined.");
+ guc_free(rawstring);
+ list_free(elemlist);
+ return false;
+ }
+
+ /* Apply the default log level to all processes not listed. */
+ for (int i = 0; i < BACKEND_NUM_TYPES; i++)
+ {
+ if (!assigned[i])
+ newlevel[i] = genericlevel;
+ }
+
+ /*
+ * Save an ordered representation of the user-specified string, for the
+ * show_hook.
+ */
+ list_sort(elemlist, log_min_messages_cmp);
+
+ initStringInfoExt(&buf, strlen(rawstring) + 1);
+ foreach_ptr(char, elem, elemlist)
+ {
+ if (foreach_current_index(elem) == 0)
+ appendStringInfoString(&buf, elem);
+ else
+ appendStringInfo(&buf, ", %s", elem);
+ }
+
+ 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;
+}
+
+/*
+ * list_sort() callback for check_log_min_messages. The default element
+ * goes first; the rest are ordered by strcmp() of the process type.
+ */
+static int
+log_min_messages_cmp(const ListCell *a, const ListCell *b)
+{
+ 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];
+}
+
/*
* GUC check_hook for backtrace_functions
*
switch (backendType)
{
-#define PG_PROCTYPE(bktype, description, main_func, shmem_attach) \
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \
case bktype: backendDesc = description; break;
#include "postmaster/proctypelist.h"
#undef PG_PROCTYPE
options => 'server_message_level_options',
},
-{ name => 'log_min_messages', type => 'enum', context => 'PGC_SUSET', group => 'LOGGING_WHEN',
+{ name => 'log_min_messages', type => 'string', context => 'PGC_SUSET', group => 'LOGGING_WHEN',
short_desc => 'Sets the message levels that are logged.',
long_desc => 'Each level includes all the levels that follow it. The later the level, the fewer messages are sent.',
- variable => 'log_min_messages',
- boot_val => 'WARNING',
- options => 'server_message_level_options',
+ flags => 'GUC_LIST_INPUT',
+ variable => 'log_min_messages_string',
+ boot_val => '"WARNING"',
+ check_hook => 'check_log_min_messages',
+ assign_hook => 'assign_log_min_messages',
},
{ name => 'log_parameter_max_length', type => 'int', context => 'PGC_SUSET', group => 'LOGGING_WHAT',
{NULL, 0, false}
};
-static const struct config_enum_entry server_message_level_options[] = {
+const struct config_enum_entry server_message_level_options[] = {
{"debug5", DEBUG5, false},
{"debug4", DEBUG4, false},
{"debug3", DEBUG3, 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;
static int server_version_num;
static char *debug_io_direct_string;
static char *restrict_nonsystem_relation_kind_string;
+static char *log_min_messages_string;
#ifdef HAVE_SYSLOG
#define DEFAULT_SYSLOG_FACILITY LOG_LOCAL0
/* should be static, but guc.c needs to get at this */
bool in_hot_standby_guc;
+/*
+ * set default log_min_messages to WARNING for all process types
+ */
+int log_min_messages[] = {
+#define PG_PROCTYPE(bktype, bkcategory, description, main_func, shmem_attach) \
+ [bktype] = WARNING,
+#include "postmaster/proctypelist.h"
+#undef PG_PROCTYPE
+};
/*
* Displayable names for context types (enum GucContext)
# - When to Log -
-#log_min_messages = warning # values in order of decreasing detail:
+#log_min_messages = warning # comma-separated list of
+ # process_type:level entries, plus
+ # one freestanding level as default.
+ # Valid process types are:
+ # archiver autovacuum
+ # backend bgworker
+ # bgwriter checkpointer
+ # ioworker postmaster
+ # slotsyncworker startup
+ # syslogger walreceiver
+ # walsummarizer walwriter
+ # walsender
+ #
+ # Level values in order of decreasing
+ # detail:
# debug5
# debug4
# debug3
*/
/*
- * List of process types (symbol, description, Main function, shmem_attach)
- * entries.
+ * List of process types (symbol, category, description, Main function,
+ * shmem_attach, message level) entries.
*/
-/* bktype, description, main_func, shmem_attach */
-PG_PROCTYPE(B_ARCHIVER, gettext_noop("archiver"), PgArchiverMain, true)
-PG_PROCTYPE(B_AUTOVAC_LAUNCHER, gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true)
-PG_PROCTYPE(B_AUTOVAC_WORKER, gettext_noop("autovacuum worker"), AutoVacWorkerMain, true)
-PG_PROCTYPE(B_BACKEND, gettext_noop("client backend"), BackendMain, true)
-PG_PROCTYPE(B_BG_WORKER, gettext_noop("background worker"), BackgroundWorkerMain, true)
-PG_PROCTYPE(B_BG_WRITER, gettext_noop("background writer"), BackgroundWriterMain, true)
-PG_PROCTYPE(B_CHECKPOINTER, gettext_noop("checkpointer"), CheckpointerMain, true)
-PG_PROCTYPE(B_DEAD_END_BACKEND, gettext_noop("dead-end client backend"), BackendMain, true)
-PG_PROCTYPE(B_INVALID, gettext_noop("unrecognized"), NULL, false)
-PG_PROCTYPE(B_IO_WORKER, gettext_noop("io worker"), IoWorkerMain, true)
-PG_PROCTYPE(B_LOGGER, gettext_noop("syslogger"), SysLoggerMain, false)
-PG_PROCTYPE(B_SLOTSYNC_WORKER, gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true)
-PG_PROCTYPE(B_STANDALONE_BACKEND, gettext_noop("standalone backend"), NULL, false)
-PG_PROCTYPE(B_STARTUP, gettext_noop("startup"), StartupProcessMain, true)
-PG_PROCTYPE(B_WAL_RECEIVER, gettext_noop("walreceiver"), WalReceiverMain, true)
-PG_PROCTYPE(B_WAL_SENDER, gettext_noop("walsender"), NULL, true)
-PG_PROCTYPE(B_WAL_SUMMARIZER, gettext_noop("walsummarizer"), WalSummarizerMain, true)
-PG_PROCTYPE(B_WAL_WRITER, gettext_noop("walwriter"), WalWriterMain, true)
+/* bktype, bkcategory, description, main_func, shmem_attach */
+PG_PROCTYPE(B_ARCHIVER, "archiver", gettext_noop("archiver"), PgArchiverMain, true)
+PG_PROCTYPE(B_AUTOVAC_LAUNCHER, "autovacuum", gettext_noop("autovacuum launcher"), AutoVacLauncherMain, true)
+PG_PROCTYPE(B_AUTOVAC_WORKER, "autovacuum", gettext_noop("autovacuum worker"), AutoVacWorkerMain, true)
+PG_PROCTYPE(B_BACKEND, "backend", gettext_noop("client backend"), BackendMain, true)
+PG_PROCTYPE(B_BG_WORKER, "bgworker", gettext_noop("background worker"), BackgroundWorkerMain, true)
+PG_PROCTYPE(B_BG_WRITER, "bgwriter", gettext_noop("background writer"), BackgroundWriterMain, true)
+PG_PROCTYPE(B_CHECKPOINTER, "checkpointer", gettext_noop("checkpointer"), CheckpointerMain, true)
+PG_PROCTYPE(B_DEAD_END_BACKEND, "backend", gettext_noop("dead-end client backend"), BackendMain, true)
+PG_PROCTYPE(B_INVALID, "postmaster", gettext_noop("unrecognized"), NULL, false)
+PG_PROCTYPE(B_IO_WORKER, "ioworker", gettext_noop("io worker"), IoWorkerMain, true)
+PG_PROCTYPE(B_LOGGER, "syslogger", gettext_noop("syslogger"), SysLoggerMain, false)
+PG_PROCTYPE(B_SLOTSYNC_WORKER, "slotsyncworker", gettext_noop("slotsync worker"), ReplSlotSyncWorkerMain, true)
+PG_PROCTYPE(B_STANDALONE_BACKEND, "backend", gettext_noop("standalone backend"), NULL, false)
+PG_PROCTYPE(B_STARTUP, "startup", gettext_noop("startup"), StartupProcessMain, true)
+PG_PROCTYPE(B_WAL_RECEIVER, "walreceiver", gettext_noop("walreceiver"), WalReceiverMain, true)
+PG_PROCTYPE(B_WAL_SENDER, "walsender", gettext_noop("walsender"), NULL, true)
+PG_PROCTYPE(B_WAL_SUMMARIZER, "walsummarizer", gettext_noop("walsummarizer"), WalSummarizerMain, true)
+PG_PROCTYPE(B_WAL_WRITER, "walwriter", gettext_noop("walwriter"), WalWriterMain, true)
extern PGDLLIMPORT int log_parameter_max_length;
extern PGDLLIMPORT int log_parameter_max_length_on_error;
extern PGDLLIMPORT int log_min_error_statement;
-extern PGDLLIMPORT int log_min_messages;
+extern PGDLLIMPORT int log_min_messages[];
extern PGDLLIMPORT int client_min_messages;
extern PGDLLIMPORT int log_min_duration_sample;
extern PGDLLIMPORT int log_min_duration_statement;
extern PGDLLIMPORT bool optimize_bounded_sort;
#endif
+extern PGDLLIMPORT const char *const log_min_messages_process_types[];
+
/*
* Declarations for options for enum values
*
extern PGDLLIMPORT const struct config_enum_entry dynamic_shared_memory_options[];
extern PGDLLIMPORT const struct config_enum_entry io_method_options[];
extern PGDLLIMPORT const struct config_enum_entry recovery_target_action_options[];
+extern PGDLLIMPORT const struct config_enum_entry server_message_level_options[];
extern PGDLLIMPORT const struct config_enum_entry wal_level_options[];
extern PGDLLIMPORT const struct config_enum_entry wal_sync_method_options[];
extern bool check_synchronized_standby_slots(char **newval, void **extra,
GucSource source);
extern void assign_synchronized_standby_slots(const char *newval, void *extra);
+extern bool check_log_min_messages(char **newval, void **extra, GucSource source);
+extern void assign_log_min_messages(const char *newval, void *extra);
#endif /* GUC_HOOKS_H */
reset search_path;
--
+-- Test parsing of log_min_messages
+--
+SET log_min_messages TO foo; -- fail
+ERROR: invalid value for parameter "log_min_messages": "foo"
+DETAIL: Unrecognized log level: "foo".
+SET log_min_messages TO fatal;
+SHOW log_min_messages;
+ log_min_messages
+------------------
+ fatal
+(1 row)
+
+SET log_min_messages TO 'fatal';
+SHOW log_min_messages;
+ log_min_messages
+------------------
+ fatal
+(1 row)
+
+SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; -- fail
+ERROR: invalid value for parameter "log_min_messages": "checkpointer:debug2, autovacuum:debug1"
+DETAIL: Default log level was not defined.
+SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail
+ERROR: invalid value for parameter "log_min_messages": "debug1, backend:error, fatal"
+DETAIL: Redundant specification of default log level.
+SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail
+ERROR: invalid value for parameter "log_min_messages": "backend:error, debug1, backend:warning"
+DETAIL: Redundant log level specification for process type "backend".
+SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail
+ERROR: invalid value for parameter "log_min_messages": "backend:error, foo:fatal, archiver:debug1"
+DETAIL: Unrecognized process type "foo".
+SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail
+ERROR: invalid value for parameter "log_min_messages": "backend:error, checkpointer:bar, archiver:debug1"
+DETAIL: Unrecognized log level for process type "checkpointer": "bar".
+SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3';
+SHOW log_min_messages;
+ log_min_messages
+-------------------------------------------------------------------------------------------------
+ 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)
+
+RESET log_min_messages;
+--
-- Tests for function-local GUC settings
--
set work_mem = '3MB';
select current_schemas(false);
reset search_path;
+--
+-- Test parsing of log_min_messages
+--
+
+SET log_min_messages TO foo; -- fail
+SET log_min_messages TO fatal;
+SHOW log_min_messages;
+SET log_min_messages TO 'fatal';
+SHOW log_min_messages;
+SET log_min_messages TO 'checkpointer:debug2, autovacuum:debug1'; -- fail
+SET log_min_messages TO 'debug1, backend:error, fatal'; -- fail
+SET log_min_messages TO 'backend:error, debug1, backend:warning'; -- fail
+SET log_min_messages TO 'backend:error, foo:fatal, archiver:debug1'; -- fail
+SET log_min_messages TO 'backend:error, checkpointer:bar, archiver:debug1'; -- fail
+SET log_min_messages TO 'backend:error, checkpointer:debug3, fatal, archiver:debug2, autovacuum:debug1, walsender:debug3';
+SHOW log_min_messages;
+SET log_min_messages TO 'warning, autovacuum:debug1';
+SHOW log_min_messages;
+SET log_min_messages TO 'autovacuum:debug1, warning';
+SHOW log_min_messages;
+RESET log_min_messages;
+
--
-- Tests for function-local GUC settings
--