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 */

Reply via email to