I wrote:
> Hmm, interesting. Taking up my point #2, I'd been thinking about
> proposing that we convert
> pg_log_error("query failed: %s", PQerrorMessage(conn));
> pg_log_error("query was: %s", todo);
> to
> pg_log_error("query failed: %s", PQerrorMessage(conn));
> pg_log_error_detail("Query was: %s", todo);
After looking around a bit, I see that a lot of these add-on messages
are more nearly hints than details, so we'd probably better support
both those cases right off the bat.
To move things along a bit, here's a draft patch to logging.h/.c only
to implement what I'm envisioning. I don't think there's much point
in doing the per-call-site gruntwork until we have agreement on what
the API is, so this seems like enough for discussion.
(As a fervent hater of colorization, I don't have an opinion about
whether or how to colorize the "detail:" and "hint:" fragments.
But I'll happily take somebody else's adjustment to add that.)
regards, tom lane
diff --git a/src/common/logging.c b/src/common/logging.c
index fb1a9b1f87..84a4cdfd1f 100644
--- a/src/common/logging.c
+++ b/src/common/logging.c
@@ -151,6 +151,9 @@ pg_logging_init(const char *argv0)
}
}
+/*
+ * Change the logging flags.
+ */
void
pg_logging_config(int new_flags)
{
@@ -194,17 +197,19 @@ pg_logging_set_locus_callback(void (*cb) (const char **filename, uint64 *lineno)
}
void
-pg_log_generic(enum pg_log_level level, const char *pg_restrict fmt,...)
+pg_log_generic(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt,...)
{
va_list ap;
va_start(ap, fmt);
- pg_log_generic_v(level, fmt, ap);
+ pg_log_generic_v(level, part, fmt, ap);
va_end(ap);
}
void
-pg_log_generic_v(enum pg_log_level level, const char *pg_restrict fmt, va_list ap)
+pg_log_generic_v(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt, va_list ap)
{
int save_errno = errno;
const char *filename = NULL;
@@ -232,7 +237,8 @@ pg_log_generic_v(enum pg_log_level level, const char *pg_restrict fmt, va_list a
fmt = _(fmt);
- if (!(log_flags & PG_LOG_FLAG_TERSE) || filename)
+ if (part == PG_LOG_PRIMARY &&
+ (!(log_flags & PG_LOG_FLAG_TERSE) || filename))
{
if (sgr_locus)
fprintf(stderr, ANSI_ESCAPE_FMT, sgr_locus);
@@ -251,30 +257,34 @@ pg_log_generic_v(enum pg_log_level level, const char *pg_restrict fmt, va_list a
if (!(log_flags & PG_LOG_FLAG_TERSE))
{
- switch (level)
+ switch (part)
{
- case PG_LOG_FATAL:
- if (sgr_error)
- fprintf(stderr, ANSI_ESCAPE_FMT, sgr_error);
- fprintf(stderr, _("fatal: "));
- if (sgr_error)
- fprintf(stderr, ANSI_ESCAPE_RESET);
- break;
- case PG_LOG_ERROR:
- if (sgr_error)
- fprintf(stderr, ANSI_ESCAPE_FMT, sgr_error);
- fprintf(stderr, _("error: "));
- if (sgr_error)
- fprintf(stderr, ANSI_ESCAPE_RESET);
+ case PG_LOG_PRIMARY:
+ switch (level)
+ {
+ case PG_LOG_ERROR:
+ if (sgr_error)
+ fprintf(stderr, ANSI_ESCAPE_FMT, sgr_error);
+ fprintf(stderr, _("error: "));
+ if (sgr_error)
+ fprintf(stderr, ANSI_ESCAPE_RESET);
+ break;
+ case PG_LOG_WARNING:
+ if (sgr_warning)
+ fprintf(stderr, ANSI_ESCAPE_FMT, sgr_warning);
+ fprintf(stderr, _("warning: "));
+ if (sgr_warning)
+ fprintf(stderr, ANSI_ESCAPE_RESET);
+ break;
+ default:
+ break;
+ }
break;
- case PG_LOG_WARNING:
- if (sgr_warning)
- fprintf(stderr, ANSI_ESCAPE_FMT, sgr_warning);
- fprintf(stderr, _("warning: "));
- if (sgr_warning)
- fprintf(stderr, ANSI_ESCAPE_RESET);
+ case PG_LOG_DETAIL:
+ fprintf(stderr, _("detail: "));
break;
- default:
+ case PG_LOG_HINT:
+ fprintf(stderr, _("hint: "));
break;
}
}
diff --git a/src/include/common/logging.h b/src/include/common/logging.h
index a71cf84249..ec589ccd03 100644
--- a/src/include/common/logging.h
+++ b/src/include/common/logging.h
@@ -16,7 +16,7 @@
enum pg_log_level
{
/*
- * Not initialized yet
+ * Not initialized yet (not to be used as an actual message log level).
*/
PG_LOG_NOTSET = 0,
@@ -43,20 +43,42 @@ enum pg_log_level
PG_LOG_ERROR,
/*
- * Severe errors that cause program termination. (One-shot programs may
- * chose to label even fatal errors as merely "errors". The distinction
- * is up to the program.)
- */
- PG_LOG_FATAL,
-
- /*
- * Turn all logging off.
+ * Turn all logging off (not to be used as an actual message log level).
*/
PG_LOG_OFF,
};
+/*
+ * __pg_log_level is the minimum log level that will actually be shown.
+ */
extern enum pg_log_level __pg_log_level;
+/*
+ * A log message can have several parts. The primary message is required,
+ * others are optional. When emitting multiple parts, do so in the order of
+ * this enum, for consistency.
+ */
+enum pg_log_part
+{
+ /*
+ * The primary message. Try to keep it to one line; follow the backend's
+ * style guideline for primary messages.
+ */
+ PG_LOG_PRIMARY,
+
+ /*
+ * Additional detail. Follow the backend's style guideline for detail
+ * messages.
+ */
+ PG_LOG_DETAIL,
+
+ /*
+ * Hint (not guaranteed correct) about how to fix the problem. Follow the
+ * backend's style guideline for hint messages.
+ */
+ PG_LOG_HINT,
+};
+
/*
* Kind of a hack to be able to produce the psql output exactly as required by
* the regression tests.
@@ -70,27 +92,85 @@ void pg_logging_increase_verbosity(void);
void pg_logging_set_pre_callback(void (*cb) (void));
void pg_logging_set_locus_callback(void (*cb) (const char **filename, uint64 *lineno));
-void pg_log_generic(enum pg_log_level level, const char *pg_restrict fmt,...) pg_attribute_printf(2, 3);
-void pg_log_generic_v(enum pg_log_level level, const char *pg_restrict fmt, va_list ap) pg_attribute_printf(2, 0);
+void pg_log_generic(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt,...)
+ pg_attribute_printf(3, 4);
+void pg_log_generic_v(enum pg_log_level level, enum pg_log_part part,
+ const char *pg_restrict fmt, va_list ap)
+ pg_attribute_printf(3, 0);
-#define pg_log_fatal(...) do { \
- if (likely(__pg_log_level <= PG_LOG_FATAL)) pg_log_generic(PG_LOG_FATAL, __VA_ARGS__); \
+/*
+ * Preferred style is to use these macros to perform logging; don't call
+ * pg_log_generic[_v] directly, except perhaps in error interface code.
+ */
+#define pg_log_error(...) do { \
+ if (likely(__pg_log_level <= PG_LOG_ERROR)) \
+ pg_log_generic(PG_LOG_ERROR, PG_LOG_PRIMARY, __VA_ARGS__); \
} while(0)
-#define pg_log_error(...) do { \
- if (likely(__pg_log_level <= PG_LOG_ERROR)) pg_log_generic(PG_LOG_ERROR, __VA_ARGS__); \
+#define pg_log_error_detail(...) do { \
+ if (likely(__pg_log_level <= PG_LOG_ERROR)) \
+ pg_log_generic(PG_LOG_ERROR, PG_LOG_DETAIL, __VA_ARGS__); \
+ } while(0)
+
+#define pg_log_error_hint(...) do { \
+ if (likely(__pg_log_level <= PG_LOG_ERROR)) \
+ pg_log_generic(PG_LOG_ERROR, PG_LOG_HINT, __VA_ARGS__); \
} while(0)
#define pg_log_warning(...) do { \
- if (likely(__pg_log_level <= PG_LOG_WARNING)) pg_log_generic(PG_LOG_WARNING, __VA_ARGS__); \
+ if (likely(__pg_log_level <= PG_LOG_WARNING)) \
+ pg_log_generic(PG_LOG_WARNING, PG_LOG_PRIMARY, __VA_ARGS__); \
+ } while(0)
+
+#define pg_log_warning_detail(...) do { \
+ if (likely(__pg_log_level <= PG_LOG_WARNING)) \
+ pg_log_generic(PG_LOG_WARNING, PG_LOG_DETAIL, __VA_ARGS__); \
+ } while(0)
+
+#define pg_log_warning_hint(...) do { \
+ if (likely(__pg_log_level <= PG_LOG_WARNING)) \
+ pg_log_generic(PG_LOG_WARNING, PG_LOG_HINT, __VA_ARGS__); \
} while(0)
#define pg_log_info(...) do { \
- if (likely(__pg_log_level <= PG_LOG_INFO)) pg_log_generic(PG_LOG_INFO, __VA_ARGS__); \
+ if (likely(__pg_log_level <= PG_LOG_INFO)) \
+ pg_log_generic(PG_LOG_INFO, PG_LOG_PRIMARY, __VA_ARGS__); \
+ } while(0)
+
+#define pg_log_info_detail(...) do { \
+ if (likely(__pg_log_level <= PG_LOG_INFO)) \
+ pg_log_generic(PG_LOG_INFO, PG_LOG_DETAIL, __VA_ARGS__); \
+ } while(0)
+
+#define pg_log_info_hint(...) do { \
+ if (likely(__pg_log_level <= PG_LOG_INFO)) \
+ pg_log_generic(PG_LOG_INFO, PG_LOG_HINT, __VA_ARGS__); \
} while(0)
#define pg_log_debug(...) do { \
- if (unlikely(__pg_log_level <= PG_LOG_DEBUG)) pg_log_generic(PG_LOG_DEBUG, __VA_ARGS__); \
+ if (unlikely(__pg_log_level <= PG_LOG_DEBUG)) \
+ pg_log_generic(PG_LOG_DEBUG, PG_LOG_PRIMARY, __VA_ARGS__); \
+ } while(0)
+
+#define pg_log_debug_detail(...) do { \
+ if (unlikely(__pg_log_level <= PG_LOG_DEBUG)) \
+ pg_log_generic(PG_LOG_DEBUG, PG_LOG_DETAIL, __VA_ARGS__); \
+ } while(0)
+
+#define pg_log_debug_hint(...) do { \
+ if (unlikely(__pg_log_level <= PG_LOG_DEBUG)) \
+ pg_log_generic(PG_LOG_DEBUG, PG_LOG_HINT, __VA_ARGS__); \
+ } while(0)
+
+/*
+ * A common special case: pg_log_error() and immediately exit(1). There is
+ * no situation where it makes sense to suppress the message, so we ignore
+ * __pg_log_level.
+ */
+#define pg_log_fatal(...) do { \
+ pg_log_generic(PG_LOG_ERROR, PG_LOG_PRIMARY, __VA_ARGS__); \
+ exit(1); \
} while(0)
#endif /* COMMON_LOGGING_H */