From 287accb62c25a960294d6054a1a97777a3625bdd Mon Sep 17 00:00:00 2001 From: Jelte Fennema-Nio Date: Tue, 27 Feb 2024 17:31:24 +0100 Subject: [PATCH v9 2/3] Restructure backtrace related GUCs Our GUCs that controlled backtraces were added a bit ad-hoc. This restructures them in a more complete and more easily extendable design: 1. Replaces the `backtrace_on_internal_error` GUC with a `log_backtrace` one, which can be set to `internal` and `none` to match the previous behaviour of the `on` and `off` values of `backtrace_on_internal_error`. But it can also be set to `all` 2. Make `backtrace_functions` behave as an additional filter on top of `log_backtrace`. The empty string (the default) is now interpreted as doing no filtering based on function name. 3. Add a `backtrace_min_level` GUC, which limits for which log entries backtraces are written, based on their log level. This defaults to ERROR. Being able to log backtraces for all ERROR logs was the trigger for this redesign of the backtrace GUCs. This is now possible by simply setting log_backtrace to "all". --- doc/src/sgml/config.sgml | 92 ++++++++++++++----- src/backend/utils/error/elog.c | 32 +++++-- src/backend/utils/misc/guc_tables.c | 61 +++++++++--- src/backend/utils/misc/postgresql.conf.sample | 1 + src/include/utils/elog.h | 8 ++ src/include/utils/guc.h | 2 +- src/tools/pgindent/typedefs.list | 1 + 7 files changed, 154 insertions(+), 43 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index 65a6e6c4086..b315c84ce98 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -7270,6 +7270,45 @@ local0.* /var/log/postgresql + + log_backtrace (boolean) + + log_backtrace configuration parameter + + + + + If this parameter is set to all then for all log + entries a backtrace is written to the server log together with the log + message. If this parameter is set to internal then + such a backtrace is only written for logs with error code XX000 + (internal error; see also ). + This can be used to debug such internal errors (which should normally + not happen in production). Finally, if this parameter is set to + none (the default), no backtraces are ever written + to the server log. + + + + The logs for which a backtrace is written can be further restricted + using (default: + error) and + (default: empty string, meaning all). + + + + Backtrace support is not available on all platforms, and the quality + of the backtraces depends on compilation options. + + + + Only superusers and users with the appropriate SET + privilege can change this setting. + + + + + log_checkpoints (boolean) @@ -11305,43 +11344,45 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir' - This parameter contains a comma-separated list of C function names. - If an error is raised and the name of the internal C function where - the error happens matches a value in the list, then a backtrace is - written to the server log together with the error message. This can - be used to debug specific areas of the source code. - - - - Backtrace support is not available on all platforms, and the quality - of the backtraces depends on compilation options. + This parameter can contain a comma-separated list of C function names, + which can be used to filter for which logs a backtrace is written to + the server log. + If a log entry is raised and the name of the + internal C function where the error happens does not match any of the + values in the list, then no backtrace is written to the server log. + This can be used to only debug specific areas of the source code. - Only superusers and users with the appropriate SET - privilege can change this setting. + The empty string (the default) disables any such filtering. So for any + logs that match both and + a backtrace is + written to the server log. - - backtrace_on_internal_error (boolean) + + backtrace_min_level (string) - backtrace_on_internal_error configuration parameter + backtrace_min_level configuration parameter - If this parameter is on and an error with error code XX000 (internal - error; see also ) is raised, then a - backtrace is written to the server log together with the error - message. This can be used to debug such internal errors (which should - normally not happen in production). The default is off. - - - - Backtrace support is not available on all platforms, and the quality - of the backtraces depends on compilation options. + Controls which message + levels cause backtraces to be written to the log, for log + messages that match both and + . + Valid values are DEBUG5, DEBUG4, + DEBUG3, DEBUG2, DEBUG1, + LOG, INFO, NOTICE, + WARNING, ERROR, FATAL, + and PANIC. Each level includes all the levels that + follow it. The later the level, the fewer messages result in a + backtrace. The default is ERROR. Note that + LOG has a different rank here than in + . @@ -11351,6 +11392,7 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir' + debug_discard_caches (integer) diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index 52bc01058c5..8f6a107d285 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -111,6 +111,7 @@ int Log_error_verbosity = PGERROR_DEFAULT; char *Log_line_prefix = NULL; /* format for extra log line info */ int Log_destination = LOG_DESTINATION_STDERR; char *Log_destination_string = NULL; +int log_backtrace = LOGBACKTRACE_NONE; bool syslog_sequence_numbers = true; bool syslog_split_messages = true; @@ -181,6 +182,7 @@ static void set_stack_entry_domain(ErrorData *edata, const char *domain); static void set_stack_entry_location(ErrorData *edata, const char *filename, int lineno, const char *funcname); +static bool matches_backtrace_gucs(ErrorData *edata); static bool matches_backtrace_functions(const char *funcname); static pg_noinline void set_backtrace(ErrorData *edata, int num_skip); static void set_errdata_field(MemoryContextData *cxt, char **ptr, const char *str); @@ -496,12 +498,7 @@ errfinish(const char *filename, int lineno, const char *funcname) oldcontext = MemoryContextSwitchTo(ErrorContext); /* Collect backtrace, if enabled and we didn't already */ - if (!edata->backtrace && - ((edata->funcname && - backtrace_functions && - matches_backtrace_functions(edata->funcname)) || - (edata->sqlerrcode == ERRCODE_INTERNAL_ERROR && - backtrace_on_internal_error))) + if (!edata->backtrace && matches_backtrace_gucs(edata)) set_backtrace(edata, 2); /* @@ -821,6 +818,26 @@ set_stack_entry_location(ErrorData *edata, edata->funcname = funcname; } +/* + * matches_backtrace_gucs --- checks whether the log entry matches + * log_backtrace, backtrace_min_level and backtrace_functions. + */ +static bool +matches_backtrace_gucs(ErrorData *edata) +{ + if (log_backtrace == LOGBACKTRACE_NONE) + return false; + + if (log_backtrace == LOGBACKTRACE_INTERNAL && + edata->sqlerrcode != ERRCODE_INTERNAL_ERROR) + return false; + + if (backtrace_min_level > edata->elevel) + return false; + + return matches_backtrace_functions(edata->funcname); +} + /* * matches_backtrace_functions --- checks whether the given funcname matches * backtrace_functions @@ -832,6 +849,9 @@ matches_backtrace_functions(const char *funcname) { const char *p; + if (!backtrace_functions || backtrace_functions[0] == '\0') + return true; + if (!backtrace_function_list || funcname == NULL || funcname[0] == '\0') return false; diff --git a/src/backend/utils/misc/guc_tables.c b/src/backend/utils/misc/guc_tables.c index 1e71e7db4a0..f6f4f0521e8 100644 --- a/src/backend/utils/misc/guc_tables.c +++ b/src/backend/utils/misc/guc_tables.c @@ -162,6 +162,23 @@ static const struct config_enum_entry server_message_level_options[] = { {NULL, 0, false} }; +static const struct config_enum_entry backtrace_functions_level_options[] = { + {"debug5", DEBUG5, false}, + {"debug4", DEBUG4, false}, + {"debug3", DEBUG3, false}, + {"debug2", DEBUG2, false}, + {"debug1", DEBUG1, false}, + {"debug", DEBUG2, true}, + {"log", LOG, false}, + {"info", INFO, true}, + {"notice", NOTICE, false}, + {"warning", WARNING, false}, + {"error", ERROR, false}, + {"fatal", FATAL, false}, + {"panic", PANIC, false}, + {NULL, 0, false} +}; + static const struct config_enum_entry intervalstyle_options[] = { {"postgres", INTSTYLE_POSTGRES, false}, {"postgres_verbose", INTSTYLE_POSTGRES_VERBOSE, false}, @@ -199,6 +216,16 @@ static const struct config_enum_entry log_error_verbosity_options[] = { StaticAssertDecl(lengthof(log_error_verbosity_options) == (PGERROR_VERBOSE + 2), "array length mismatch"); +static const struct config_enum_entry log_backtrace_options[] = { + {"none", LOGBACKTRACE_NONE, false}, + {"internal", LOGBACKTRACE_INTERNAL, false}, + {"all", LOGBACKTRACE_ALL, false}, + {NULL, 0, false} +}; + +StaticAssertDecl(lengthof(log_backtrace_options) == (LOGBACKTRACE_ALL + 2), + "array length mismatch"); + static const struct config_enum_entry log_statement_options[] = { {"none", LOGSTMT_NONE, false}, {"ddl", LOGSTMT_DDL, false}, @@ -529,7 +556,7 @@ int log_temp_files = -1; double log_statement_sample_rate = 1.0; double log_xact_sample_rate = 0; char *backtrace_functions; -bool backtrace_on_internal_error = false; +int backtrace_min_level = ERROR; int temp_file_limit = -1; @@ -768,16 +795,6 @@ StaticAssertDecl(lengthof(config_type_names) == (PGC_ENUM + 1), struct config_bool ConfigureNamesBool[] = { - { - {"backtrace_on_internal_error", PGC_SUSET, DEVELOPER_OPTIONS, - gettext_noop("Log backtrace for any error with error code XX000 (internal error)."), - NULL, - GUC_NOT_IN_SAMPLE - }, - &backtrace_on_internal_error, - false, - NULL, NULL, NULL - }, { {"enable_seqscan", PGC_USERSET, QUERY_TUNING_METHOD, gettext_noop("Enables the planner's use of sequential-scan plans."), @@ -4703,6 +4720,18 @@ struct config_enum ConfigureNamesEnum[] = NULL, NULL, NULL }, + { + {"backtrace_min_level", PGC_SUSET, DEVELOPER_OPTIONS, + gettext_noop("Sets the message levels that create backtraces when log_backtrace is configured."), + gettext_noop("Each level includes all the levels that follow it. The later" + " the level, the fewer backtraces are created."), + GUC_NOT_IN_SAMPLE + }, + &backtrace_min_level, + ERROR, backtrace_functions_level_options, + NULL, NULL, NULL + }, + { {"bytea_output", PGC_USERSET, CLIENT_CONN_STATEMENT, gettext_noop("Sets the output format for bytea."), @@ -4799,6 +4828,16 @@ struct config_enum ConfigureNamesEnum[] = NULL, NULL, NULL }, + { + {"log_backtrace", PGC_SUSET, LOGGING_WHAT, + gettext_noop("Sets if logs should include a backtrace."), + NULL + }, + &log_backtrace, + LOGBACKTRACE_NONE, log_backtrace_options, + NULL, NULL, NULL + }, + { {"log_error_verbosity", PGC_SUSET, LOGGING_WHAT, gettext_noop("Sets the verbosity of logged messages."), diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index 2244ee52f79..46d21df6b60 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -574,6 +574,7 @@ # their durations, > 0 logs only # actions running at least this number # of milliseconds. +#log_backtrace = 'none' #log_checkpoints = on #log_connections = off #log_disconnections = off diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h index da1a7469fa5..6370f23f27d 100644 --- a/src/include/utils/elog.h +++ b/src/include/utils/elog.h @@ -495,9 +495,17 @@ typedef enum PGERROR_VERBOSE, /* all the facts, ma'am */ } PGErrorVerbosity; +typedef enum +{ + LOGBACKTRACE_NONE, /* no backtrace */ + LOGBACKTRACE_INTERNAL, /* backtrace for internal error code */ + LOGBACKTRACE_ALL, /* backtrace for all logs */ +} LogBacktraceVerbosity; + extern PGDLLIMPORT int Log_error_verbosity; extern PGDLLIMPORT char *Log_line_prefix; extern PGDLLIMPORT int Log_destination; +extern PGDLLIMPORT int log_backtrace; extern PGDLLIMPORT char *Log_destination_string; extern PGDLLIMPORT bool syslog_sequence_numbers; extern PGDLLIMPORT bool syslog_split_messages; diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index 3712aba09b0..2398ab7d730 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -266,7 +266,7 @@ extern PGDLLIMPORT int log_temp_files; extern PGDLLIMPORT double log_statement_sample_rate; extern PGDLLIMPORT double log_xact_sample_rate; extern PGDLLIMPORT char *backtrace_functions; -extern PGDLLIMPORT bool backtrace_on_internal_error; +extern PGDLLIMPORT int backtrace_min_level; extern PGDLLIMPORT int temp_file_limit; diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index a2e6d4fdc6a..a6b1d9f11e7 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -1489,6 +1489,7 @@ LockTupleMode LockViewRecurse_context LockWaitPolicy LockingClause +LogBacktraceVerbosity LogOpts LogStmtLevel LogicalDecodeBeginCB -- 2.34.1