Subject: Re: Frontend error logging style

From: Peter Eisentraut <[email-masked]>
Date: Thu, 24 Feb 2022 14:06:18 +0100

On 23.02.22 00:23, Tom Lane wrote:
> This conversation seems to have tailed off without full resolution,
> but I observe that pretty much everyone except Peter is on board
> with defining pg_log_fatal as pg_log_error + exit(1).  So I think
> we should just do that, unless Peter wants to produce a finished
> alternative proposal.
> 
> I've now gone through and fleshed out the patch I sketched upthread.

This patch already illustrates a couple of things that are wrong with 
this approach:

- It doesn't allow any other way of exiting.  For example, in pg_dump, 
you have removed a few exit_nicely() calls.  It's not clear why that is 
valid or whether it would always be valid for all call sites.

- It doesn't allow other exit codes.  For example, in psql, you have 
changed a pg_log_fatal() call to pg_log_error() because it needed 
another exit code.  This slides us right back into that annoying 
situation where in the backend we have to log error messages using 
elog(LOG) because the flow control is tangled up with the log level.

My suggestion is to just get rid of pg_log_fatal() and replace them all 
with pg_log_error().

---

From: Tom Lane <[email-masked]>
Date: Thu, 24 Feb 2022 10:10:41 -0500

> This patch already illustrates a couple of things that are wrong with 

All of these objections are a bit moot with my followup proposal, no?

> valid or whether it would always be valid for all call sites.

As the patch stood, I'd hacked it so that pg_log_fatal called
exit_nicely() not exit() within pg_dump.  I agree that's not a great
solution, but I think that the correct fix is to get rid of exit_nicely
in favor of doing the requisite cleanup in an atexit hook.  pg_dump
is already at great hazard of somebody calling exit() not exit_nicely(),
either through failure to pay attention to that undocumented convention,
or because some code it imports from someplace like src/common calls
exit() directly.  You need not look any further than pg_malloc()
to see that there are live problems there.

I figured this could be addressed in a separate patch, though,
because to the extent that missing the exit-nicely callbacks is
a real bug, it's already a bug.

> My suggestion is to just get rid of pg_log_fatal() and replace them all 

I'm on board with dropping the separate FATAL log level if there's
consensus to do so; I think it adds more confusion than anything else.
I still want to have something like pg_fatal(), though, because it's
impossible to deny the usefulness of such an abbreviation.  It's already
been reinvented three or four times independently.

Independently of either of those points, I still want to make the changes
I proposed w.r.t. making explicit concepts of "detail" and "hint"
addendums.  What we have right now in the frontend code is an impossible
mishmash of three or four ways of filling that lack, all inconsistent.

			regards, tom lane

---

From: Tom Lane <[email-masked]>
Date: Fri, 25 Feb 2022 12:15:25 -0500

> consensus to do so; I think it adds more confusion than anything else.

I feel that the reasonable alternatives are either to drop the FATAL
log level, or try to make it actually mean something by consistently
using it for errors that are indeed fatal.  I had a go at doing the
latter, but eventually concluded that that way madness lies.  It's
not too hard to use "pg_log_fatal" when there's an exit(1) right
after it, but there are quite a lot of cases where a subroutine
reports an error and returns a failure code to its caller, whereupon
the caller exits.  Either the subroutine has to make an unwarranted
assumption about what its callers will do, or we need to make an API
change to allow the subroutine itself to exit(), or we are going to
present a user experience that is inconsistently different depending
on internal implementation details.

Just to add insult to injury, once I'd gotten done with the easy
part (use pg_log_fatal where there's an adjacent exit()), a whole
lot of TAP tests fell over, because they were expecting "error:"
not "fatal:".  The reverse direction of s/fatal/error/g only breaks
one TAP test, which says a lot about how many places are troubling
to make this distinction now.

I conclude that we ought to drop the separate FATAL level and just
use ERROR instead.

The attached revision does that, standardizes on pg_fatal() as the
abbreviation for pg_log_error() + exit(1), and invents detail/hint
features as per previous discussion.

			regards, tom lane

diff --git a/contrib/oid2name/oid2name.c b/contrib/oid2name/oid2name.c
index 65cce49993..a62a5eedb1 100644
--- a/contrib/oid2name/oid2name.c
+++ b/contrib/oid2name/oid2name.c
@@ -182,16 +182,17 @@ get_opts(int argc, char **argv, struct options *my_opts)
 				break;
 
 			default:
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+				/* getopt_long already emitted a complaint */
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 				exit(1);
 		}
 	}
 
 	if (optind < argc)
 	{
-		fprintf(stderr, _("%s: too many command-line arguments (first is \"%s\")\n"),
-				progname, argv[optind]);
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+		pg_log_error("too many command-line arguments (first is \"%s\")",
+					 argv[optind]);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 }
@@ -328,11 +329,8 @@ sql_conn(struct options *my_opts)
 		conn = PQconnectdbParams(keywords, values, true);
 
 		if (!conn)
-		{
-			pg_log_error("could not connect to database %s",
-						 my_opts->dbname);
-			exit(1);
-		}
+			pg_fatal("could not connect to database %s",
+					 my_opts->dbname);
 
 		if (PQstatus(conn) == CONNECTION_BAD &&
 			PQconnectionNeedsPassword(conn) &&
@@ -359,7 +357,7 @@ sql_conn(struct options *my_opts)
 					 PQerrorMessage(conn));
 		PQclear(res);
 		PQfinish(conn);
-		exit(-1);
+		exit(1);
 	}
 	PQclear(res);
 
@@ -390,11 +388,11 @@ sql_exec(PGconn *conn, const char *todo, bool quiet)
 	if (!res || PQresultStatus(res) > 2)
 	{
 		pg_log_error("query failed: %s", PQerrorMessage(conn));
-		pg_log_error("query was: %s", todo);
+		pg_log_error_detail("Query was: %s", todo);
 
 		PQclear(res);
 		PQfinish(conn);
-		exit(-1);
+		exit(1);
 	}
 
 	/* get the number of fields */
diff --git a/contrib/vacuumlo/vacuumlo.c b/contrib/vacuumlo/vacuumlo.c
index d15edca454..b7c8f2c805 100644
--- a/contrib/vacuumlo/vacuumlo.c
+++ b/contrib/vacuumlo/vacuumlo.c
@@ -492,19 +492,13 @@ main(int argc, char **argv)
 	{
 		switch (c)
 		{
-			case '?':
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
-				exit(1);
 			case 'h':
 				param.pg_host = pg_strdup(optarg);
 				break;
 			case 'l':
 				param.transaction_limit = strtol(optarg, NULL, 10);
 				if (param.transaction_limit < 0)
-				{
-					pg_log_error("transaction limit must not be negative (0 disables)");
-					exit(1);
-				}
+					pg_fatal("transaction limit must not be negative (0 disables)");
 				break;
 			case 'n':
 				param.dry_run = 1;
@@ -513,10 +507,7 @@ main(int argc, char **argv)
 			case 'p':
 				port = strtol(optarg, NULL, 10);
 				if ((port < 1) || (port > 65535))
-				{
-					pg_log_error("invalid port number: %s", optarg);
-					exit(1);
-				}
+					pg_fatal("invalid port number: %s", optarg);
 				param.pg_port = pg_strdup(optarg);
 				break;
 			case 'U':
@@ -532,7 +523,8 @@ main(int argc, char **argv)
 				param.pg_prompt = TRI_YES;
 				break;
 			default:
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+				/* getopt_long already emitted a complaint */
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 				exit(1);
 		}
 	}
@@ -541,7 +533,7 @@ main(int argc, char **argv)
 	if (optind >= argc)
 	{
 		pg_log_error("missing required argument: database name");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 97f15971e2..4dea2ccd52 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -329,10 +329,7 @@ escape_quotes(const char *src)
 	char	   *result = escape_single_quotes_ascii(src);
 
 	if (!result)
-	{
-		pg_log_error("out of memory");
-		exit(1);
-	}
+		pg_fatal("out of memory");
 	return result;
 }
 
@@ -462,10 +459,7 @@ readfile(const char *path)
 	int			n;
 
 	if ((infile = fopen(path, "r")) == NULL)
-	{
-		pg_log_error("could not open file \"%s\" for reading: %m", path);
-		exit(1);
-	}
+		pg_fatal("could not open file \"%s\" for reading: %m", path);
 
 	initStringInfo(&line);
 
@@ -506,24 +500,15 @@ writefile(char *path, char **lines)
 	char	  **line;
 
 	if ((out_file = fopen(path, "w")) == NULL)
-	{
-		pg_log_error("could not open file \"%s\" for writing: %m", path);
-		exit(1);
-	}
+		pg_fatal("could not open file \"%s\" for writing: %m", path);
 	for (line = lines; *line != NULL; line++)
 	{
 		if (fputs(*line, out_file) < 0)
-		{
-			pg_log_error("could not write file \"%s\": %m", path);
-			exit(1);
-		}
+			pg_fatal("could not write file \"%s\": %m", path);
 		free(*line);
 	}
 	if (fclose(out_file))
-	{
-		pg_log_error("could not write file \"%s\": %m", path);
-		exit(1);
-	}
+		pg_fatal("could not write file \"%s\": %m", path);
 }
 
 /*
@@ -609,9 +594,7 @@ get_id(void)
 	if (geteuid() == 0)			/* 0 is root's uid */
 	{
 		pg_log_error("cannot be run as root");
-		fprintf(stderr,
-				_("Please log in (using, e.g., \"su\") as the (unprivileged) user that will\n"
-				  "own the server process.\n"));
+		pg_log_error_hint("Please log in (using, e.g., \"su\") as the (unprivileged) user that will own the server process.");
 		exit(1);
 	}
 #endif
@@ -643,9 +626,8 @@ get_encoding_id(const char *encoding_name)
 		if ((enc = pg_valid_server_encoding(encoding_name)) >= 0)
 			return enc;
 	}
-	pg_log_error("\"%s\" is not a valid server encoding name",
-				 encoding_name ? encoding_name : "(null)");
-	exit(1);
+	pg_fatal("\"%s\" is not a valid server encoding name",
+			 encoding_name ? encoding_name : "(null)");
 }
 
 /*
@@ -789,25 +771,19 @@ check_input(char *path)
 		if (errno == ENOENT)
 		{
 			pg_log_error("file \"%s\" does not exist", path);
-			fprintf(stderr,
-					_("This might mean you have a corrupted installation or identified\n"
-					  "the wrong directory with the invocation option -L.\n"));
+			pg_log_error_hint("This might mean you have a corrupted installation or identified the wrong directory with the invocation option -L.");
 		}
 		else
 		{
 			pg_log_error("could not access file \"%s\": %m", path);
-			fprintf(stderr,
-					_("This might mean you have a corrupted installation or identified\n"
-					  "the wrong directory with the invocation option -L.\n"));
+			pg_log_error_hint("This might mean you have a corrupted installation or identified the wrong directory with the invocation option -L.");
 		}
 		exit(1);
 	}
 	if (!S_ISREG(statbuf.st_mode))
 	{
 		pg_log_error("file \"%s\" is not a regular file", path);
-		fprintf(stderr,
-				_("This might mean you have a corrupted installation or identified\n"
-				  "the wrong directory with the invocation option -L.\n"));
+		pg_log_error_hint("This might mean you have a corrupted installation or identified the wrong directory with the invocation option -L.");
 		exit(1);
 	}
 }
@@ -828,16 +804,10 @@ write_version_file(const char *extrapath)
 		path = psprintf("%s/%s/PG_VERSION", pg_data, extrapath);
 
 	if ((version_file = fopen(path, PG_BINARY_W)) == NULL)
-	{
-		pg_log_error("could not open file \"%s\" for writing: %m", path);
-		exit(1);
-	}
+		pg_fatal("could not open file \"%s\" for writing: %m", path);
 	if (fprintf(version_file, "%s\n", PG_MAJORVERSION) < 0 ||
 		fclose(version_file))
-	{
-		pg_log_error("could not write file \"%s\": %m", path);
-		exit(1);
-	}
+		pg_fatal("could not write file \"%s\": %m", path);
 	free(path);
 }
 
@@ -854,15 +824,9 @@ set_null_conf(void)
 	path = psprintf("%s/postgresql.conf", pg_data);
 	conf_file = fopen(path, PG_BINARY_W);
 	if (conf_file == NULL)
-	{
-		pg_log_error("could not open file \"%s\" for writing: %m", path);
-		exit(1);
-	}
+		pg_fatal("could not open file \"%s\" for writing: %m", path);
 	if (fclose(conf_file))
-	{
-		pg_log_error("could not write file \"%s\": %m", path);
-		exit(1);
-	}
+		pg_fatal("could not write file \"%s\": %m", path);
 	free(path);
 }
 
@@ -1216,10 +1180,7 @@ setup_config(void)
 
 	writefile(path, conflines);
 	if (chmod(path, pg_file_create_mode) != 0)
-	{
-		pg_log_error("could not change permissions of \"%s\": %m", path);
-		exit(1);
-	}
+		pg_fatal("could not change permissions of \"%s\": %m", path);
 
 	/*
 	 * create the automatic configuration file to store the configuration
@@ -1235,10 +1196,7 @@ setup_config(void)
 
 	writefile(path, autoconflines);
 	if (chmod(path, pg_file_create_mode) != 0)
-	{
-		pg_log_error("could not change permissions of \"%s\": %m", path);
-		exit(1);
-	}
+		pg_fatal("could not change permissions of \"%s\": %m", path);
 
 	free(conflines);
 
@@ -1321,10 +1279,7 @@ setup_config(void)
 
 	writefile(path, conflines);
 	if (chmod(path, pg_file_create_mode) != 0)
-	{
-		pg_log_error("could not change permissions of \"%s\": %m", path);
-		exit(1);
-	}
+		pg_fatal("could not change permissions of \"%s\": %m", path);
 
 	free(conflines);
 
@@ -1336,10 +1291,7 @@ setup_config(void)
 
 	writefile(path, conflines);
 	if (chmod(path, pg_file_create_mode) != 0)
-	{
-		pg_log_error("could not change permissions of \"%s\": %m", path);
-		exit(1);
-	}
+		pg_fatal("could not change permissions of \"%s\": %m", path);
 
 	free(conflines);
 
@@ -1373,9 +1325,8 @@ bootstrap_template1(void)
 	{
 		pg_log_error("input file \"%s\" does not belong to PostgreSQL %s",
 					 bki_file, PG_VERSION);
-		fprintf(stderr,
-				_("Check your installation or specify the correct path "
-				  "using the option -L.\n"));
+		pg_log_error_hint("Check your installation or specify the correct path "
+						  "using the option -L.");
 		exit(1);
 	}
 
@@ -1495,21 +1446,17 @@ get_su_pwd(void)
 		FILE	   *pwf = fopen(pwfilename, "r");
 
 		if (!pwf)
-		{
-			pg_log_error("could not open file \"%s\" for reading: %m",
-						 pwfilename);
-			exit(1);
-		}
+			pg_fatal("could not open file \"%s\" for reading: %m",
+					 pwfilename);
 		pwd1 = pg_get_line(pwf, NULL);
 		if (!pwd1)
 		{
 			if (ferror(pwf))
-				pg_log_error("could not read password from file \"%s\": %m",
-							 pwfilename);
+				pg_fatal("could not read password from file \"%s\": %m",
+						 pwfilename);
 			else
-				pg_log_error("password file \"%s\" is empty",
-							 pwfilename);
-			exit(1);
+				pg_fatal("password file \"%s\" is empty",
+						 pwfilename);
 		}
 		fclose(pwf);
 
@@ -2049,10 +1996,7 @@ check_locale_name(int category, const char *locale, char **canonname)
 
 	save = setlocale(category, NULL);
 	if (!save)
-	{
-		pg_log_error("setlocale() failed");
-		exit(1);
-	}
+		pg_fatal("setlocale() failed");
 
 	/* save may be pointing at a modifiable scratch variable, so copy it. */
 	save = pg_strdup(save);
@@ -2070,17 +2014,14 @@ check_locale_name(int category, const char *locale, char **canonname)
 
 	/* restore old value. */
 	if (!setlocale(category, save))
-	{
-		pg_log_error("failed to restore old locale \"%s\"", save);
-		exit(1);
-	}
+		pg_fatal("failed to restore old locale \"%s\"", save);
 	free(save);
 
 	/* complain if locale wasn't valid */
 	if (res == NULL)
 	{
 		if (*locale)
-			pg_log_error("invalid locale name \"%s\"", locale);
+			pg_fatal("invalid locale name \"%s\"", locale);
 		else
 		{
 			/*
@@ -2091,9 +2032,8 @@ check_locale_name(int category, const char *locale, char **canonname)
 			 * setlocale's behavior is implementation-specific, it's hard to
 			 * be sure what it didn't like.  Print a safe generic message.
 			 */
-			pg_log_error("invalid locale settings; check LANG and LC_* environment variables");
+			pg_fatal("invalid locale settings; check LANG and LC_* environment variables");
 		}
-		exit(1);
 	}
 }
 
@@ -2119,15 +2059,14 @@ check_locale_encoding(const char *locale, int user_enc)
 		  user_enc == PG_SQL_ASCII))
 	{
 		pg_log_error("encoding mismatch");
-		fprintf(stderr,
-				_("The encoding you selected (%s) and the encoding that the\n"
-				  "selected locale uses (%s) do not match.  This would lead to\n"
-				  "misbehavior in various character string processing functions.\n"
-				  "Rerun %s and either do not specify an encoding explicitly,\n"
-				  "or choose a matching combination.\n"),
-				pg_encoding_to_char(user_enc),
-				pg_encoding_to_char(locale_enc),
-				progname);
+		pg_log_error_detail("The encoding you selected (%s) and the encoding that the "
+							"selected locale uses (%s) do not match. This would lead to "
+							"misbehavior in various character string processing functions.",
+							pg_encoding_to_char(user_enc),
+							pg_encoding_to_char(locale_enc));
+		pg_log_error_hint("Rerun %s and either do not specify an encoding explicitly, "
+						  "or choose a matching combination.",
+						  progname);
 		return false;
 	}
 	return true;
@@ -2259,9 +2198,8 @@ check_authmethod_valid(const char *authmethod, const char *const *valid_methods,
 				return;
 	}
 
-	pg_log_error("invalid authentication method \"%s\" for \"%s\" connections",
-				 authmethod, conntype);
-	exit(1);
+	pg_fatal("invalid authentication method \"%s\" for \"%s\" connections",
+			 authmethod, conntype);
 }
 
 static void
@@ -2274,10 +2212,7 @@ check_need_password(const char *authmethodlocal, const char *authmethodhost)
 		 strcmp(authmethodhost, "password") == 0 ||
 		 strcmp(authmethodhost, "scram-sha-256") == 0) &&
 		!(pwprompt || pwfilename))
-	{
-		pg_log_error("must specify a password for the superuser to enable password authentication");
-		exit(1);
-	}
+		pg_fatal("must specify a password for the superuser to enable password authentication");
 }

@@ -2297,10 +2232,9 @@ setup_pgdata(void)
 		else
 		{
 			pg_log_error("no data directory specified");
-			fprintf(stderr,
-					_("You must identify the directory where the data for this database system\n"
-					  "will reside.  Do this with either the invocation option -D or the\n"
-					  "environment variable PGDATA.\n"));
+			pg_log_error_hint("You must identify the directory where the data for this database system "
+							  "will reside.  Do this with either the invocation option -D or the "
+							  "environment variable PGDATA.");
 			exit(1);
 		}
 	}
@@ -2315,10 +2249,7 @@ setup_pgdata(void)
 	 * have embedded spaces.
 	 */
 	if (setenv("PGDATA", pg_data, 1) != 0)
-	{
-		pg_log_error("could not set environment");
-		exit(1);
-	}
+		pg_fatal("could not set environment");
 }

@@ -2336,15 +2267,13 @@ setup_bin_paths(const char *argv0)
 			strlcpy(full_path, progname, sizeof(full_path));
 
 		if (ret == -1)
-			pg_log_error("The program \"%s\" is needed by %s but was not found in the\n"
-						 "same directory as \"%s\".\n"
-						 "Check your installation.",
+			pg_log_error("the program \"%s\" is needed by %s but was not found in the same directory as \"%s\"",
 						 "postgres", progname, full_path);
 		else
-			pg_log_error("The program \"%s\" was found by \"%s\"\n"
-						 "but was not the same version as %s.\n"
-						 "Check your installation.",
+			pg_log_error("the program \"%s\" was found by \"%s\" "
+						 "but was not the same version as %s",
 						 "postgres", full_path, progname);
+		pg_log_error_hint("Check your installation.");
 		exit(1);
 	}
 
@@ -2359,10 +2288,7 @@ setup_bin_paths(const char *argv0)
 		get_share_path(backend_exec, share_path);
 	}
 	else if (!is_absolute_path(share_path))
-	{
-		pg_log_error("input file location must be an absolute path");
-		exit(1);
-	}
+		pg_fatal("input file location must be an absolute path");
 
 	canonicalize_path(share_path);
 }
@@ -2406,9 +2332,8 @@ setup_locale_encoding(void)
 			/* Couldn't recognize the locale's codeset */
 			pg_log_error("could not find suitable encoding for locale \"%s\"",
 						 lc_ctype);
-			fprintf(stderr, _("Rerun %s with the -E option.\n"), progname);
-			fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-					progname);
+			pg_log_error_hint("Rerun %s with the -E option.", progname);
+			pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 			exit(1);
 		}
 		else if (!pg_valid_server_encoding_id(ctype_enc))
@@ -2427,10 +2352,10 @@ setup_locale_encoding(void)
 #else
 			pg_log_error("locale \"%s\" requires unsupported encoding \"%s\"",
 						 lc_ctype, pg_encoding_to_char(ctype_enc));
-			fprintf(stderr,
-					_("Encoding \"%s\" is not allowed as a server-side encoding.\n"
-					  "Rerun %s with a different locale selection.\n"),
-					pg_encoding_to_char(ctype_enc), progname);
+			pg_log_error_detail("Encoding \"%s\" is not allowed as a server-side encoding.",
+								pg_encoding_to_char(ctype_enc));
+			pg_log_error_hint("Rerun %s with a different locale selection.",
+							  progname);
 			exit(1);
 #endif
 		}
@@ -2573,10 +2498,7 @@ create_data_directory(void)
 			fflush(stdout);
 
 			if (pg_mkdir_p(pg_data, pg_dir_create_mode) != 0)
-			{
-				pg_log_error("could not create directory \"%s\": %m", pg_data);
-				exit(1);
-			}
+				pg_fatal("could not create directory \"%s\": %m", pg_data);
 			else
 				check_ok();
 
@@ -2590,11 +2512,8 @@ create_data_directory(void)
 			fflush(stdout);
 
 			if (chmod(pg_data, pg_dir_create_mode) != 0)
-			{
-				pg_log_error("could not change permissions of directory \"%s\": %m",
-							 pg_data);
-				exit(1);
-			}
+				pg_fatal("could not change permissions of directory \"%s\": %m",
+						 pg_data);
 			else
 				check_ok();
 
@@ -2609,17 +2528,15 @@ create_data_directory(void)
 			if (ret != 4)
 				warn_on_mount_point(ret);
 			else
-				fprintf(stderr,
-						_("If you want to create a new database system, either remove or empty\n"
-						  "the directory \"%s\" or run %s\n"
-						  "with an argument other than \"%s\".\n"),
-						pg_data, progname, pg_data);
+				pg_log_error_hint("If you want to create a new database system, either remove or empty "
+								  "the directory \"%s\" or run %s "
+								  "with an argument other than \"%s\".",
+								  pg_data, progname, pg_data);
 			exit(1);			/* no further message needed */
 
 		default:
 			/* Trouble accessing directory */
-			pg_log_error("could not access directory \"%s\": %m", pg_data);
-			exit(1);
+			pg_fatal("could not access directory \"%s\": %m", pg_data);
 	}
 }
 
@@ -2640,10 +2557,7 @@ create_xlog_or_symlink(void)
 		/* clean up xlog directory name, check it's absolute */
 		canonicalize_path(xlog_dir);
 		if (!is_absolute_path(xlog_dir))
-		{
-			pg_log_error("WAL directory location must be an absolute path");
-			exit(1);
-		}
+			pg_fatal("WAL directory location must be an absolute path");
 
 		/* check if the specified xlog directory exists/is empty */
 		switch ((ret = pg_check_dir(xlog_dir)))
@@ -2655,11 +2569,8 @@ create_xlog_or_symlink(void)
 				fflush(stdout);
 
 				if (pg_mkdir_p(xlog_dir, pg_dir_create_mode) != 0)
-				{
-					pg_log_error("could not create directory \"%s\": %m",
-								 xlog_dir);
-					exit(1);
-				}
+					pg_fatal("could not create directory \"%s\": %m",
+							 xlog_dir);
 				else
 					check_ok();
 
@@ -2673,11 +2584,8 @@ create_xlog_or_symlink(void)
 				fflush(stdout);
 
 				if (chmod(xlog_dir, pg_dir_create_mode) != 0)
-				{
-					pg_log_error("could not change permissions of directory \"%s\": %m",
-								 xlog_dir);
-					exit(1);
-				}
+					pg_fatal("could not change permissions of directory \"%s\": %m",
+							 xlog_dir);
 				else
 					check_ok();
 
@@ -2692,39 +2600,29 @@ create_xlog_or_symlink(void)
 				if (ret != 4)
 					warn_on_mount_point(ret);
 				else
-					fprintf(stderr,
-							_("If you want to store the WAL there, either remove or empty the directory\n"
-							  "\"%s\".\n"),
-							xlog_dir);
+					pg_log_error_hint("If you want to store the WAL there, either remove or empty the directory \"%s\".",
+									  xlog_dir);
 				exit(1);
 
 			default:
 				/* Trouble accessing directory */
-				pg_log_error("could not access directory \"%s\": %m", xlog_dir);
-				exit(1);
+				pg_fatal("could not access directory \"%s\": %m", xlog_dir);
 		}
 
 #ifdef HAVE_SYMLINK
 		if (symlink(xlog_dir, subdirloc) != 0)
-		{
-			pg_log_error("could not create symbolic link \"%s\": %m",
-						 subdirloc);
-			exit(1);
-		}
+			pg_fatal("could not create symbolic link \"%s\": %m",
+					 subdirloc);
 #else
-		pg_log_error("symlinks are not supported on this platform");
-		exit(1);
+		pg_fatal("symlinks are not supported on this platform");
 #endif
 	}
 	else
 	{
 		/* Without -X option, just make the subdirectory normally */
 		if (mkdir(subdirloc, pg_dir_create_mode) < 0)
-		{
-			pg_log_error("could not create directory \"%s\": %m",
-						 subdirloc);
-			exit(1);
-		}
+			pg_fatal("could not create directory \"%s\": %m",
+					 subdirloc);
 	}
 
 	free(subdirloc);
@@ -2735,15 +2633,12 @@ void
 warn_on_mount_point(int error)
 {
 	if (error == 2)
-		fprintf(stderr,
-				_("It contains a dot-prefixed/invisible file, perhaps due to it being a mount point.\n"));
+		pg_log_error_detail("It contains a dot-prefixed/invisible file, perhaps due to it being a mount point.");
 	else if (error == 3)
-		fprintf(stderr,
-				_("It contains a lost+found directory, perhaps due to it being a mount point.\n"));
+		pg_log_error_detail("It contains a lost+found directory, perhaps due to it being a mount point.");
 
-	fprintf(stderr,
-			_("Using a mount point directly as the data directory is not recommended.\n"
-			  "Create a subdirectory under the mount point.\n"));
+	pg_log_error_hint("Using a mount point directly as the data directory is not recommended.\n"
+					  "Create a subdirectory under the mount point.");
 }

@@ -2782,10 +2677,7 @@ initialize_data_directory(void)
 		 * pg_mkdir_p() here, which avoids some failure modes; cf bug #13853.
 		 */
 		if (mkdir(path, pg_dir_create_mode) < 0)
-		{
-			pg_log_error("could not create directory \"%s\": %m", path);
-			exit(1);
-		}
+			pg_fatal("could not create directory \"%s\": %m", path);
 
 		free(path);
 	}
@@ -3047,8 +2939,7 @@ main(int argc, char *argv[])
 				break;
 			default:
 				/* getopt_long already emitted a complaint */
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-						progname);
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 				exit(1);
 		}
 	}
@@ -3068,8 +2959,7 @@ main(int argc, char *argv[])
 	{
 		pg_log_error("too many command-line arguments (first is \"%s\")",
 					 argv[optind]);
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
@@ -3082,10 +2972,7 @@ main(int argc, char *argv[])
 
 		/* must check that directory is readable */
 		if (pg_check_dir(pg_data) <= 0)
-		{
-			pg_log_error("could not access directory \"%s\": %m", pg_data);
-			exit(1);
-		}
+			pg_fatal("could not access directory \"%s\": %m", pg_data);
 
 		fputs(_("syncing data to disk ... "), stdout);
 		fflush(stdout);
@@ -3095,10 +2982,7 @@ main(int argc, char *argv[])
 	}
 
 	if (pwprompt && pwfilename)
-	{
-		pg_log_error("password prompt and password file cannot be specified together");
-		exit(1);
-	}
+		pg_fatal("password prompt and password file cannot be specified together");
 
 	check_authmethod_unspecified(&authmethodlocal);
 	check_authmethod_unspecified(&authmethodhost);
@@ -3120,15 +3004,9 @@ main(int argc, char *argv[])
 
 		/* verify that wal segment size is valid */
 		if (endptr == str_wal_segment_size_mb || *endptr != '\0')
-		{
-			pg_log_error("argument of --wal-segsize must be a number");
-			exit(1);
-		}
+			pg_fatal("argument of --wal-segsize must be a number");
 		if (!IsValidWalSegSize(wal_segment_size_mb * 1024 * 1024))
-		{
-			pg_log_error("argument of --wal-segsize must be a power of 2 between 1 and 1024");
-			exit(1);
-		}
+			pg_fatal("argument of --wal-segsize must be a power of 2 between 1 and 1024");
 	}
 
 	get_restricted_token();
@@ -3142,10 +3020,7 @@ main(int argc, char *argv[])
 		username = effective_user;
 
 	if (strncmp(username, "pg_", 3) == 0)
-	{
-		pg_log_error("superuser name \"%s\" is disallowed; role names cannot begin with \"pg_\"", username);
-		exit(1);
-	}
+		pg_fatal("superuser name \"%s\" is disallowed; role names cannot begin with \"pg_\"", username);
 
 	printf(_("The files belonging to this database system will be owned "
 			 "by user \"%s\".\n"
@@ -3188,8 +3063,8 @@ main(int argc, char *argv[])
 	{
 		printf("\n");
 		pg_log_warning("enabling \"trust\" authentication for local connections");
-		fprintf(stderr, _("You can change this by editing pg_hba.conf or using the option -A, or\n"
-						  "--auth-local and --auth-host, the next time you run initdb.\n"));
+		pg_log_warning_hint("You can change this by editing pg_hba.conf or using the option -A, or "
+							"--auth-local and --auth-host, the next time you run initdb.");
 	}
 
 	if (!noinstructions)
diff --git a/src/bin/pg_amcheck/pg_amcheck.c b/src/bin/pg_amcheck/pg_amcheck.c
index 6607f72938..90471e096d 100644
--- a/src/bin/pg_amcheck/pg_amcheck.c
+++ b/src/bin/pg_amcheck/pg_amcheck.c
@@ -202,9 +202,9 @@ static void compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations,
 
 #define log_no_match(...) do { \
 		if (opts.strict_names) \
-			pg_log_generic(PG_LOG_ERROR, __VA_ARGS__); \
+			pg_log_error(__VA_ARGS__); \
 		else \
-			pg_log_generic(PG_LOG_WARNING, __VA_ARGS__); \
+			pg_log_warning(__VA_ARGS__); \
 	} while(0)
 
 #define FREE_AND_SET_NULL(x) do { \
@@ -396,39 +396,24 @@ main(int argc, char *argv[])
 				else if (pg_strcasecmp(optarg, "none") == 0)
 					opts.skip = "none";
 				else
-				{
-					pg_log_error("invalid argument for option %s", "--skip");
-					exit(1);
-				}
+					pg_fatal("invalid argument for option %s", "--skip");
 				break;
 			case 7:
 				errno = 0;
 				optval = strtoul(optarg, &endptr, 10);
 				if (endptr == optarg || *endptr != '\0' || errno != 0)
-				{
-					pg_log_error("invalid start block");
-					exit(1);
-				}
+					pg_fatal("invalid start block");
 				if (optval > MaxBlockNumber)
-				{
-					pg_log_error("start block out of bounds");
-					exit(1);
-				}
+					pg_fatal("start block out of bounds");
 				opts.startblock = optval;
 				break;
 			case 8:
 				errno = 0;
 				optval = strtoul(optarg, &endptr, 10);
 				if (endptr == optarg || *endptr != '\0' || errno != 0)
-				{
-					pg_log_error("invalid end block");
-					exit(1);
-				}
+					pg_fatal("invalid end block");
 				if (optval > MaxBlockNumber)
-				{
-					pg_log_error("end block out of bounds");
-					exit(1);
-				}
+					pg_fatal("end block out of bounds");
 				opts.endblock = optval;
 				break;
 			case 9:
@@ -450,18 +435,14 @@ main(int argc, char *argv[])
 					opts.install_schema = pg_strdup(optarg);
 				break;
 			default:
-				fprintf(stderr,
-						_("Try \"%s --help\" for more information.\n"),
-						progname);
+				/* getopt_long already emitted a complaint */
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 				exit(1);
 		}
 	}
 
 	if (opts.endblock >= 0 && opts.endblock < opts.startblock)
-	{
-		pg_log_error("end block precedes start block");
-		exit(1);
-	}
+		pg_fatal("end block precedes start block");
 
 	/*
 	 * A single non-option arguments specifies a database name or connection
@@ -477,7 +458,7 @@ main(int argc, char *argv[])
 	{
 		pg_log_error("too many command-line arguments (first is \"%s\")",
 					 argv[optind]);
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
@@ -495,19 +476,13 @@ main(int argc, char *argv[])
 	if (opts.alldb)
 	{
 		if (db != NULL)
-		{
-			pg_log_error("cannot specify a database name with --all");
-			exit(1);
-		}
+			pg_fatal("cannot specify a database name with --all");
 		cparams.dbname = maintenance_db;
 	}
 	else if (db != NULL)
 	{
 		if (opts.dbpattern)
-		{
-			pg_log_error("cannot specify both a database name and database patterns");
-			exit(1);
-		}
+			pg_fatal("cannot specify both a database name and database patterns");
 		cparams.dbname = db;
 	}
 
@@ -535,7 +510,7 @@ main(int argc, char *argv[])
 	{
 		if (conn != NULL)
 			disconnectDatabase(conn);
-		pg_log_error("no databases to check");
+		pg_log_warning("no databases to check");
 		exit(0);
 	}
 
@@ -593,7 +568,7 @@ main(int argc, char *argv[])
 			/* Querying the catalog failed. */
 			pg_log_error("database \"%s\": %s",
 						 PQdb(conn), PQerrorMessage(conn));
-			pg_log_info("query was: %s", amcheck_sql);
+			pg_log_error_detail("Query was: %s", amcheck_sql);
 			PQclear(result);
 			disconnectDatabase(conn);
 			exit(1);
@@ -669,8 +644,7 @@ main(int argc, char *argv[])
 	{
 		if (conn != NULL)
 			disconnectDatabase(conn);
-		pg_log_error("no relations to check");
-		exit(1);
+		pg_fatal("no relations to check");
 	}
 	progress_report(reltotal, relprogress, pagestotal, pageschecked,
 					NULL, true, false);
@@ -919,7 +893,7 @@ run_command(ParallelSlot *slot, const char *sql)
 		pg_log_error("error sending command to database \"%s\": %s",
 					 PQdb(slot->connection),
 					 PQerrorMessage(slot->connection));
-		pg_log_error("command was: %s", sql);
+		pg_log_error_detail("Command was: %s", sql);
 		exit(1);
 	}
 }
@@ -1123,9 +1097,9 @@ verify_btree_slot_handler(PGresult *res, PGconn *conn, void *context)
 			pg_log_warning("btree index \"%s.%s.%s\": btree checking function returned unexpected number of rows: %d",
 						   rel->datinfo->datname, rel->nspname, rel->relname, ntups);
 			if (opts.verbose)
-				pg_log_info("query was: %s", rel->sql);
-			pg_log_warning("Are %s's and amcheck's versions compatible?",
-						   progname);
+				pg_log_warning_detail("Query was: %s", rel->sql);
+			pg_log_warning_hint("Are %s's and amcheck's versions compatible?",
+								progname);
 			progress_since_last_stderr = false;
 		}
 	}
@@ -1648,7 +1622,7 @@ compile_database_list(PGconn *conn, SimplePtrList *databases,
 	if (PQresultStatus(res) != PGRES_TUPLES_OK)
 	{
 		pg_log_error("query failed: %s", PQerrorMessage(conn));
-		pg_log_info("query was: %s", sql.data);
+		pg_log_error_detail("Query was: %s", sql.data);
 		disconnectDatabase(conn);
 		exit(1);
 	}
@@ -1673,11 +1647,8 @@ compile_database_list(PGconn *conn, SimplePtrList *databases,
 			 */
 			fatal = opts.strict_names;
 			if (pattern_id >= opts.include.len)
-			{
-				pg_log_error("internal error: received unexpected database pattern_id %d",
-							 pattern_id);
-				exit(1);
-			}
+				pg_fatal("internal error: received unexpected database pattern_id %d",
+						 pattern_id);
 			log_no_match("no connectable databases to check matching \"%s\"",
 						 opts.include.data[pattern_id].pattern);
 		}
@@ -2096,7 +2067,7 @@ compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations,
 	if (PQresultStatus(res) != PGRES_TUPLES_OK)
 	{
 		pg_log_error("query failed: %s", PQerrorMessage(conn));
-		pg_log_info("query was: %s", sql.data);
+		pg_log_error_detail("Query was: %s", sql.data);
 		disconnectDatabase(conn);
 		exit(1);
 	}
@@ -2136,11 +2107,8 @@ compile_relation_list_one_db(PGconn *conn, SimplePtrList *relations,
 			 */
 
 			if (pattern_id >= opts.include.len)
-			{
-				pg_log_error("internal error: received unexpected relation pattern_id %d",
-							 pattern_id);
-				exit(1);
-			}
+				pg_fatal("internal error: received unexpected relation pattern_id %d",
+						 pattern_id);
 
 			opts.include.data[pattern_id].matched = true;
 		}
diff --git a/src/bin/pg_archivecleanup/pg_archivecleanup.c b/src/bin/pg_archivecleanup/pg_archivecleanup.c
index 6c3e7f4e01..064cbb222f 100644
--- a/src/bin/pg_archivecleanup/pg_archivecleanup.c
+++ b/src/bin/pg_archivecleanup/pg_archivecleanup.c
@@ -148,33 +148,21 @@ CleanupPriorWALFiles(void)
 
 				rc = unlink(WALFilePath);
 				if (rc != 0)
-				{
-					pg_log_error("could not remove file \"%s\": %m",
-								 WALFilePath);
-					exit(1);
-				}
+					pg_fatal("could not remove file \"%s\": %m",
+							 WALFilePath);
 			}
 		}
 
 		if (errno)
-		{
-			pg_log_error("could not read archive location \"%s\": %m",
-						 archiveLocation);
-			exit(1);
-		}
+			pg_fatal("could not read archive location \"%s\": %m",
+					 archiveLocation);
 		if (closedir(xldir))
-		{
-			pg_log_error("could not close archive location \"%s\": %m",
-						 archiveLocation);
-			exit(1);
-		}
-	}
-	else
-	{
-		pg_log_error("could not open archive location \"%s\": %m",
+			pg_fatal("could not close archive location \"%s\": %m",
 					 archiveLocation);
-		exit(1);
 	}
+	else
+		pg_fatal("could not open archive location \"%s\": %m",
+				 archiveLocation);
 }
 
 /*
@@ -247,7 +235,7 @@ SetWALFileNameForCleanup(void)
 	if (!fnameOK)
 	{
 		pg_log_error("invalid file name argument");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(2);
 	}
 }
@@ -321,9 +309,9 @@ main(int argc, char **argv)
 													 * from xlogfile names */
 				break;
 			default:
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+				/* getopt already emitted a complaint */
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 				exit(2);
-				break;
 		}
 	}
 
@@ -342,7 +330,7 @@ main(int argc, char **argv)
 	else
 	{
 		pg_log_error("must specify archive location");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(2);
 	}
 
@@ -354,14 +342,14 @@ main(int argc, char **argv)
 	else
 	{
 		pg_log_error("must specify oldest kept WAL file");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(2);
 	}
 
 	if (optind < argc)
 	{
 		pg_log_error("too many command-line arguments");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(2);
 	}
 
diff --git a/src/bin/pg_basebackup/bbstreamer_file.c b/src/bin/pg_basebackup/bbstreamer_file.c
index d721f87891..393e9f340c 100644
--- a/src/bin/pg_basebackup/bbstreamer_file.c
+++ b/src/bin/pg_basebackup/bbstreamer_file.c
@@ -90,10 +90,7 @@ bbstreamer_plain_writer_new(char *pathname, FILE *file)
 	{
 		streamer->file = fopen(pathname, "wb");
 		if (streamer->file == NULL)
-		{
-			pg_log_error("could not create file \"%s\": %m", pathname);
-			exit(1);
-		}
+			pg_fatal("could not create file \"%s\": %m", pathname);
 		streamer->should_close_file = true;
 	}
 
@@ -121,9 +118,8 @@ bbstreamer_plain_writer_content(bbstreamer *streamer,
 		/* if write didn't set errno, assume problem is no disk space */
 		if (errno == 0)
 			errno = ENOSPC;
-		pg_log_error("could not write to file \"%s\": %m",
-					 mystreamer->pathname);
-		exit(1);
+		pg_fatal("could not write to file \"%s\": %m",
+				 mystreamer->pathname);
 	}
 }
 
@@ -139,11 +135,8 @@ bbstreamer_plain_writer_finalize(bbstreamer *streamer)
 	mystreamer = (bbstreamer_plain_writer *) streamer;
 
 	if (mystreamer->should_close_file && fclose(mystreamer->file) != 0)
-	{
-		pg_log_error("could not close file \"%s\": %m",
-					 mystreamer->pathname);
-		exit(1);
-	}
+		pg_fatal("could not close file \"%s\": %m",
+				 mystreamer->pathname);
 
 	mystreamer->file = NULL;
 	mystreamer->should_close_file = false;
@@ -262,9 +255,8 @@ bbstreamer_extractor_content(bbstreamer *streamer, bbstreamer_member *member,
 				/* if write didn't set errno, assume problem is no disk space */
 				if (errno == 0)
 					errno = ENOSPC;
-				pg_log_error("could not write to file \"%s\": %m",
-							 mystreamer->filename);
-				exit(1);
+				pg_fatal("could not write to file \"%s\": %m",
+						 mystreamer->filename);
 			}
 			break;
 
@@ -280,8 +272,7 @@ bbstreamer_extractor_content(bbstreamer *streamer, bbstreamer_member *member,
 
 		default:
 			/* Shouldn't happen. */
-			pg_log_error("unexpected state while extracting archive");
-			exit(1);
+			pg_fatal("unexpected state while extracting archive");
 	}
 }
 
@@ -304,20 +295,14 @@ extract_directory(const char *filename, mode_t mode)
 			   pg_str_endswith(filename, "/pg_xlog") ||
 			   pg_str_endswith(filename, "/archive_status")) &&
 			  errno == EEXIST))
-		{
-			pg_log_error("could not create directory \"%s\": %m",
-						 filename);
-			exit(1);
-		}
+			pg_fatal("could not create directory \"%s\": %m",
+					 filename);
 	}
 
 #ifndef WIN32
 	if (chmod(filename, mode))
-	{
-		pg_log_error("could not set permissions on directory \"%s\": %m",
-					 filename);
-		exit(1);
-	}
+		pg_fatal("could not set permissions on directory \"%s\": %m",
+				 filename);
 #endif
 }
 
@@ -335,11 +320,8 @@ static void
 extract_link(const char *filename, const char *linktarget)
 {
 	if (symlink(linktarget, filename) != 0)
-	{
-		pg_log_error("could not create symbolic link from \"%s\" to \"%s\": %m",
-					 filename, linktarget);
-		exit(1);
-	}
+		pg_fatal("could not create symbolic link from \"%s\" to \"%s\": %m",
+				 filename, linktarget);
 }
 
 /*
@@ -354,18 +336,12 @@ create_file_for_extract(const char *filename, mode_t mode)
 
 	file = fopen(filename, "wb");
 	if (file == NULL)
-	{
-		pg_log_error("could not create file \"%s\": %m", filename);
-		exit(1);
-	}
+		pg_fatal("could not create file \"%s\": %m", filename);
 
 #ifndef WIN32
 	if (chmod(filename, mode))
-	{
-		pg_log_error("could not set permissions on file \"%s\": %m",
-					 filename);
-		exit(1);
-	}
+		pg_fatal("could not set permissions on file \"%s\": %m",
+				 filename);
 #endif
 
 	return file;
diff --git a/src/bin/pg_basebackup/bbstreamer_gzip.c b/src/bin/pg_basebackup/bbstreamer_gzip.c
index 894f857103..52fcde9ed8 100644
--- a/src/bin/pg_basebackup/bbstreamer_gzip.c
+++ b/src/bin/pg_basebackup/bbstreamer_gzip.c
@@ -91,42 +91,29 @@ bbstreamer_gzip_writer_new(char *pathname, FILE *file, int compresslevel)
 	{
 		streamer->gzfile = gzopen(pathname, "wb");
 		if (streamer->gzfile == NULL)
-		{
-			pg_log_error("could not create compressed file \"%s\": %m",
-						 pathname);
-			exit(1);
-		}
+			pg_fatal("could not create compressed file \"%s\": %m",
+					 pathname);
 	}
 	else
 	{
 		int			fd = dup(fileno(file));
 
 		if (fd < 0)
-		{
-			pg_log_error("could not duplicate stdout: %m");
-			exit(1);
-		}
+			pg_fatal("could not duplicate stdout: %m");
 
 		streamer->gzfile = gzdopen(fd, "wb");
 		if (streamer->gzfile == NULL)
-		{
-			pg_log_error("could not open output file: %m");
-			exit(1);
-		}
+			pg_fatal("could not open output file: %m");
 	}
 
 	if (gzsetparams(streamer->gzfile, compresslevel,
 					Z_DEFAULT_STRATEGY) != Z_OK)
-	{
-		pg_log_error("could not set compression level %d: %s",
-					 compresslevel, get_gz_error(streamer->gzfile));
-		exit(1);
-	}
+		pg_fatal("could not set compression level %d: %s",
+				 compresslevel, get_gz_error(streamer->gzfile));
 
 	return &streamer->base;
 #else
-	pg_log_error("this build does not support compression");
-	exit(1);
+	pg_fatal("this build does not support compression");
 #endif
 }
 
@@ -152,9 +139,8 @@ bbstreamer_gzip_writer_content(bbstreamer *streamer,
 		/* if write didn't set errno, assume problem is no disk space */
 		if (errno == 0)
 			errno = ENOSPC;
-		pg_log_error("could not write to compressed file \"%s\": %s",
-					 mystreamer->pathname, get_gz_error(mystreamer->gzfile));
-		exit(1);
+		pg_fatal("could not write to compressed file \"%s\": %s",
+				 mystreamer->pathname, get_gz_error(mystreamer->gzfile));
 	}
 }
 
@@ -177,11 +163,8 @@ bbstreamer_gzip_writer_finalize(bbstreamer *streamer)
 
 	errno = 0;					/* in case gzclose() doesn't set it */
 	if (gzclose(mystreamer->gzfile) != 0)
-	{
-		pg_log_error("could not close compressed file \"%s\": %m",
-					 mystreamer->pathname);
-		exit(1);
-	}
+		pg_fatal("could not close compressed file \"%s\": %m",
+				 mystreamer->pathname);
 
 	mystreamer->gzfile = NULL;
 }
@@ -258,15 +241,11 @@ bbstreamer_gzip_decompressor_new(bbstreamer *next)
 	 * possible value for safety.
 	 */
 	if (inflateInit2(zs, 15 + 16) != Z_OK)
-	{
-		pg_log_error("could not initialize compression library");
-		exit(1);
-	}
+		pg_fatal("could not initialize compression library");
 
 	return &streamer->base;
 #else
-	pg_log_error("this build does not support compression");
-	exit(1);
+	pg_fatal("this build does not support compression");
 #endif
 }
 
diff --git a/src/bin/pg_basebackup/bbstreamer_inject.c b/src/bin/pg_basebackup/bbstreamer_inject.c
index 79c378d96e..cc804f1091 100644
--- a/src/bin/pg_basebackup/bbstreamer_inject.c
+++ b/src/bin/pg_basebackup/bbstreamer_inject.c
@@ -186,8 +186,7 @@ bbstreamer_recovery_injector_content(bbstreamer *streamer,
 
 		default:
 			/* Shouldn't happen. */
-			pg_log_error("unexpected state while injecting recovery settings");
-			exit(1);
+			pg_fatal("unexpected state while injecting recovery settings");
 	}
 
 	bbstreamer_content(mystreamer->base.bbs_next, &mystreamer->member,
diff --git a/src/bin/pg_basebackup/bbstreamer_lz4.c b/src/bin/pg_basebackup/bbstreamer_lz4.c
index f0bc226bf8..8416713994 100644
--- a/src/bin/pg_basebackup/bbstreamer_lz4.c
+++ b/src/bin/pg_basebackup/bbstreamer_lz4.c
@@ -104,13 +104,12 @@ bbstreamer_lz4_compressor_new(bbstreamer *next, int compresslevel)
 
 	ctxError = LZ4F_createCompressionContext(&streamer->cctx, LZ4F_VERSION);
 	if (LZ4F_isError(ctxError))
-			pg_log_error("could not create lz4 compression context: %s",
-						 LZ4F_getErrorName(ctxError));
+		pg_log_error("could not create lz4 compression context: %s",
+					 LZ4F_getErrorName(ctxError));
 
 	return &streamer->base;
 #else
-	pg_log_error("this build does not support compression");
-	exit(1);
+	pg_fatal("this build does not support compression");
 #endif
 }
 
@@ -296,16 +295,12 @@ bbstreamer_lz4_decompressor_new(bbstreamer *next)
 	/* Initialize internal stream state for decompression */
 	ctxError = LZ4F_createDecompressionContext(&streamer->dctx, LZ4F_VERSION);
 	if (LZ4F_isError(ctxError))
-	{
-		pg_log_error("could not initialize compression library: %s",
-				LZ4F_getErrorName(ctxError));
-		exit(1);
-	}
+		pg_fatal("could not initialize compression library: %s",
+				 LZ4F_getErrorName(ctxError));
 
 	return &streamer->base;
 #else
-	pg_log_error("this build does not support compression");
-	exit(1);
+	pg_fatal("this build does not support compression");
 #endif
 }
 
diff --git a/src/bin/pg_basebackup/bbstreamer_tar.c b/src/bin/pg_basebackup/bbstreamer_tar.c
index 6ab981156e..fcbad579df 100644
--- a/src/bin/pg_basebackup/bbstreamer_tar.c
+++ b/src/bin/pg_basebackup/bbstreamer_tar.c
@@ -241,16 +241,12 @@ bbstreamer_tar_parser_content(bbstreamer *streamer, bbstreamer_member *member,
 				 */
 				bbstreamer_buffer_bytes(streamer, &data, &len, len);
 				if (len > 2 * TAR_BLOCK_SIZE)
-				{
-					pg_log_error("tar file trailer exceeds 2 blocks");
-					exit(1);
-				}
+					pg_fatal("tar file trailer exceeds 2 blocks");
 				return;
 
 			default:
 				/* Shouldn't happen. */
-				pg_log_error("unexpected state while parsing tar archive");
-				exit(1);
+				pg_fatal("unexpected state while parsing tar archive");
 		}
 	}
 }
@@ -297,10 +293,7 @@ bbstreamer_tar_header(bbstreamer_tar_parser *mystreamer)
 	 */
 	strlcpy(member->pathname, &buffer[0], MAXPGPATH);
 	if (member->pathname[0] == '\0')
-	{
-		pg_log_error("tar member has empty name");
-		exit(1);
-	}
+		pg_fatal("tar member has empty name");
 	member->size = read_tar_number(&buffer[124], 12);
 	member->mode = read_tar_number(&buffer[100], 8);
 	member->uid = read_tar_number(&buffer[108], 8);
@@ -332,10 +325,7 @@ bbstreamer_tar_parser_finalize(bbstreamer *streamer)
 	if (mystreamer->next_context != BBSTREAMER_ARCHIVE_TRAILER &&
 		(mystreamer->next_context != BBSTREAMER_MEMBER_HEADER ||
 		 mystreamer->base.bbs_buffer.len > 0))
-	{
-		pg_log_error("COPY stream ended before last file was finished");
-		exit(1);
-	}
+		pg_fatal("COPY stream ended before last file was finished");
 
 	/* Send the archive trailer, even if empty. */
 	bbstreamer_content(streamer->bbs_next, NULL,
diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c
index c1ed7aeeee..b589b3bbb6 100644
--- a/src/bin/pg_basebackup/pg_basebackup.c
+++ b/src/bin/pg_basebackup/pg_basebackup.c
@@ -320,20 +320,14 @@ tablespace_list_append(const char *arg)
 	for (arg_ptr = arg; *arg_ptr; arg_ptr++)
 	{
 		if (dst_ptr - dst >= MAXPGPATH)
-		{
-			pg_log_error("directory name too long");
-			exit(1);
-		}
+			pg_fatal("directory name too long");
 
 		if (*arg_ptr == '\\' && *(arg_ptr + 1) == '=')
 			;					/* skip backslash escaping = */
 		else if (*arg_ptr == '=' && (arg_ptr == arg || *(arg_ptr - 1) != '\\'))
 		{
 			if (*cell->new_dir)
-			{
-				pg_log_error("multiple \"=\" signs in tablespace mapping");
-				exit(1);
-			}
+				pg_fatal("multiple \"=\" signs in tablespace mapping");
 			else
 				dst = dst_ptr = cell->new_dir;
 		}
@@ -342,10 +336,7 @@ tablespace_list_append(const char *arg)
 	}
 
 	if (!*cell->old_dir || !*cell->new_dir)
-	{
-		pg_log_error("invalid tablespace mapping format \"%s\", must be \"OLDDIR=NEWDIR\"", arg);
-		exit(1);
-	}
+		pg_fatal("invalid tablespace mapping format \"%s\", must be \"OLDDIR=NEWDIR\"", arg);
 
 	/*
 	 * This check isn't absolutely necessary.  But all tablespaces are created
@@ -354,18 +345,12 @@ tablespace_list_append(const char *arg)
 	 * consistent with the new_dir check.
 	 */
 	if (!is_absolute_path(cell->old_dir))
-	{
-		pg_log_error("old directory is not an absolute path in tablespace mapping: %s",
-					 cell->old_dir);
-		exit(1);
-	}
+		pg_fatal("old directory is not an absolute path in tablespace mapping: %s",
+				 cell->old_dir);
 
 	if (!is_absolute_path(cell->new_dir))
-	{
-		pg_log_error("new directory is not an absolute path in tablespace mapping: %s",
-					 cell->new_dir);
-		exit(1);
-	}
+		pg_fatal("new directory is not an absolute path in tablespace mapping: %s",
+				 cell->new_dir);
 
 	/*
 	 * Comparisons done with these values should involve similarly
@@ -480,17 +465,11 @@ reached_end_position(XLogRecPtr segendpos, uint32 timeline,
 			MemSet(xlogend, 0, sizeof(xlogend));
 			r = read(bgpipe[0], xlogend, sizeof(xlogend) - 1);
 			if (r < 0)
-			{
-				pg_log_error("could not read from ready pipe: %m");
-				exit(1);
-			}
+				pg_fatal("could not read from ready pipe: %m");
 
 			if (sscanf(xlogend, "%X/%X", &hi, &lo) != 2)
-			{
-				pg_log_error("could not parse write-ahead log location \"%s\"",
-							 xlogend);
-				exit(1);
-			}
+				pg_fatal("could not parse write-ahead log location \"%s\"",
+						 xlogend);
 			xlogendptr = ((uint64) hi) << 32 | lo;
 			has_xlogendptr = 1;
 
@@ -641,11 +620,8 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier)
 
 	/* Convert the starting position */
 	if (sscanf(startpos, "%X/%X", &hi, &lo) != 2)
-	{
-		pg_log_error("could not parse write-ahead log location \"%s\"",
-					 startpos);
-		exit(1);
-	}
+		pg_fatal("could not parse write-ahead log location \"%s\"",
+				 startpos);
 	param->startptr = ((uint64) hi) << 32 | lo;
 	/* Round off to even segment position */
 	param->startptr -= XLogSegmentOffset(param->startptr, WalSegSz);
@@ -653,10 +629,7 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier)
 #ifndef WIN32
 	/* Create our background pipe */
 	if (pipe(bgpipe) < 0)
-	{
-		pg_log_error("could not create pipe for background process: %m");
-		exit(1);
-	}
+		pg_fatal("could not create pipe for background process: %m");
 #endif
 
 	/* Get a second connection */
@@ -711,10 +684,7 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier)
 				 "pg_xlog" : "pg_wal");
 
 		if (pg_mkdir_p(statusdir, pg_dir_create_mode) != 0 && errno != EEXIST)
-		{
-			pg_log_error("could not create directory \"%s\": %m", statusdir);
-			exit(1);
-		}
+			pg_fatal("could not create directory \"%s\": %m", statusdir);
 	}
 
 	/*
@@ -737,10 +707,7 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier)
 		exit(ret);
 	}
 	else if (bgchild < 0)
-	{
-		pg_log_error("could not create background process: %m");
-		exit(1);
-	}
+		pg_fatal("could not create background process: %m");
 
 	/*
 	 * Else we are in the parent process and all is well.
@@ -749,10 +716,7 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier)
 #else							/* WIN32 */
 	bgchild = _beginthreadex(NULL, 0, (void *) LogStreamerMain, param, 0, NULL);
 	if (bgchild == 0)
-	{
-		pg_log_error("could not create background thread: %m");
-		exit(1);
-	}
+		pg_fatal("could not create background thread: %m");
 #endif
 }
 
@@ -772,10 +736,7 @@ verify_dir_is_empty_or_create(char *dirname, bool *created, bool *found)
 			 * Does not exist, so create
 			 */
 			if (pg_mkdir_p(dirname, pg_dir_create_mode) == -1)
-			{
-				pg_log_error("could not create directory \"%s\": %m", dirname);
-				exit(1);
-			}
+				pg_fatal("could not create directory \"%s\": %m", dirname);
 			if (created)
 				*created = true;
 			return;
@@ -794,15 +755,13 @@ verify_dir_is_empty_or_create(char *dirname, bool *created, bool *found)
 			/*
 			 * Exists, not empty
 			 */
-			pg_log_error("directory \"%s\" exists but is not empty", dirname);
-			exit(1);
+			pg_fatal("directory \"%s\" exists but is not empty", dirname);
 		case -1:
 
 			/*
 			 * Access problem
 			 */
-			pg_log_error("could not access directory \"%s\": %m", dirname);
-			exit(1);
+			pg_fatal("could not access directory \"%s\": %m", dirname);
 	}
 }
 
@@ -931,23 +890,16 @@ parse_max_rate(char *src)
 	errno = 0;
 	result = strtod(src, &after_num);
 	if (src == after_num)
-	{
-		pg_log_error("transfer rate \"%s\" is not a valid value", src);
-		exit(1);
-	}
+		pg_fatal("transfer rate \"%s\" is not a valid value", src);
 	if (errno != 0)
-	{
-		pg_log_error("invalid transfer rate \"%s\": %m", src);
-		exit(1);
-	}
+		pg_fatal("invalid transfer rate \"%s\": %m", src);
 
 	if (result <= 0)
 	{
 		/*
 		 * Reject obviously wrong values here.
 		 */
-		pg_log_error("transfer rate must be greater than zero");
-		exit(1);
+		pg_fatal("transfer rate must be greater than zero");
 	}
 
 	/*
@@ -977,27 +929,18 @@ parse_max_rate(char *src)
 		after_num++;
 
 	if (*after_num != '\0')
-	{
-		pg_log_error("invalid --max-rate unit: \"%s\"", suffix);
-		exit(1);
-	}
+		pg_fatal("invalid --max-rate unit: \"%s\"", suffix);
 
 	/* Valid integer? */
 	if ((uint64) result != (uint64) ((uint32) result))
-	{
-		pg_log_error("transfer rate \"%s\" exceeds integer range", src);
-		exit(1);
-	}
+		pg_fatal("transfer rate \"%s\" exceeds integer range", src);
 
 	/*
 	 * The range is checked on the server side too, but avoid the server
 	 * connection if a nonsensical value was passed.
 	 */
 	if (result < MAX_RATE_LOWER || result > MAX_RATE_UPPER)
-	{
-		pg_log_error("transfer rate \"%s\" is out of range", src);
-		exit(1);
-	}
+		pg_fatal("transfer rate \"%s\" is out of range", src);
 
 	return (int32) result;
 }
@@ -1104,10 +1047,7 @@ parse_compress_options(char *src, WalCompressionMethod *methodres,
 	/* Check the contents after the colon separator. */
 	sep++;
 	if (*sep == '\0')
-	{
-		pg_log_error("no compression level defined for method %s", firstpart);
-		exit(1);
-	}
+		pg_fatal("no compression level defined for method %s", firstpart);
 
 	/*
 	 * For any of the methods currently supported, the data after the
@@ -1133,11 +1073,8 @@ ReceiveCopyData(PGconn *conn, WriteDataCallback callback,
 	/* Get the COPY data stream. */
 	res = PQgetResult(conn);
 	if (PQresultStatus(res) != PGRES_COPY_OUT)
-	{
-		pg_log_error("could not get COPY data stream: %s",
-					 PQerrorMessage(conn));
-		exit(1);
-	}
+		pg_fatal("could not get COPY data stream: %s",
+				 PQerrorMessage(conn));
 	PQclear(res);
 
 	/* Loop over chunks until done. */
@@ -1153,11 +1090,8 @@ ReceiveCopyData(PGconn *conn, WriteDataCallback callback,
 			break;
 		}
 		else if (r == -2)
-		{
-			pg_log_error("could not read COPY data: %s",
-						 PQerrorMessage(conn));
-			exit(1);
-		}
+			pg_fatal("could not read COPY data: %s",
+					 PQerrorMessage(conn));
 
 		if (bgchild_exited)
 		{
@@ -1226,13 +1160,13 @@ CreateBackupStreamer(char *archive_name, char *spclocation,
 	if (must_parse_archive && !is_tar && !is_tar_gz && !is_tar_lz4)
 	{
 		pg_log_error("unable to parse archive: %s", archive_name);
-		pg_log_info("only tar archives can be parsed");
+		pg_log_error_detail("Only tar archives can be parsed.");
 		if (format == 'p')
-			pg_log_info("plain format requires pg_basebackup to parse the archive");
+			pg_log_error_detail("Plain format requires pg_basebackup to parse the archive.");
 		if (inject_manifest)
-			pg_log_info("using - as the output directory requires pg_basebackup to parse the archive");
+			pg_log_error_detail("Using - as the output directory requires pg_basebackup to parse the archive.");
 		if (writerecoveryconf)
-			pg_log_info("the -R option requires pg_basebackup to parse the archive");
+			pg_log_error_detail("The -R option requires pg_basebackup to parse the archive.");
 		exit(1);
 	}
 
@@ -1437,10 +1371,7 @@ ReceiveArchiveStreamChunk(size_t r, char *copybuf, void *callback_data)
 				/* Sanity check. */
 				if (state->manifest_buffer != NULL ||
 					state->manifest_file !=NULL)
-				{
-					pg_log_error("archives should precede manifest");
-					exit(1);
-				}
+					pg_fatal("archives should precede manifest");
 
 				/* Parse the rest of the CopyData message. */
 				archive_name = GetCopyDataString(r, copybuf, &cursor);
@@ -1455,11 +1386,8 @@ ReceiveArchiveStreamChunk(size_t r, char *copybuf, void *callback_data)
 				if (archive_name[0] == '\0' || archive_name[0] == '.' ||
 					strchr(archive_name, '/') != NULL ||
 					strchr(archive_name, '\\') != NULL)
-				{
-					pg_log_error("invalid archive name: \"%s\"",
-								 archive_name);
-					exit(1);
-				}
+					pg_fatal("invalid archive name: \"%s\"",
+							 archive_name);
 
 				/*
 				 * An empty spclocation is treated as NULL. We expect this
@@ -1518,9 +1446,8 @@ ReceiveArchiveStreamChunk(size_t r, char *copybuf, void *callback_data)
 						 */
 						if (errno == 0)
 							errno = ENOSPC;
-						pg_log_error("could not write to file \"%s\": %m",
-									 state->manifest_filename);
-						exit(1);
+						pg_fatal("could not write to file \"%s\": %m",
+								 state->manifest_filename);
 					}
 				}
 				else if (state->streamer != NULL)
@@ -1530,10 +1457,7 @@ ReceiveArchiveStreamChunk(size_t r, char *copybuf, void *callback_data)
 									   r - 1, BBSTREAMER_UNKNOWN);
 				}
 				else
-				{
-					pg_log_error("unexpected payload data");
-					exit(1);
-				}
+					pg_fatal("unexpected payload data");
 				break;
 			}
 
@@ -1586,11 +1510,8 @@ ReceiveArchiveStreamChunk(size_t r, char *copybuf, void *callback_data)
 						state->manifest_file =
 							fopen(state->manifest_filename, "wb");
 						if (state->manifest_file == NULL)
-						{
-							pg_log_error("could not create file \"%s\": %m",
-										 state->manifest_filename);
-							exit(1);
-						}
+							pg_fatal("could not create file \"%s\": %m",
+									 state->manifest_filename);
 					}
 				}
 				break;
@@ -1679,11 +1600,10 @@ static void
 ReportCopyDataParseError(size_t r, char *copybuf)
 {
 	if (r == 0)
-		pg_log_error("empty COPY message");
+		pg_fatal("empty COPY message");
 	else
-		pg_log_error("malformed COPY message of type %d, length %zu",
-					 copybuf[0], r);
-	exit(1);
+		pg_fatal("malformed COPY message of type %d, length %zu",
+				 copybuf[0], r);
 }
 
 /*
@@ -1728,10 +1648,7 @@ ReceiveTarFile(PGconn *conn, char *archive_name, char *spclocation,
 		initPQExpBuffer(&buf);
 		ReceiveBackupManifestInMemory(conn, &buf);
 		if (PQExpBufferDataBroken(buf))
-		{
-			pg_log_error("out of memory");
-			exit(1);
-		}
+			pg_fatal("out of memory");
 
 		/* Inject it into the output tarfile. */
 		bbstreamer_inject_file(manifest_inject_streamer, "backup_manifest",
@@ -1801,10 +1718,7 @@ ReceiveBackupManifest(PGconn *conn)
 			 "%s/backup_manifest.tmp", basedir);
 	state.file = fopen(state.filename, "wb");
 	if (state.file == NULL)
-	{
-		pg_log_error("could not create file \"%s\": %m", state.filename);
-		exit(1);
-	}
+		pg_fatal("could not create file \"%s\": %m", state.filename);
 
 	ReceiveCopyData(conn, ReceiveBackupManifestChunk, &state);
 
@@ -1825,8 +1739,7 @@ ReceiveBackupManifestChunk(size_t r, char *copybuf, void *callback_data)
 		/* if write didn't set errno, assume problem is no disk space */
 		if (errno == 0)
 			errno = ENOSPC;
-		pg_log_error("could not write to file \"%s\": %m", state->filename);
-		exit(1);
+		pg_fatal("could not write to file \"%s\": %m", state->filename);
 	}
 }
 
@@ -1885,9 +1798,8 @@ BaseBackup(void)
 	{
 		const char *serverver = PQparameterStatus(conn, "server_version");
 
-		pg_log_error("incompatible server version %s",
-					 serverver ? serverver : "'unknown'");
-		exit(1);
+		pg_fatal("incompatible server version %s",
+				 serverver ? serverver : "'unknown'");
 	}
 	if (serverMajor >= 1500)
 		use_new_option_syntax = true;
@@ -1970,16 +1882,10 @@ BaseBackup(void)
 		char	   *colon;
 
 		if (serverMajor < 1500)
-		{
-			pg_log_error("backup targets are not supported by this server version");
-			exit(1);
-		}
+			pg_fatal("backup targets are not supported by this server version");
 
 		if (writerecoveryconf)
-		{
-			pg_log_error("recovery configuration cannot be written when a backup target is used");
-			exit(1);
-		}
+			pg_fatal("recovery configuration cannot be written when a backup target is used");
 
 		AppendPlainCommandOption(&buf, use_new_option_syntax, "TABLESPACE_MAP");
 
@@ -2008,10 +1914,7 @@ BaseBackup(void)
 		char *compressmethodstr = NULL;
 
 		if (!use_new_option_syntax)
-		{
-			pg_log_error("server does not support server-side compression");
-			exit(1);
-		}
+			pg_fatal("server does not support server-side compression");
 		switch (compressmethod)
 		{
 			case COMPRESSION_GZIP:
@@ -2049,28 +1952,19 @@ BaseBackup(void)
 		basebkp = psprintf("BASE_BACKUP %s", buf.data);
 
 	if (PQsendQuery(conn, basebkp) == 0)
-	{
-		pg_log_error("could not send replication command \"%s\": %s",
-					 "BASE_BACKUP", PQerrorMessage(conn));
-		exit(1);
-	}
+		pg_fatal("could not send replication command \"%s\": %s",
+				 "BASE_BACKUP", PQerrorMessage(conn));
 
 	/*
 	 * Get the starting WAL location
 	 */
 	res = PQgetResult(conn);
 	if (PQresultStatus(res) != PGRES_TUPLES_OK)
-	{
-		pg_log_error("could not initiate base backup: %s",
-					 PQerrorMessage(conn));
-		exit(1);
-	}
+		pg_fatal("could not initiate base backup: %s",
+				 PQerrorMessage(conn));
 	if (PQntuples(res) != 1)
-	{
-		pg_log_error("server returned unexpected response to BASE_BACKUP command; got %d rows and %d fields, expected %d rows and %d fields",
-					 PQntuples(res), PQnfields(res), 1, 2);
-		exit(1);
-	}
+		pg_fatal("server returned unexpected response to BASE_BACKUP command; got %d rows and %d fields, expected %d rows and %d fields",
+				 PQntuples(res), PQnfields(res), 1, 2);
 
 	strlcpy(xlogstart, PQgetvalue(res, 0, 0), sizeof(xlogstart));
 
@@ -2098,16 +1992,10 @@ BaseBackup(void)
 	 */
 	res = PQgetResult(conn);
 	if (PQresultStatus(res) != PGRES_TUPLES_OK)
-	{
-		pg_log_error("could not get backup header: %s",
-					 PQerrorMessage(conn));
-		exit(1);
-	}
+		pg_fatal("could not get backup header: %s",
+				 PQerrorMessage(conn));
 	if (PQntuples(res) < 1)
-	{
-		pg_log_error("no data returned from server");
-		exit(1);
-	}
+		pg_fatal("no data returned from server");
 
 	/*
 	 * Sum up the total size, for progress reporting
@@ -2142,11 +2030,8 @@ BaseBackup(void)
 	writing_to_stdout = format == 't' && basedir != NULL &&
 		strcmp(basedir, "-") == 0;
 	if (writing_to_stdout && PQntuples(res) > 1)
-	{
-		pg_log_error("can only write single tablespace to stdout, database has %d",
-					 PQntuples(res));
-		exit(1);
-	}
+		pg_fatal("can only write single tablespace to stdout, database has %d",
+				 PQntuples(res));
 
 	/*
 	 * If we're streaming WAL, start the streaming session before we start
@@ -2222,16 +2107,10 @@ BaseBackup(void)
 	 */
 	res = PQgetResult(conn);
 	if (PQresultStatus(res) != PGRES_TUPLES_OK)
-	{
-		pg_log_error("backup failed: %s",
-					 PQerrorMessage(conn));
-		exit(1);
-	}
+		pg_fatal("backup failed: %s",
+				 PQerrorMessage(conn));
 	if (PQntuples(res) != 1)
-	{
-		pg_log_error("no write-ahead log end position returned from server");
-		exit(1);
-	}
+		pg_fatal("no write-ahead log end position returned from server");
 	strlcpy(xlogend, PQgetvalue(res, 0, 0), sizeof(xlogend));
 	if (verbose && includewal != NO_WAL)
 		pg_log_info("write-ahead log end point: %s", xlogend);
@@ -2286,20 +2165,11 @@ BaseBackup(void)
 		/* Just wait for the background process to exit */
 		r = waitpid(bgchild, &status, 0);
 		if (r == (pid_t) -1)
-		{
-			pg_log_error("could not wait for child process: %m");
-			exit(1);
-		}
+			pg_fatal("could not wait for child process: %m");
 		if (r != bgchild)
-		{
-			pg_log_error("child %d died, expected %d", (int) r, (int) bgchild);
-			exit(1);
-		}
+			pg_fatal("child %d died, expected %d", (int) r, (int) bgchild);
 		if (status != 0)
-		{
-			pg_log_error("%s", wait_result_to_str(status));
-			exit(1);
-		}
+			pg_fatal("%s", wait_result_to_str(status));
 		/* Exited normally, we're happy! */
 #else							/* WIN32 */
 
@@ -2309,11 +2179,8 @@ BaseBackup(void)
 		 * it's there.
 		 */
 		if (sscanf(xlogend, "%X/%X", &hi, &lo) != 2)
-		{
-			pg_log_error("could not parse write-ahead log location \"%s\"",
-						 xlogend);
-			exit(1);
-		}
+			pg_fatal("could not parse write-ahead log location \"%s\"",
+					 xlogend);
 		xlogendptr = ((uint64) hi) << 32 | lo;
 		InterlockedIncrement(&has_xlogendptr);
 
@@ -2322,21 +2189,16 @@ BaseBackup(void)
 			WAIT_OBJECT_0)
 		{
 			_dosmaperr(GetLastError());
-			pg_log_error("could not wait for child thread: %m");
-			exit(1);
+			pg_fatal("could not wait for child thread: %m");
 		}
 		if (GetExitCodeThread((HANDLE) bgchild_handle, &status) == 0)
 		{
 			_dosmaperr(GetLastError());
-			pg_log_error("could not get child thread exit status: %m");
-			exit(1);
+			pg_fatal("could not get child thread exit status: %m");
 		}
 		if (status != 0)
-		{
-			pg_log_error("child thread exited with error %u",
-						 (unsigned int) status);
-			exit(1);
-		}
+			pg_fatal("child thread exited with error %u",
+					 (unsigned int) status);
 		/* Exited normally, we're happy */
 #endif
 	}
@@ -2403,11 +2265,8 @@ BaseBackup(void)
 		else
 		{
 			if (rename(tmp_filename, filename) != 0)
-			{
-				pg_log_error("could not rename file \"%s\" to \"%s\": %m",
-							 tmp_filename, filename);
-				exit(1);
-			}
+				pg_fatal("could not rename file \"%s\" to \"%s\": %m",
+						 tmp_filename, filename);
 		}
 	}
 
@@ -2497,11 +2356,8 @@ main(int argc, char **argv)
 				else if (strcmp(optarg, "t") == 0 || strcmp(optarg, "tar") == 0)
 					format = 't';
 				else
-				{
-					pg_log_error("invalid output format \"%s\", must be \"plain\" or \"tar\"",
-								 optarg);
-					exit(1);
-				}
+					pg_fatal("invalid output format \"%s\", must be \"plain\" or \"tar\"",
+							 optarg);
 				break;
 			case 'r':
 				maxrate = parse_max_rate(optarg);
@@ -2544,11 +2400,8 @@ main(int argc, char **argv)
 					includewal = STREAM_WAL;
 				}
 				else
-				{
-					pg_log_error("invalid wal-method option \"%s\", must be \"fetch\", \"stream\", or \"none\"",
-								 optarg);
-					exit(1);
-				}
+					pg_fatal("invalid wal-method option \"%s\", must be \"fetch\", \"stream\", or \"none\"",
+							 optarg);
 				break;
 			case 1:
 				xlog_dir = pg_strdup(optarg);
@@ -2581,11 +2434,8 @@ main(int argc, char **argv)
 				else if (pg_strcasecmp(optarg, "spread") == 0)
 					fastcheckpoint = false;
 				else
-				{
-					pg_log_error("invalid checkpoint argument \"%s\", must be \"fast\" or \"spread\"",
-								 optarg);
-					exit(1);
-				}
+					pg_fatal("invalid checkpoint argument \"%s\", must be \"fast\" or \"spread\"",
+							 optarg);
 				break;
 			case 'd':
 				connection_string = pg_strdup(optarg);
@@ -2634,12 +2484,8 @@ main(int argc, char **argv)
 				manifest_checksums = pg_strdup(optarg);
 				break;
 			default:
-
-				/*
-				 * getopt_long already emitted a complaint
-				 */
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-						progname);
+				/* getopt_long already emitted a complaint */
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 				exit(1);
 		}
 	}
@@ -2651,8 +2497,7 @@ main(int argc, char **argv)
 	{
 		pg_log_error("too many command-line arguments (first is \"%s\")",
 					 argv[optind]);
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
@@ -2674,8 +2519,7 @@ main(int argc, char **argv)
 	if (backup_target != NULL && format != '\0')
 	{
 		pg_log_error("cannot specify both format and backup target");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 	if (format == '\0')
@@ -2687,15 +2531,13 @@ main(int argc, char **argv)
 	if (basedir == NULL && backup_target == NULL)
 	{
 		pg_log_error("must specify output directory or backup target");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 	if (basedir != NULL && backup_target != NULL)
 	{
 		pg_log_error("cannot specify both output directory and backup target");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
@@ -2720,8 +2562,7 @@ main(int argc, char **argv)
 	if (backup_target != NULL && compressloc == COMPRESS_LOCATION_CLIENT)
 	{
 		pg_log_error("client-side compression is not possible when a backup target is specified");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
@@ -2731,8 +2572,7 @@ main(int argc, char **argv)
 	if (format == 'p' && compressloc == COMPRESS_LOCATION_CLIENT)
 	{
 		pg_log_error("only tar mode backups can be compressed");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
@@ -2742,23 +2582,20 @@ main(int argc, char **argv)
 	if (backup_target != NULL && includewal == STREAM_WAL)
 	{
 		pg_log_error("WAL cannot be streamed when a backup target is specified");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 	if (format == 't' && includewal == STREAM_WAL && strcmp(basedir, "-") == 0)
 	{
 		pg_log_error("cannot stream write-ahead logs in tar mode to stdout");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
 	if (replication_slot && includewal != STREAM_WAL)
 	{
 		pg_log_error("replication slots can only be used with WAL streaming");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
@@ -2770,8 +2607,7 @@ main(int argc, char **argv)
 		if (replication_slot)
 		{
 			pg_log_error("--no-slot cannot be used with slot name");
-			fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-					progname);
+			pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 			exit(1);
 		}
 		temp_replication_slot = false;
@@ -2783,8 +2619,7 @@ main(int argc, char **argv)
 		{
 			pg_log_error("%s needs a slot to be specified using --slot",
 						 "--create-slot");
-			fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-					progname);
+			pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 			exit(1);
 		}
 
@@ -2792,8 +2627,7 @@ main(int argc, char **argv)
 		{
 			pg_log_error("%s and %s are incompatible options",
 						 "--create-slot", "--no-slot");
-			fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-					progname);
+			pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 			exit(1);
 		}
 	}
@@ -2806,15 +2640,13 @@ main(int argc, char **argv)
 		if (backup_target != NULL)
 		{
 			pg_log_error("WAL directory location cannot be specified along with a backup target");
-			fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-					progname);
+			pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 			exit(1);
 		}
 		if (format != 'p')
 		{
 			pg_log_error("WAL directory location can only be specified in plain mode");
-			fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-					progname);
+			pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 			exit(1);
 		}
 
@@ -2823,8 +2655,7 @@ main(int argc, char **argv)
 		if (!is_absolute_path(xlog_dir))
 		{
 			pg_log_error("WAL directory location must be an absolute path");
-			fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-					progname);
+			pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 			exit(1);
 		}
 	}
@@ -2837,37 +2668,29 @@ main(int argc, char **argv)
 			{
 				pg_log_error("cannot use compression level with method %s",
 							 "none");
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-						progname);
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 				exit(1);
 			}
 			break;
 		case COMPRESSION_GZIP:
 			if (compresslevel > 9)
-			{
-				pg_log_error("compression level %d of method %s higher than maximum of 9",
-							 compresslevel, "gzip");
-				exit(1);
-			}
+				pg_fatal("compression level %d of method %s higher than maximum of 9",
+						 compresslevel, "gzip");
 			if (compressloc == COMPRESS_LOCATION_CLIENT)
 			{
 #ifdef HAVE_LIBZ
 				if (compresslevel == 0)
 					compresslevel = Z_DEFAULT_COMPRESSION;
 #else
-				pg_log_error("this build does not support compression with %s",
-							 "gzip");
-				exit(1);
+				pg_fatal("this build does not support compression with %s",
+						 "gzip");
 #endif
 			}
 			break;
 		case COMPRESSION_LZ4:
 			if (compresslevel > 12)
-			{
-				pg_log_error("compression level %d of method %s higher than maximum of 12",
-							 compresslevel, "lz4");
-				exit(1);
-			}
+				pg_fatal("compression level %d of method %s higher than maximum of 12",
+						 compresslevel, "lz4");
 			break;
 	}
 
@@ -2878,8 +2701,7 @@ main(int argc, char **argv)
 	{
 		pg_log_error("%s and %s are incompatible options",
 					 "--progress", "--no-estimate-size");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
@@ -2890,8 +2712,7 @@ main(int argc, char **argv)
 	{
 		pg_log_error("%s and %s are incompatible options",
 					 "--no-manifest", "--manifest-checksums");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
@@ -2899,8 +2720,7 @@ main(int argc, char **argv)
 	{
 		pg_log_error("%s and %s are incompatible options",
 					 "--no-manifest", "--manifest-force-encode");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
@@ -2968,13 +2788,9 @@ main(int argc, char **argv)
 
 #ifdef HAVE_SYMLINK
 		if (symlink(xlog_dir, linkloc) != 0)
-		{
-			pg_log_error("could not create symbolic link \"%s\": %m", linkloc);
-			exit(1);
-		}
+			pg_fatal("could not create symbolic link \"%s\": %m", linkloc);
 #else
-		pg_log_error("symlinks are not supported on this platform");
-		exit(1);
+		pg_fatal("symlinks are not supported on this platform");
 #endif
 		free(linkloc);
 	}
diff --git a/src/bin/pg_basebackup/pg_receivewal.c b/src/bin/pg_basebackup/pg_receivewal.c
index ccb215c398..f1dc85447e 100644
--- a/src/bin/pg_basebackup/pg_receivewal.c
+++ b/src/bin/pg_basebackup/pg_receivewal.c
@@ -239,10 +239,7 @@ get_destination_dir(char *dest_folder)
 	Assert(dest_folder != NULL);
 	dir = opendir(dest_folder);
 	if (dir == NULL)
-	{
-		pg_log_error("could not open directory \"%s\": %m", basedir);
-		exit(1);
-	}
+		pg_fatal("could not open directory \"%s\": %m", basedir);
 
 	return dir;
 }
@@ -256,10 +253,7 @@ close_destination_dir(DIR *dest_dir, char *dest_folder)
 {
 	Assert(dest_dir != NULL && dest_folder != NULL);
 	if (closedir(dest_dir))
-	{
-		pg_log_error("could not close directory \"%s\": %m", dest_folder);
-		exit(1);
-	}
+		pg_fatal("could not close directory \"%s\": %m", dest_folder);
 }

@@ -322,10 +316,7 @@ FindStreamingStart(uint32 *tli)
 
 			snprintf(fullpath, sizeof(fullpath), "%s/%s", basedir, dirent->d_name);
 			if (stat(fullpath, &statbuf) != 0)
-			{
-				pg_log_error("could not stat file \"%s\": %m", fullpath);
-				exit(1);
-			}
+				pg_fatal("could not stat file \"%s\": %m", fullpath);
 
 			if (statbuf.st_size != WalSegSz)
 			{
@@ -346,27 +337,20 @@ FindStreamingStart(uint32 *tli)
 
 			fd = open(fullpath, O_RDONLY | PG_BINARY, 0);
 			if (fd < 0)
-			{
-				pg_log_error("could not open compressed file \"%s\": %m",
-							 fullpath);
-				exit(1);
-			}
+				pg_fatal("could not open compressed file \"%s\": %m",
+						 fullpath);
 			if (lseek(fd, (off_t) (-4), SEEK_END) < 0)
-			{
-				pg_log_error("could not seek in compressed file \"%s\": %m",
-							 fullpath);
-				exit(1);
-			}
+				pg_fatal("could not seek in compressed file \"%s\": %m",
+						 fullpath);
 			r = read(fd, (char *) buf, sizeof(buf));
 			if (r != sizeof(buf))
 			{
 				if (r < 0)
-					pg_log_error("could not read compressed file \"%s\": %m",
-								 fullpath);
+					pg_fatal("could not read compressed file \"%s\": %m",
+							 fullpath);
 				else
-					pg_log_error("could not read compressed file \"%s\": read %d of %zu",
-								 fullpath, r, sizeof(buf));
-				exit(1);
+					pg_fatal("could not read compressed file \"%s\": read %d of %zu",
+							 fullpath, r, sizeof(buf));
 			}
 
 			close(fd);
@@ -399,18 +383,12 @@ FindStreamingStart(uint32 *tli)
 
 			fd = open(fullpath, O_RDONLY | PG_BINARY, 0);
 			if (fd < 0)
-			{
-				pg_log_error("could not open file \"%s\": %m", fullpath);
-				exit(1);
-			}
+				pg_fatal("could not open file \"%s\": %m", fullpath);
 
 			status = LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
 			if (LZ4F_isError(status))
-			{
-				pg_log_error("could not create LZ4 decompression context: %s",
-							 LZ4F_getErrorName(status));
-				exit(1);
-			}
+				pg_fatal("could not create LZ4 decompression context: %s",
+						 LZ4F_getErrorName(status));
 
 			outbuf = pg_malloc0(LZ4_CHUNK_SZ);
 			readbuf = pg_malloc0(LZ4_CHUNK_SZ);
@@ -421,10 +399,7 @@ FindStreamingStart(uint32 *tli)
 
 				r = read(fd, readbuf, LZ4_CHUNK_SZ);
 				if (r < 0)
-				{
-					pg_log_error("could not read file \"%s\": %m", fullpath);
-					exit(1);
-				}
+					pg_fatal("could not read file \"%s\": %m", fullpath);
 
 				/* Done reading the file */
 				if (r == 0)
@@ -442,12 +417,9 @@ FindStreamingStart(uint32 *tli)
 					status = LZ4F_decompress(ctx, outbuf, &out_size,
 											 readp, &read_size, &dec_opt);
 					if (LZ4F_isError(status))
-					{
-						pg_log_error("could not decompress file \"%s\": %s",
-									 fullpath,
-									 LZ4F_getErrorName(status));
-						exit(1);
-					}
+						pg_fatal("could not decompress file \"%s\": %s",
+								 fullpath,
+								 LZ4F_getErrorName(status));
 
 					readp += read_size;
 					uncompressed_size += out_size;
@@ -468,11 +440,8 @@ FindStreamingStart(uint32 *tli)
 
 			status = LZ4F_freeDecompressionContext(ctx);
 			if (LZ4F_isError(status))
-			{
-				pg_log_error("could not free LZ4 decompression context: %s",
-							 LZ4F_getErrorName(status));
-				exit(1);
-			}
+				pg_fatal("could not free LZ4 decompression context: %s",
+						 LZ4F_getErrorName(status));
 
 			if (uncompressed_size != WalSegSz)
 			{
@@ -483,8 +452,8 @@ FindStreamingStart(uint32 *tli)
 #else
 			pg_log_error("could not check file \"%s\"",
 						 dirent->d_name);
-			pg_log_error("this build does not support compression with %s",
-						 "LZ4");
+			pg_log_error_detail("This build does not support compression with %s.",
+								"LZ4");
 			exit(1);
 #endif
 		}
@@ -501,10 +470,7 @@ FindStreamingStart(uint32 *tli)
 	}
 
 	if (errno)
-	{
-		pg_log_error("could not read directory \"%s\": %m", basedir);
-		exit(1);
-	}
+		pg_fatal("could not read directory \"%s\": %m", basedir);
 
 	close_destination_dir(dir, basedir);
 
@@ -752,10 +718,7 @@ main(int argc, char **argv)
 				break;
 			case 'E':
 				if (sscanf(optarg, "%X/%X", &hi, &lo) != 2)
-				{
-					pg_log_error("could not parse end position \"%s\"", optarg);
-					exit(1);
-				}
+					pg_fatal("could not parse end position \"%s\"", optarg);
 				endpos = ((uint64) hi) << 32 | lo;
 				break;
 			case 'n':
@@ -793,19 +756,12 @@ main(int argc, char **argv)
 				else if (pg_strcasecmp(optarg, "none") == 0)
 					compression_method = COMPRESSION_NONE;
 				else
-				{
-					pg_log_error("invalid value \"%s\" for option %s",
-								 optarg, "--compression-method");
-					exit(1);
-				}
+					pg_fatal("invalid value \"%s\" for option %s",
+							 optarg, "--compression-method");
 				break;
 			default:
-
-				/*
-				 * getopt_long already emitted a complaint
-				 */
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-						progname);
+				/* getopt_long already emitted a complaint */
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 				exit(1);
 		}
 	}
@@ -817,16 +773,14 @@ main(int argc, char **argv)
 	{
 		pg_log_error("too many command-line arguments (first is \"%s\")",
 					 argv[optind]);
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
 	if (do_drop_slot && do_create_slot)
 	{
 		pg_log_error("cannot use --create-slot together with --drop-slot");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
@@ -835,16 +789,14 @@ main(int argc, char **argv)
 		/* translator: second %s is an option name */
 		pg_log_error("%s needs a slot to be specified using --slot",
 					 do_drop_slot ? "--drop-slot" : "--create-slot");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
 	if (synchronous && !do_sync)
 	{
 		pg_log_error("cannot use --synchronous together with --no-sync");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
@@ -854,8 +806,7 @@ main(int argc, char **argv)
 	if (basedir == NULL && !do_drop_slot && !do_create_slot)
 	{
 		pg_log_error("no target directory specified");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
@@ -870,8 +821,7 @@ main(int argc, char **argv)
 			{
 				pg_log_error("cannot use --compress with --compression-method=%s",
 							 "none");
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-						progname);
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 				exit(1);
 			}
 			break;
@@ -883,9 +833,8 @@ main(int argc, char **argv)
 				compresslevel = Z_DEFAULT_COMPRESSION;
 			}
 #else
-			pg_log_error("this build does not support compression with %s",
-						 "gzip");
-			exit(1);
+			pg_fatal("this build does not support compression with %s",
+					 "gzip");
 #endif
 			break;
 		case COMPRESSION_LZ4:
@@ -894,14 +843,12 @@ main(int argc, char **argv)
 			{
 				pg_log_error("cannot use --compress with --compression-method=%s",
 							 "lz4");
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-						progname);
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 				exit(1);
 			}
 #else
-			pg_log_error("this build does not support compression with %s",
-						 "LZ4");
-			exit(1);
+			pg_fatal("this build does not support compression with %s",
+					 "LZ4");
 #endif
 			break;
 	}
@@ -947,11 +894,8 @@ main(int argc, char **argv)
 	 * be defined in this context.
 	 */
 	if (db_name)
-	{
-		pg_log_error("replication connection using slot \"%s\" is unexpectedly database specific",
-					 replication_slot);
-		exit(1);
-	}
+		pg_fatal("replication connection using slot \"%s\" is unexpectedly database specific",
+				 replication_slot);
 
 	/*
 	 * Set umask so that directories/files are created with the same
@@ -1009,10 +953,7 @@ main(int argc, char **argv)
 			exit(0);
 		}
 		else if (noloop)
-		{
-			pg_log_error("disconnected");
-			exit(1);
-		}
+			pg_fatal("disconnected");
 		else
 		{
 			/* translator: check source for value for %d */
diff --git a/src/bin/pg_basebackup/pg_recvlogical.c b/src/bin/pg_basebackup/pg_recvlogical.c
index cc35d16f32..b59ff23f61 100644
--- a/src/bin/pg_basebackup/pg_recvlogical.c
+++ b/src/bin/pg_basebackup/pg_recvlogical.c
@@ -193,10 +193,7 @@ OutputFsync(TimestampTz now)
 		return true;
 
 	if (fsync(outfd) != 0)
-	{
-		pg_log_fatal("could not fsync file \"%s\": %m", outfile);
-		exit(1);
-	}
+		pg_fatal("could not fsync file \"%s\": %m", outfile);
 
 	return true;
 }
@@ -780,18 +777,12 @@ main(int argc, char **argv)
 /* replication options */
 			case 'I':
 				if (sscanf(optarg, "%X/%X", &hi, &lo) != 2)
-				{
-					pg_log_error("could not parse start position \"%s\"", optarg);
-					exit(1);
-				}
+					pg_fatal("could not parse start position \"%s\"", optarg);
 				startpos = ((uint64) hi) << 32 | lo;
 				break;
 			case 'E':
 				if (sscanf(optarg, "%X/%X", &hi, &lo) != 2)
-				{
-					pg_log_error("could not parse end position \"%s\"", optarg);
-					exit(1);
-				}
+					pg_fatal("could not parse end position \"%s\"", optarg);
 				endpos = ((uint64) hi) << 32 | lo;
 				break;
 			case 'o':
@@ -842,12 +833,8 @@ main(int argc, char **argv)
 				break;
 
 			default:
-
-				/*
-				 * getopt_long already emitted a complaint
-				 */
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-						progname);
+				/* getopt_long already emitted a complaint */
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 				exit(1);
 		}
 	}
@@ -859,8 +846,7 @@ main(int argc, char **argv)
 	{
 		pg_log_error("too many command-line arguments (first is \"%s\")",
 					 argv[optind]);
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
@@ -870,64 +856,56 @@ main(int argc, char **argv)
 	if (replication_slot == NULL)
 	{
 		pg_log_error("no slot specified");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
 	if (do_start_slot && outfile == NULL)
 	{
 		pg_log_error("no target file specified");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
 	if (!do_drop_slot && dbname == NULL)
 	{
 		pg_log_error("no database specified");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
 	if (!do_drop_slot && !do_create_slot && !do_start_slot)
 	{
 		pg_log_error("at least one action needs to be specified");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
 	if (do_drop_slot && (do_create_slot || do_start_slot))
 	{
 		pg_log_error("cannot use --create-slot or --start together with --drop-slot");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
 	if (startpos != InvalidXLogRecPtr && (do_create_slot || do_drop_slot))
 	{
 		pg_log_error("cannot use --create-slot or --drop-slot together with --startpos");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
 	if (endpos != InvalidXLogRecPtr && !do_start_slot)
 	{
 		pg_log_error("--endpos may only be specified with --start");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
 	if (two_phase && !do_create_slot)
 	{
 		pg_log_error("--two-phase may only be specified with --create-slot");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
@@ -958,10 +936,7 @@ main(int argc, char **argv)
 		exit(1);
 
 	if (db_name == NULL)
-	{
-		pg_log_error("could not establish database-specific replication connection");
-		exit(1);
-	}
+		pg_fatal("could not establish database-specific replication connection");
 
 	/*
 	 * Set umask so that directories/files are created with the same
@@ -1011,10 +986,7 @@ main(int argc, char **argv)
 			exit(0);
 		}
 		else if (noloop)
-		{
-			pg_log_error("disconnected");
-			exit(1);
-		}
+			pg_fatal("disconnected");
 		else
 		{
 			/* translator: check source for value for %d */
diff --git a/src/bin/pg_basebackup/receivelog.c b/src/bin/pg_basebackup/receivelog.c
index d39e4b11a1..42d50931d3 100644
--- a/src/bin/pg_basebackup/receivelog.c
+++ b/src/bin/pg_basebackup/receivelog.c
@@ -140,7 +140,7 @@ open_walfile(StreamCtl *stream, XLogRecPtr startpoint)
 			/* fsync file in case of a previous crash */
 			if (stream->walmethod->sync(f) != 0)
 			{
-				pg_log_fatal("could not fsync existing write-ahead log file \"%s\": %s",
+				pg_log_error("could not fsync existing write-ahead log file \"%s\": %s",
 							 fn, stream->walmethod->getlasterror());
 				stream->walmethod->close(f, CLOSE_UNLINK);
 				exit(1);
@@ -778,11 +778,8 @@ HandleCopyStream(PGconn *conn, StreamCtl *stream,
 		if (stream->synchronous && lastFlushPosition < blockpos && walfile != NULL)
 		{
 			if (stream->walmethod->sync(walfile) != 0)
-			{
-				pg_log_fatal("could not fsync file \"%s\": %s",
-							 current_walfile_name, stream->walmethod->getlasterror());
-				exit(1);
-			}
+				pg_fatal("could not fsync file \"%s\": %s",
+						 current_walfile_name, stream->walmethod->getlasterror());
 			lastFlushPosition = blockpos;
 
 			/*
@@ -1030,11 +1027,8 @@ ProcessKeepaliveMsg(PGconn *conn, StreamCtl *stream, char *copybuf, int len,
 			 * shutdown of the server.
 			 */
 			if (stream->walmethod->sync(walfile) != 0)
-			{
-				pg_log_fatal("could not fsync file \"%s\": %s",
-							 current_walfile_name, stream->walmethod->getlasterror());
-				exit(1);
-			}
+				pg_fatal("could not fsync file \"%s\": %s",
+						 current_walfile_name, stream->walmethod->getlasterror());
 			lastFlushPosition = blockpos;
 		}
 
diff --git a/src/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c
index 4a6afd1a06..86c0493a94 100644
--- a/src/bin/pg_basebackup/streamutil.c
+++ b/src/bin/pg_basebackup/streamutil.c
@@ -88,10 +88,7 @@ GetConnection(void)
 	{
 		conn_opts = PQconninfoParse(connection_string, &err_msg);
 		if (conn_opts == NULL)
-		{
-			pg_log_error("%s", err_msg);
-			exit(1);
-		}
+			pg_fatal("%s", err_msg);
 
 		for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++)
 		{
@@ -182,10 +179,7 @@ GetConnection(void)
 		 * and PQconnectdbParams returns NULL, we call exit(1) directly.
 		 */
 		if (!tmpconn)
-		{
-			pg_log_error("could not connect to server");
-			exit(1);
-		}
+			pg_fatal("could not connect to server");
 
 		/* If we need a password and -w wasn't given, loop back and get one */
 		if (PQstatus(tmpconn) == CONNECTION_BAD &&
diff --git a/src/bin/pg_basebackup/walmethods.c b/src/bin/pg_basebackup/walmethods.c
index a6d08c1270..d824bea053 100644
--- a/src/bin/pg_basebackup/walmethods.c
+++ b/src/bin/pg_basebackup/walmethods.c
@@ -1195,9 +1195,8 @@ tar_close(Walfile f, WalCloseMethod method)
 	if (tar_sync(f) < 0)
 	{
 		/* XXX this seems pretty bogus; why is only this case fatal? */
-		pg_log_fatal("could not fsync file \"%s\": %s",
-					 tf->pathname, tar_getlasterror());
-		exit(1);
+		pg_fatal("could not fsync file \"%s\": %s",
+				 tf->pathname, tar_getlasterror());
 	}
 
 	/* Clean up and done */
diff --git a/src/bin/pg_checksums/pg_checksums.c b/src/bin/pg_checksums/pg_checksums.c
index 7e69475947..22220a5b33 100644
--- a/src/bin/pg_checksums/pg_checksums.c
+++ b/src/bin/pg_checksums/pg_checksums.c
@@ -208,10 +208,7 @@ scan_file(const char *fn, int segmentno)
 	f = open(fn, PG_BINARY | flags, 0);
 
 	if (f < 0)
-	{
-		pg_log_error("could not open file \"%s\": %m", fn);
-		exit(1);
-	}
+		pg_fatal("could not open file \"%s\": %m", fn);
 
 	files_scanned++;
 
@@ -225,12 +222,11 @@ scan_file(const char *fn, int segmentno)
 		if (r != BLCKSZ)
 		{
 			if (r < 0)
-				pg_log_error("could not read block %u in file \"%s\": %m",
-							 blockno, fn);
+				pg_fatal("could not read block %u in file \"%s\": %m",
+						 blockno, fn);
 			else
-				pg_log_error("could not read block %u in file \"%s\": read %d of %d",
-							 blockno, fn, r, BLCKSZ);
-			exit(1);
+				pg_fatal("could not read block %u in file \"%s\": read %d of %d",
+						 blockno, fn, r, BLCKSZ);
 		}
 		blocks_scanned++;
 
@@ -275,22 +271,18 @@ scan_file(const char *fn, int segmentno)
 
 			/* Seek back to beginning of block */
 			if (lseek(f, -BLCKSZ, SEEK_CUR) < 0)
-			{
-				pg_log_error("seek failed for block %u in file \"%s\": %m", blockno, fn);
-				exit(1);
-			}
+				pg_fatal("seek failed for block %u in file \"%s\": %m", blockno, fn);
 
 			/* Write block with checksum */
 			w = write(f, buf.data, BLCKSZ);
 			if (w != BLCKSZ)
 			{
 				if (w < 0)
-					pg_log_error("could not write block %u in file \"%s\": %m",
-								 blockno, fn);
+					pg_fatal("could not write block %u in file \"%s\": %m",
+							 blockno, fn);
 				else
-					pg_log_error("could not write block %u in file \"%s\": wrote %d of %d",
-								 blockno, fn, w, BLCKSZ);
-				exit(1);
+					pg_fatal("could not write block %u in file \"%s\": wrote %d of %d",
+							 blockno, fn, w, BLCKSZ);
 			}
 		}
 
@@ -334,10 +326,7 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
 	snprintf(path, sizeof(path), "%s/%s", basedir, subdir);
 	dir = opendir(path);
 	if (!dir)
-	{
-		pg_log_error("could not open directory \"%s\": %m", path);
-		exit(1);
-	}
+		pg_fatal("could not open directory \"%s\": %m", path);
 	while ((de = readdir(dir)) != NULL)
 	{
 		char		fn[MAXPGPATH];
@@ -361,10 +350,7 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
 
 		snprintf(fn, sizeof(fn), "%s/%s", path, de->d_name);
 		if (lstat(fn, &st) < 0)
-		{
-			pg_log_error("could not stat file \"%s\": %m", fn);
-			exit(1);
-		}
+			pg_fatal("could not stat file \"%s\": %m", fn);
 		if (S_ISREG(st.st_mode))
 		{
 			char		fnonly[MAXPGPATH];
@@ -388,11 +374,8 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
 				*segmentpath++ = '\0';
 				segmentno = atoi(segmentpath);
 				if (segmentno == 0)
-				{
-					pg_log_error("invalid segment number %d in file name \"%s\"",
-								 segmentno, fn);
-					exit(1);
-				}
+					pg_fatal("invalid segment number %d in file name \"%s\"",
+							 segmentno, fn);
 			}
 
 			forkpath = strchr(fnonly, '_');
@@ -440,11 +423,8 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
 						 path, de->d_name, TABLESPACE_VERSION_DIRECTORY);
 
 				if (lstat(tblspc_path, &tblspc_st) < 0)
-				{
-					pg_log_error("could not stat file \"%s\": %m",
-								 tblspc_path);
-					exit(1);
-				}
+					pg_fatal("could not stat file \"%s\": %m",
+							 tblspc_path);
 
 				/*
 				 * Move backwards once as the scan needs to happen for the
@@ -539,7 +519,8 @@ main(int argc, char *argv[])
 				showprogress = true;
 				break;
 			default:
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+				/* getopt_long already emitted a complaint */
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 				exit(1);
 		}
 	}
@@ -555,7 +536,7 @@ main(int argc, char *argv[])
 		if (DataDir == NULL)
 		{
 			pg_log_error("no data directory specified");
-			fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+			pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 			exit(1);
 		}
 	}
@@ -565,8 +546,7 @@ main(int argc, char *argv[])
 	{
 		pg_log_error("too many command-line arguments (first is \"%s\")",
 					 argv[optind]);
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
@@ -574,30 +554,23 @@ main(int argc, char *argv[])
 	if (mode != PG_MODE_CHECK && only_filenode)
 	{
 		pg_log_error("option -f/--filenode can only be used with --check");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
 	/* Read the control file and check compatibility */
 	ControlFile = get_controlfile(DataDir, &crc_ok);
 	if (!crc_ok)
-	{
-		pg_log_error("pg_control CRC value is incorrect");
-		exit(1);
-	}
+		pg_fatal("pg_control CRC value is incorrect");
 
 	if (ControlFile->pg_control_version != PG_CONTROL_VERSION)
-	{
-		pg_log_error("cluster is not compatible with this version of pg_checksums");
-		exit(1);
-	}
+		pg_fatal("cluster is not compatible with this version of pg_checksums");
 
 	if (ControlFile->blcksz != BLCKSZ)
 	{
 		pg_log_error("database cluster is not compatible");
-		fprintf(stderr, _("The database cluster was initialized with block size %u, but pg_checksums was compiled with block size %u.\n"),
-				ControlFile->blcksz, BLCKSZ);
+		pg_log_error_detail("The database cluster was initialized with block size %u, but pg_checksums was compiled with block size %u.",
+							ControlFile->blcksz, BLCKSZ);
 		exit(1);
 	}
 
@@ -608,31 +581,19 @@ main(int argc, char *argv[])
 	 */
 	if (ControlFile->state != DB_SHUTDOWNED &&
 		ControlFile->state != DB_SHUTDOWNED_IN_RECOVERY)
-	{
-		pg_log_error("cluster must be shut down");
-		exit(1);
-	}
+		pg_fatal("cluster must be shut down");
 
 	if (ControlFile->data_checksum_version == 0 &&
 		mode == PG_MODE_CHECK)
-	{
-		pg_log_error("data checksums are not enabled in cluster");
-		exit(1);
-	}
+		pg_fatal("data checksums are not enabled in cluster");
 
 	if (ControlFile->data_checksum_version == 0 &&
 		mode == PG_MODE_DISABLE)
-	{
-		pg_log_error("data checksums are already disabled in cluster");
-		exit(1);
-	}
+		pg_fatal("data checksums are already disabled in cluster");
 
 	if (ControlFile->data_checksum_version > 0 &&
 		mode == PG_MODE_ENABLE)
-	{
-		pg_log_error("data checksums are already enabled in cluster");
-		exit(1);
-	}
+		pg_fatal("data checksums are already enabled in cluster");
 
 	/* Operate on all files if checking or enabling checksums */
 	if (mode == PG_MODE_CHECK || mode == PG_MODE_ENABLE)
diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c
index f911f98d94..c390ec51ce 100644
--- a/src/bin/pg_controldata/pg_controldata.c
+++ b/src/bin/pg_controldata/pg_controldata.c
@@ -134,7 +134,8 @@ main(int argc, char *argv[])
 				break;
 
 			default:
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+				/* getopt_long already emitted a complaint */
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 				exit(1);
 		}
 	}
@@ -152,15 +153,14 @@ main(int argc, char *argv[])
 	{
 		pg_log_error("too many command-line arguments (first is \"%s\")",
 					 argv[optind]);
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
 	if (DataDir == NULL)
 	{
 		pg_log_error("no data directory specified");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index b9a25442f5..794e6e7ce9 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -340,9 +340,9 @@ flagInhTables(Archive *fout, TableInfo *tblinfo, int numTables,
 
 			/* With partitions there can only be one parent */
 			if (tblinfo[i].numParents != 1)
-				fatal("invalid number of parents %d for table \"%s\"",
-					  tblinfo[i].numParents,
-					  tblinfo[i].dobj.name);
+				pg_fatal("invalid number of parents %d for table \"%s\"",
+						 tblinfo[i].numParents,
+						 tblinfo[i].dobj.name);
 
 			attachinfo = (TableAttachInfo *) palloc(sizeof(TableAttachInfo));
 			attachinfo->dobj.objType = DO_TABLE_ATTACH;
@@ -1001,13 +1001,10 @@ findParentsByOid(TableInfo *self,
 
 				parent = findTableByOid(inhinfo[i].inhparent);
 				if (parent == NULL)
-				{
-					pg_log_error("failed sanity check, parent OID %u of table \"%s\" (OID %u) not found",
-								 inhinfo[i].inhparent,
-								 self->dobj.name,
-								 oid);
-					exit_nicely(1);
-				}
+					pg_fatal("failed sanity check, parent OID %u of table \"%s\" (OID %u) not found",
+							 inhinfo[i].inhparent,
+							 self->dobj.name,
+							 oid);
 				self->parents[j++] = parent;
 			}
 		}
@@ -1043,10 +1040,7 @@ parseOidArray(const char *str, Oid *array, int arraysize)
 			if (j > 0)
 			{
 				if (argNum >= arraysize)
-				{
-					pg_log_error("could not parse numeric array \"%s\": too many numbers", str);
-					exit_nicely(1);
-				}
+					pg_fatal("could not parse numeric array \"%s\": too many numbers", str);
 				temp[j] = '\0';
 				array[argNum++] = atooid(temp);
 				j = 0;
@@ -1058,10 +1052,7 @@ parseOidArray(const char *str, Oid *array, int arraysize)
 		{
 			if (!(isdigit((unsigned char) s) || s == '-') ||
 				j >= sizeof(temp) - 1)
-			{
-				pg_log_error("could not parse numeric array \"%s\": invalid character in number", str);
-				exit_nicely(1);
-			}
+				pg_fatal("could not parse numeric array \"%s\": invalid character in number", str);
 			temp[j++] = s;
 		}
 	}
diff --git a/src/bin/pg_dump/compress_io.c b/src/bin/pg_dump/compress_io.c
index 9077fdb74d..62f940ff7a 100644
--- a/src/bin/pg_dump/compress_io.c
+++ b/src/bin/pg_dump/compress_io.c
@@ -108,7 +108,7 @@ ParseCompressionOption(int compression, CompressionAlgorithm *alg, int *level)
 		*alg = COMPR_ALG_NONE;
 	else
 	{
-		fatal("invalid compression code: %d", compression);
+		pg_fatal("invalid compression code: %d", compression);
 		*alg = COMPR_ALG_NONE;	/* keep compiler quiet */
 	}
 
@@ -131,7 +131,7 @@ AllocateCompressor(int compression, WriteFunc writeF)
 
 #ifndef HAVE_LIBZ
 	if (alg == COMPR_ALG_LIBZ)
-		fatal("not built with zlib support");
+		pg_fatal("not built with zlib support");
 #endif
 
 	cs = (CompressorState *) pg_malloc0(sizeof(CompressorState));
@@ -167,7 +167,7 @@ ReadDataFromArchive(ArchiveHandle *AH, int compression, ReadFunc readF)
 #ifdef HAVE_LIBZ
 		ReadDataFromArchiveZlib(AH, readF);
 #else
-		fatal("not built with zlib support");
+		pg_fatal("not built with zlib support");
 #endif
 	}
 }
@@ -185,7 +185,7 @@ WriteDataToArchive(ArchiveHandle *AH, CompressorState *cs,
 #ifdef HAVE_LIBZ
 			WriteDataToArchiveZlib(AH, cs, data, dLen);
 #else
-			fatal("not built with zlib support");
+			pg_fatal("not built with zlib support");
 #endif
 			break;
 		case COMPR_ALG_NONE:
@@ -233,8 +233,8 @@ InitCompressorZlib(CompressorState *cs, int level)
 	cs->zlibOutSize = ZLIB_OUT_SIZE;
 
 	if (deflateInit(zp, level) != Z_OK)
-		fatal("could not initialize compression library: %s",
-			  zp->msg);
+		pg_fatal("could not initialize compression library: %s",
+				 zp->msg);
 
 	/* Just be paranoid - maybe End is called after Start, with no Write */
 	zp->next_out = (void *) cs->zlibOut;
@@ -253,7 +253,7 @@ EndCompressorZlib(ArchiveHandle *AH, CompressorState *cs)
 	DeflateCompressorZlib(AH, cs, true);
 
 	if (deflateEnd(zp) != Z_OK)
-		fatal("could not close compression stream: %s", zp->msg);
+		pg_fatal("could not close compression stream: %s", zp->msg);
 
 	free(cs->zlibOut);
 	free(cs->zp);
@@ -270,7 +270,7 @@ DeflateCompressorZlib(ArchiveHandle *AH, CompressorState *cs, bool flush)
 	{
 		res = deflate(zp, flush ? Z_FINISH : Z_NO_FLUSH);
 		if (res == Z_STREAM_ERROR)
-			fatal("could not compress data: %s", zp->msg);
+			pg_fatal("could not compress data: %s", zp->msg);
 		if ((flush && (zp->avail_out < cs->zlibOutSize))
 			|| (zp->avail_out == 0)
 			|| (zp->avail_in != 0)
@@ -330,8 +330,8 @@ ReadDataFromArchiveZlib(ArchiveHandle *AH, ReadFunc readF)
 	out = pg_malloc(ZLIB_OUT_SIZE + 1);
 
 	if (inflateInit(zp) != Z_OK)
-		fatal("could not initialize compression library: %s",
-			  zp->msg);
+		pg_fatal("could not initialize compression library: %s",
+				 zp->msg);
 
 	/* no minimal chunk size for zlib */
 	while ((cnt = readF(AH, &buf, &buflen)))
@@ -346,7 +346,7 @@ ReadDataFromArchiveZlib(ArchiveHandle *AH, ReadFunc readF)
 
 			res = inflate(zp, 0);
 			if (res != Z_OK && res != Z_STREAM_END)
-				fatal("could not uncompress data: %s", zp->msg);
+				pg_fatal("could not uncompress data: %s", zp->msg);
 
 			out[ZLIB_OUT_SIZE - zp->avail_out] = '\0';
 			ahwrite(out, 1, ZLIB_OUT_SIZE - zp->avail_out, AH);
@@ -361,14 +361,14 @@ ReadDataFromArchiveZlib(ArchiveHandle *AH, ReadFunc readF)
 		zp->avail_out = ZLIB_OUT_SIZE;
 		res = inflate(zp, 0);
 		if (res != Z_OK && res != Z_STREAM_END)
-			fatal("could not uncompress data: %s", zp->msg);
+			pg_fatal("could not uncompress data: %s", zp->msg);
 
 		out[ZLIB_OUT_SIZE - zp->avail_out] = '\0';
 		ahwrite(out, 1, ZLIB_OUT_SIZE - zp->avail_out, AH);
 	}
 
 	if (inflateEnd(zp) != Z_OK)
-		fatal("could not close compression library: %s", zp->msg);
+		pg_fatal("could not close compression library: %s", zp->msg);
 
 	free(buf);
 	free(out);
@@ -501,7 +501,7 @@ cfopen_write(const char *path, const char *mode, int compression)
 		fp = cfopen(fname, mode, compression);
 		free_keep_errno(fname);
 #else
-		fatal("not built with zlib support");
+		pg_fatal("not built with zlib support");
 		fp = NULL;				/* keep compiler quiet */
 #endif
 	}
@@ -544,7 +544,7 @@ cfopen(const char *path, const char *mode, int compression)
 			fp = NULL;
 		}
 #else
-		fatal("not built with zlib support");
+		pg_fatal("not built with zlib support");
 #endif
 	}
 	else
@@ -581,8 +581,8 @@ cfread(void *ptr, int size, cfp *fp)
 			int			errnum;
 			const char *errmsg = gzerror(fp->compressedfp, &errnum);
 
-			fatal("could not read from input file: %s",
-				  errnum == Z_ERRNO ? strerror(errno) : errmsg);
+			pg_fatal("could not read from input file: %s",
+					 errnum == Z_ERRNO ? strerror(errno) : errmsg);
 		}
 	}
 	else
@@ -618,9 +618,9 @@ cfgetc(cfp *fp)
 		if (ret == EOF)
 		{
 			if (!gzeof(fp->compressedfp))
-				fatal("could not read from input file: %s", strerror(errno));
+				pg_fatal("could not read from input file: %s", strerror(errno));
 			else
-				fatal("could not read from input file: end of file");
+				pg_fatal("could not read from input file: end of file");
 		}
 	}
 	else
diff --git a/src/bin/pg_dump/nls.mk b/src/bin/pg_dump/nls.mk
index 6276fd443b..220d1ec75f 100644
--- a/src/bin/pg_dump/nls.mk
+++ b/src/bin/pg_dump/nls.mk
@@ -11,8 +11,7 @@ GETTEXT_FILES    = $(FRONTEND_COMMON_GETTEXT_FILES) \
                    ../../common/exec.c ../../common/fe_memutils.c \
                    ../../common/wait_error.c
 GETTEXT_TRIGGERS = $(FRONTEND_COMMON_GETTEXT_TRIGGERS) \
-                   fatal simple_prompt \
+                   simple_prompt \
                    ExecuteSqlCommand:3 warn_or_exit_horribly:2
 GETTEXT_FLAGS    = $(FRONTEND_COMMON_GETTEXT_FLAGS) \
-    fatal:1:c-format \
     warn_or_exit_horribly:2:c-format
diff --git a/src/bin/pg_dump/parallel.c b/src/bin/pg_dump/parallel.c
index bc5251be82..c9f6b86bb0 100644
--- a/src/bin/pg_dump/parallel.c
+++ b/src/bin/pg_dump/parallel.c
@@ -250,10 +250,7 @@ init_parallel_dump_utils(void)
 		/* Initialize socket access */
 		err = WSAStartup(MAKEWORD(2, 2), &wsaData);
 		if (err != 0)
-		{
-			pg_log_error("%s() failed: error code %d", "WSAStartup", err);
-			exit_nicely(1);
-		}
+			pg_fatal("%s() failed: error code %d", "WSAStartup", err);
 
 		parallel_init_done = true;
 	}
@@ -393,7 +390,7 @@ archive_close_connection(int code, void *arg)
  *
  * Note that we don't expect to come here during normal exit (the workers
  * should be long gone, and the ParallelState too).  We're only here in a
- * fatal() situation, so intervening to cancel active commands is
+ * pg_fatal() situation, so intervening to cancel active commands is
  * appropriate.
  */
 static void
@@ -961,7 +958,7 @@ ParallelBackupStart(ArchiveHandle *AH)
 
 		/* Create communication pipes for this worker */
 		if (pgpipe(pipeMW) < 0 || pgpipe(pipeWM) < 0)
-			fatal("could not create communication channels: %m");
+			pg_fatal("could not create communication channels: %m");
 
 		/* leader's ends of the pipes */
 		slot->pipeRead = pipeWM[PIPE_READ];
@@ -1018,7 +1015,7 @@ ParallelBackupStart(ArchiveHandle *AH)
 		else if (pid < 0)
 		{
 			/* fork failed */
-			fatal("could not create worker process: %m");
+			pg_fatal("could not create worker process: %m");
 		}
 
 		/* In Leader after successful fork */
@@ -1148,8 +1145,8 @@ parseWorkerCommand(ArchiveHandle *AH, TocEntry **te, T_Action *act,
 		Assert(*te != NULL);
 	}
 	else
-		fatal("unrecognized command received from leader: \"%s\"",
-			  msg);
+		pg_fatal("unrecognized command received from leader: \"%s\"",
+				 msg);
 }
 
 /*
@@ -1191,8 +1188,8 @@ parseWorkerResponse(ArchiveHandle *AH, TocEntry *te,
 		AH->public.n_errors += n_errors;
 	}
 	else
-		fatal("invalid message received from worker: \"%s\"",
-			  msg);
+		pg_fatal("invalid message received from worker: \"%s\"",
+				 msg);
 
 	return status;
 }
@@ -1323,10 +1320,10 @@ lockTableForWorker(ArchiveHandle *AH, TocEntry *te)
 	res = PQexec(AH->connection, query->data);
 
 	if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
-		fatal("could not obtain lock on relation \"%s\"\n"
-			  "This usually means that someone requested an ACCESS EXCLUSIVE lock "
-			  "on the table after the pg_dump parent process had gotten the "
-			  "initial ACCESS SHARE lock on the table.", qualId);
+		pg_fatal("could not obtain lock on relation \"%s\"\n"
+				 "This usually means that someone requested an ACCESS EXCLUSIVE lock "
+				 "on the table after the pg_dump parent process had gotten the "
+				 "initial ACCESS SHARE lock on the table.", qualId);
 
 	PQclear(res);
 	destroyPQExpBuffer(query);
@@ -1412,7 +1409,7 @@ ListenToWorkers(ArchiveHandle *AH, ParallelState *pstate, bool do_wait)
 	{
 		/* If do_wait is true, we must have detected EOF on some socket */
 		if (do_wait)
-			fatal("a worker process died unexpectedly");
+			pg_fatal("a worker process died unexpectedly");
 		return false;
 	}
 
@@ -1429,8 +1426,8 @@ ListenToWorkers(ArchiveHandle *AH, ParallelState *pstate, bool do_wait)
 		pstate->te[worker] = NULL;
 	}
 	else
-		fatal("invalid message received from worker: \"%s\"",
-			  msg);
+		pg_fatal("invalid message received from worker: \"%s\"",
+				 msg);
 
 	/* Free the string returned from getMessageFromWorker */
 	free(msg);
@@ -1534,7 +1531,7 @@ sendMessageToLeader(int pipefd[2], const char *str)
 	int			len = strlen(str) + 1;
 
 	if (pipewrite(pipefd[PIPE_WRITE], str, len) != len)
-		fatal("could not write to the communication channel: %m");
+		pg_fatal("could not write to the communication channel: %m");
 }
 
 /*
@@ -1611,7 +1608,7 @@ getMessageFromWorker(ParallelState *pstate, bool do_wait, int *worker)
 	}
 
 	if (i < 0)
-		fatal("%s() failed: %m", "select");
+		pg_fatal("%s() failed: %m", "select");
 
 	for (i = 0; i < pstate->numWorkers; i++)
 	{
@@ -1652,7 +1649,7 @@ sendMessageToWorker(ParallelState *pstate, int worker, const char *str)
 
 	if (pipewrite(pstate->parallelSlot[worker].pipeWrite, str, len) != len)
 	{
-		fatal("could not write to the communication channel: %m");
+		pg_fatal("could not write to the communication channel: %m");
 	}
 }
 
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index d41a99d6ea..24e42fa5d7 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -276,7 +276,7 @@ CloseArchive(Archive *AHX)
 		res = fclose(AH->OF);
 
 	if (res != 0)
-		fatal("could not close output file: %m");
+		pg_fatal("could not close output file: %m");
 }
 
 /* Public */
@@ -330,8 +330,8 @@ ProcessArchiveRestoreOptions(Archive *AHX)
 					/* ok no matter which section we were in */
 					break;
 				default:
-					fatal("unexpected section code %d",
-						  (int) te->section);
+					pg_fatal("unexpected section code %d",
+							 (int) te->section);
 					break;
 			}
 		}
@@ -367,11 +367,11 @@ RestoreArchive(Archive *AHX)
 	{
 		/* We haven't got round to making this work for all archive formats */
 		if (AH->ClonePtr == NULL || AH->ReopenPtr == NULL)
-			fatal("parallel restore is not supported with this archive file format");
+			pg_fatal("parallel restore is not supported with this archive file format");
 
 		/* Doesn't work if the archive represents dependencies as OIDs */
 		if (AH->version < K_VERS_1_8)
-			fatal("parallel restore is not supported with archives made by pre-8.0 pg_dump");
+			pg_fatal("parallel restore is not supported with archives made by pre-8.0 pg_dump");
 
 		/*
 		 * It's also not gonna work if we can't reopen the input file, so
@@ -389,7 +389,7 @@ RestoreArchive(Archive *AHX)
 		for (te = AH->toc->next; te != AH->toc; te = te->next)
 		{
 			if (te->hadDumper && (te->reqs & REQ_DATA) != 0)
-				fatal("cannot restore from compressed archive (compression not supported in this installation)");
+				pg_fatal("cannot restore from compressed archive (compression not supported in this installation)");
 		}
 	}
 #endif
@@ -408,7 +408,7 @@ RestoreArchive(Archive *AHX)
 	{
 		pg_log_info("connecting to database for restore");
 		if (AH->version < K_VERS_1_3)
-			fatal("direct database connections are not supported in pre-1.3 archives");
+			pg_fatal("direct database connections are not supported in pre-1.3 archives");
 
 		/*
 		 * We don't want to guess at whether the dump will successfully
@@ -1037,7 +1037,7 @@ WriteData(Archive *AHX, const void *data, size_t dLen)
 	ArchiveHandle *AH = (ArchiveHandle *) AHX;
 
 	if (!AH->currToc)
-		fatal("internal error -- WriteData cannot be called outside the context of a DataDumper routine");
+		pg_fatal("internal error -- WriteData cannot be called outside the context of a DataDumper routine");
 
 	AH->WriteDataPtr(AH, data, dLen);
 }
@@ -1220,7 +1220,7 @@ StartBlob(Archive *AHX, Oid oid)
 	ArchiveHandle *AH = (ArchiveHandle *) AHX;
 
 	if (!AH->StartBlobPtr)
-		fatal("large-object output not supported in chosen format");
+		pg_fatal("large-object output not supported in chosen format");
 
 	AH->StartBlobPtr(AH, AH->currToc, oid);
 
@@ -1311,13 +1311,13 @@ StartRestoreBlob(ArchiveHandle *AH, Oid oid, bool drop)
 		{
 			loOid = lo_create(AH->connection, oid);
 			if (loOid == 0 || loOid != oid)
-				fatal("could not create large object %u: %s",
-					  oid, PQerrorMessage(AH->connection));
+				pg_fatal("could not create large object %u: %s",
+						 oid, PQerrorMessage(AH->connection));
 		}
 		AH->loFd = lo_open(AH->connection, oid, INV_WRITE);
 		if (AH->loFd == -1)
-			fatal("could not open large object %u: %s",
-				  oid, PQerrorMessage(AH->connection));
+			pg_fatal("could not open large object %u: %s",
+					 oid, PQerrorMessage(AH->connection));
 	}
 	else
 	{
@@ -1372,7 +1372,7 @@ SortTocFromFile(Archive *AHX)
 	/* Setup the file */
 	fh = fopen(ropt->tocFile, PG_BINARY_R);
 	if (!fh)
-		fatal("could not open TOC file \"%s\": %m", ropt->tocFile);
+		pg_fatal("could not open TOC file \"%s\": %m", ropt->tocFile);
 
 	initStringInfo(&linebuf);
 
@@ -1407,8 +1407,8 @@ SortTocFromFile(Archive *AHX)
 		/* Find TOC entry */
 		te = getTocEntryByDumpId(AH, id);
 		if (!te)
-			fatal("could not find entry for ID %d",
-				  id);
+			pg_fatal("could not find entry for ID %d",
+					 id);
 
 		/* Mark it wanted */
 		ropt->idWanted[id - 1] = true;
@@ -1430,7 +1430,7 @@ SortTocFromFile(Archive *AHX)
 	pg_free(linebuf.data);
 
 	if (fclose(fh) != 0)
-		fatal("could not close TOC file: %m");
+		pg_fatal("could not close TOC file: %m");
 }
 
 /**********************
@@ -1544,9 +1544,9 @@ SetOutput(ArchiveHandle *AH, const char *filename, int compression)
 	if (!AH->OF)
 	{
 		if (filename)
-			fatal("could not open output file \"%s\": %m", filename);
+			pg_fatal("could not open output file \"%s\": %m", filename);
 		else
-			fatal("could not open output file: %m");
+			pg_fatal("could not open output file: %m");
 	}
 }
 
@@ -1573,7 +1573,7 @@ RestoreOutput(ArchiveHandle *AH, OutputContext savedContext)
 		res = fclose(AH->OF);
 
 	if (res != 0)
-		fatal("could not close output file: %m");
+		pg_fatal("could not close output file: %m");
 
 	AH->gzOut = savedContext.gzOut;
 	AH->OF = savedContext.OF;
@@ -1736,34 +1736,34 @@ warn_or_exit_horribly(ArchiveHandle *AH, const char *fmt,...)
 
 		case STAGE_INITIALIZING:
 			if (AH->stage != AH->lastErrorStage)
-				pg_log_generic(PG_LOG_INFO, "while INITIALIZING:");
+				pg_log_info("while INITIALIZING:");
 			break;
 
 		case STAGE_PROCESSING:
 			if (AH->stage != AH->lastErrorStage)
-				pg_log_generic(PG_LOG_INFO, "while PROCESSING TOC:");
+				pg_log_info("while PROCESSING TOC:");
 			break;
 
 		case STAGE_FINALIZING:
 			if (AH->stage != AH->lastErrorStage)
-				pg_log_generic(PG_LOG_INFO, "while FINALIZING:");
+				pg_log_info("while FINALIZING:");
 			break;
 	}
 	if (AH->currentTE != NULL && AH->currentTE != AH->lastErrorTE)
 	{
-		pg_log_generic(PG_LOG_INFO, "from TOC entry %d; %u %u %s %s %s",
-					   AH->currentTE->dumpId,
-					   AH->currentTE->catalogId.tableoid,
-					   AH->currentTE->catalogId.oid,
-					   AH->currentTE->desc ? AH->currentTE->desc : "(no desc)",
-					   AH->currentTE->tag ? AH->currentTE->tag : "(no tag)",
-					   AH->currentTE->owner ? AH->currentTE->owner : "(no owner)");
+		pg_log_info("from TOC entry %d; %u %u %s %s %s",
+					AH->currentTE->dumpId,
+					AH->currentTE->catalogId.tableoid,
+					AH->currentTE->catalogId.oid,
+					AH->currentTE->desc ? AH->currentTE->desc : "(no desc)",
+					AH->currentTE->tag ? AH->currentTE->tag : "(no tag)",
+					AH->currentTE->owner ? AH->currentTE->owner : "(no owner)");
 	}
 	AH->lastErrorStage = AH->stage;
 	AH->lastErrorTE = AH->currentTE;
 
 	va_start(ap, fmt);
-	pg_log_generic_v(PG_LOG_ERROR, fmt, ap);
+	pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, ap);
 	va_end(ap);
 
 	if (AH->public.exit_on_error)
@@ -1827,7 +1827,7 @@ buildTocEntryArrays(ArchiveHandle *AH)
 	{
 		/* this check is purely paranoia, maxDumpId should be correct */
 		if (te->dumpId <= 0 || te->dumpId > maxDumpId)
-			fatal("bad dumpId");
+			pg_fatal("bad dumpId");
 
 		/* tocsByDumpId indexes all TOCs by their dump ID */
 		AH->tocsByDumpId[te->dumpId] = te;
@@ -1848,7 +1848,7 @@ buildTocEntryArrays(ArchiveHandle *AH)
 			 * item's dump ID, so there should be a place for it in the array.
 			 */
 			if (tableId <= 0 || tableId > maxDumpId)
-				fatal("bad table dumpId for TABLE DATA item");
+				pg_fatal("bad table dumpId for TABLE DATA item");
 
 			AH->tableDataId[tableId] = te->dumpId;
 		}
@@ -1940,7 +1940,7 @@ ReadOffset(ArchiveHandle *AH, pgoff_t * o)
 			break;
 
 		default:
-			fatal("unexpected data offset flag %d", offsetFlg);
+			pg_fatal("unexpected data offset flag %d", offsetFlg);
 	}
 
 	/*
@@ -1953,7 +1953,7 @@ ReadOffset(ArchiveHandle *AH, pgoff_t * o)
 		else
 		{
 			if (AH->ReadBytePtr(AH) != 0)
-				fatal("file offset in dump file is too large");
+				pg_fatal("file offset in dump file is too large");
 		}
 	}
 
@@ -2091,8 +2091,8 @@ _discoverArchiveFormat(ArchiveHandle *AH)
 			char		buf[MAXPGPATH];
 
 			if (snprintf(buf, MAXPGPATH, "%s/toc.dat", AH->fSpec) >= MAXPGPATH)
-				fatal("directory name too long: \"%s\"",
-					  AH->fSpec);
+				pg_fatal("directory name too long: \"%s\"",
+						 AH->fSpec);
 			if (stat(buf, &st) == 0 && S_ISREG(st.st_mode))
 			{
 				AH->format = archDirectory;
@@ -2101,39 +2101,39 @@ _discoverArchiveFormat(ArchiveHandle *AH)
 
 #ifdef HAVE_LIBZ
 			if (snprintf(buf, MAXPGPATH, "%s/toc.dat.gz", AH->fSpec) >= MAXPGPATH)
-				fatal("directory name too long: \"%s\"",
-					  AH->fSpec);
+				pg_fatal("directory name too long: \"%s\"",
+						 AH->fSpec);
 			if (stat(buf, &st) == 0 && S_ISREG(st.st_mode))
 			{
 				AH->format = archDirectory;
 				return AH->format;
 			}
 #endif
-			fatal("directory \"%s\" does not appear to be a valid archive (\"toc.dat\" does not exist)",
-				  AH->fSpec);
+			pg_fatal("directory \"%s\" does not appear to be a valid archive (\"toc.dat\" does not exist)",
+					 AH->fSpec);
 			fh = NULL;			/* keep compiler quiet */
 		}
 		else
 		{
 			fh = fopen(AH->fSpec, PG_BINARY_R);
 			if (!fh)
-				fatal("could not open input file \"%s\": %m", AH->fSpec);
+				pg_fatal("could not open input file \"%s\": %m", AH->fSpec);
 		}
 	}
 	else
 	{
 		fh = stdin;
 		if (!fh)
-			fatal("could not open input file: %m");
+			pg_fatal("could not open input file: %m");
 	}
 
 	if ((cnt = fread(sig, 1, 5, fh)) != 5)
 	{
 		if (ferror(fh))
-			fatal("could not read input file: %m");
+			pg_fatal("could not read input file: %m");
 		else
-			fatal("input file is too short (read %lu, expected 5)",
-				  (unsigned long) cnt);
+			pg_fatal("input file is too short (read %lu, expected 5)",
+					 (unsigned long) cnt);
 	}
 
 	/* Save it, just in case we need it later */
@@ -2164,19 +2164,19 @@ _discoverArchiveFormat(ArchiveHandle *AH)
 			 * looks like it's probably a text format dump. so suggest they
 			 * try psql
 			 */
-			fatal("input file appears to be a text format dump. Please use psql.");
+			pg_fatal("input file appears to be a text format dump. Please use psql.");
 		}
 
 		if (AH->lookaheadLen != 512)
 		{
 			if (feof(fh))
-				fatal("input file does not appear to be a valid archive (too short?)");
+				pg_fatal("input file does not appear to be a valid archive (too short?)");
 			else
 				READ_ERROR_EXIT(fh);
 		}
 
 		if (!isValidTarHeader(AH->lookahead))
-			fatal("input file does not appear to be a valid archive");
+			pg_fatal("input file does not appear to be a valid archive");
 
 		AH->format = archTar;
 	}
@@ -2185,7 +2185,7 @@ _discoverArchiveFormat(ArchiveHandle *AH)
 	if (wantClose)
 	{
 		if (fclose(fh) != 0)
-			fatal("could not close input file: %m");
+			pg_fatal("could not close input file: %m");
 		/* Forget lookahead, since we'll re-read header after re-opening */
 		AH->readHeader = 0;
 		AH->lookaheadLen = 0;
@@ -2302,7 +2302,7 @@ _allocAH(const char *FileSpec, const ArchiveFormat fmt,
 			break;
 
 		default:
-			fatal("unrecognized file format \"%d\"", fmt);
+			pg_fatal("unrecognized file format \"%d\"", fmt);
 	}
 
 	return AH;
@@ -2388,8 +2388,8 @@ mark_dump_job_done(ArchiveHandle *AH,
 				te->dumpId, te->desc, te->tag);
 
 	if (status != 0)
-		fatal("worker process failed: exit code %d",
-			  status);
+		pg_fatal("worker process failed: exit code %d",
+				 status);
 }

@@ -2509,8 +2509,8 @@ ReadToc(ArchiveHandle *AH)
 
 		/* Sanity check */
 		if (te->dumpId <= 0)
-			fatal("entry ID %d out of range -- perhaps a corrupt TOC",
-				  te->dumpId);
+			pg_fatal("entry ID %d out of range -- perhaps a corrupt TOC",
+					 te->dumpId);
 
 		te->hadDumper = ReadInt(AH);
 
@@ -2671,13 +2671,13 @@ processEncodingEntry(ArchiveHandle *AH, TocEntry *te)
 		*ptr2 = '\0';
 		encoding = pg_char_to_encoding(ptr1);
 		if (encoding < 0)
-			fatal("unrecognized encoding \"%s\"",
-				  ptr1);
+			pg_fatal("unrecognized encoding \"%s\"",
+					 ptr1);
 		AH->public.encoding = encoding;
 	}
 	else
-		fatal("invalid ENCODING item: %s",
-			  te->defn);
+		pg_fatal("invalid ENCODING item: %s",
+				 te->defn);
 
 	free(defn);
 }
@@ -2694,8 +2694,8 @@ processStdStringsEntry(ArchiveHandle *AH, TocEntry *te)
 	else if (ptr1 && strncmp(ptr1, "'off'", 5) == 0)
 		AH->public.std_strings = false;
 	else
-		fatal("invalid STDSTRINGS item: %s",
-			  te->defn);
+		pg_fatal("invalid STDSTRINGS item: %s",
+				 te->defn);
 }
 
 static void
@@ -2719,35 +2719,35 @@ StrictNamesCheck(RestoreOptions *ropt)
 	{
 		missing_name = simple_string_list_not_touched(&ropt->schemaNames);
 		if (missing_name != NULL)
-			fatal("schema \"%s\" not found", missing_name);
+			pg_fatal("schema \"%s\" not found", missing_name);
 	}
 
 	if (ropt->tableNames.head != NULL)
 	{
 		missing_name = simple_string_list_not_touched(&ropt->tableNames);
 		if (missing_name != NULL)
-			fatal("table \"%s\" not found", missing_name);
+			pg_fatal("table \"%s\" not found", missing_name);
 	}
 
 	if (ropt->indexNames.head != NULL)
 	{
 		missing_name = simple_string_list_not_touched(&ropt->indexNames);
 		if (missing_name != NULL)
-			fatal("index \"%s\" not found", missing_name);
+			pg_fatal("index \"%s\" not found", missing_name);
 	}
 
 	if (ropt->functionNames.head != NULL)
 	{
 		missing_name = simple_string_list_not_touched(&ropt->functionNames);
 		if (missing_name != NULL)
-			fatal("function \"%s\" not found", missing_name);
+			pg_fatal("function \"%s\" not found", missing_name);
 	}
 
 	if (ropt->triggerNames.head != NULL)
 	{
 		missing_name = simple_string_list_not_touched(&ropt->triggerNames);
 		if (missing_name != NULL)
-			fatal("trigger \"%s\" not found", missing_name);
+			pg_fatal("trigger \"%s\" not found", missing_name);
 	}
 }
 
@@ -3140,8 +3140,8 @@ _doSetSessionAuth(ArchiveHandle *AH, const char *user)
 
 		if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
 			/* NOT warn_or_exit_horribly... use -O instead to skip this. */
-			fatal("could not set session user to \"%s\": %s",
-				  user, PQerrorMessage(AH->connection));
+			pg_fatal("could not set session user to \"%s\": %s",
+					 user, PQerrorMessage(AH->connection));
 
 		PQclear(res);
 	}
@@ -3751,7 +3751,7 @@ ReadHead(ArchiveHandle *AH)
 		AH->ReadBufPtr(AH, tmpMag, 5);
 
 		if (strncmp(tmpMag, "PGDMP", 5) != 0)
-			fatal("did not find magic string in file header");
+			pg_fatal("did not find magic string in file header");
 	}
 
 	vmaj = AH->ReadBytePtr(AH);
@@ -3765,13 +3765,13 @@ ReadHead(ArchiveHandle *AH)
 	AH->version = MAKE_ARCHIVE_VERSION(vmaj, vmin, vrev);
 
 	if (AH->version < K_VERS_1_0 || AH->version > K_VERS_MAX)
-		fatal("unsupported version (%d.%d) in file header",
-			  vmaj, vmin);
+		pg_fatal("unsupported version (%d.%d) in file header",
+				 vmaj, vmin);
 
 	AH->intSize = AH->ReadBytePtr(AH);
 	if (AH->intSize > 32)
-		fatal("sanity check on integer size (%lu) failed",
-			  (unsigned long) AH->intSize);
+		pg_fatal("sanity check on integer size (%lu) failed",
+				 (unsigned long) AH->intSize);
 
 	if (AH->intSize > sizeof(int))
 		pg_log_warning("archive was made on a machine with larger integers, some operations might fail");
@@ -3784,8 +3784,8 @@ ReadHead(ArchiveHandle *AH)
 	fmt = AH->ReadBytePtr(AH);
 
 	if (AH->format != fmt)
-		fatal("expected format (%d) differs from format found in file (%d)",
-			  AH->format, fmt);
+		pg_fatal("expected format (%d) differs from format found in file (%d)",
+				 AH->format, fmt);
 
 	if (AH->version >= K_VERS_1_2)
 	{
@@ -4455,8 +4455,8 @@ mark_restore_job_done(ArchiveHandle *AH,
 	else if (status == WORKER_IGNORED_ERRORS)
 		AH->public.n_errors++;
 	else if (status != 0)
-		fatal("worker process failed: exit code %d",
-			  status);
+		pg_fatal("worker process failed: exit code %d",
+				 status);
 
 	reduce_dependencies(AH, te, ready_list);
 }
diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h
index 540d4f6a83..084cd87e8d 100644
--- a/src/bin/pg_dump/pg_backup_archiver.h
+++ b/src/bin/pg_dump/pg_backup_archiver.h
@@ -121,14 +121,14 @@ struct ParallelState;
 #define READ_ERROR_EXIT(fd) \
 	do { \
 		if (feof(fd)) \
-			fatal("could not read from input file: end of file"); \
+			pg_fatal("could not read from input file: end of file"); \
 		else \
-			fatal("could not read from input file: %m"); \
+			pg_fatal("could not read from input file: %m"); \
 	} while (0)
 
 #define WRITE_ERROR_EXIT \
 	do { \
-		fatal("could not write to output file: %m"); \
+		pg_fatal("could not write to output file: %m"); \
 	} while (0)
 
 typedef enum T_Action
diff --git a/src/bin/pg_dump/pg_backup_custom.c b/src/bin/pg_dump/pg_backup_custom.c
index 77d402c323..c3b9c365d5 100644
--- a/src/bin/pg_dump/pg_backup_custom.c
+++ b/src/bin/pg_dump/pg_backup_custom.c
@@ -153,13 +153,13 @@ InitArchiveFmt_Custom(ArchiveHandle *AH)
 		{
 			AH->FH = fopen(AH->fSpec, PG_BINARY_W);
 			if (!AH->FH)
-				fatal("could not open output file \"%s\": %m", AH->fSpec);
+				pg_fatal("could not open output file \"%s\": %m", AH->fSpec);
 		}
 		else
 		{
 			AH->FH = stdout;
 			if (!AH->FH)
-				fatal("could not open output file: %m");
+				pg_fatal("could not open output file: %m");
 		}
 
 		ctx->hasSeek = checkSeek(AH->FH);
@@ -170,13 +170,13 @@ InitArchiveFmt_Custom(ArchiveHandle *AH)
 		{
 			AH->FH = fopen(AH->fSpec, PG_BINARY_R);
 			if (!AH->FH)
-				fatal("could not open input file \"%s\": %m", AH->fSpec);
+				pg_fatal("could not open input file \"%s\": %m", AH->fSpec);
 		}
 		else
 		{
 			AH->FH = stdin;
 			if (!AH->FH)
-				fatal("could not open input file: %m");
+				pg_fatal("could not open input file: %m");
 		}
 
 		ctx->hasSeek = checkSeek(AH->FH);
@@ -373,7 +373,7 @@ _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
 	lclContext *ctx = (lclContext *) AH->formatData;
 
 	if (oid == 0)
-		fatal("invalid OID for large object");
+		pg_fatal("invalid OID for large object");
 
 	WriteInt(AH, oid);
 
@@ -436,7 +436,7 @@ _PrintTocData(ArchiveHandle *AH, TocEntry *te)
 		if (ctx->hasSeek)
 		{
 			if (fseeko(AH->FH, ctx->lastFilePos, SEEK_SET) != 0)
-				fatal("error during file seek: %m");
+				pg_fatal("error during file seek: %m");
 		}
 
 		for (;;)
@@ -492,8 +492,8 @@ _PrintTocData(ArchiveHandle *AH, TocEntry *te)
 					break;
 
 				default:		/* Always have a default */
-					fatal("unrecognized data block type (%d) while searching archive",
-						  blkType);
+					pg_fatal("unrecognized data block type (%d) while searching archive",
+							 blkType);
 					break;
 			}
 		}
@@ -502,7 +502,7 @@ _PrintTocData(ArchiveHandle *AH, TocEntry *te)
 	{
 		/* We can just seek to the place we need to be. */
 		if (fseeko(AH->FH, tctx->dataPos, SEEK_SET) != 0)
-			fatal("error during file seek: %m");
+			pg_fatal("error during file seek: %m");
 
 		_readBlockHeader(AH, &blkType, &id);
 	}
@@ -514,20 +514,20 @@ _PrintTocData(ArchiveHandle *AH, TocEntry *te)
 	if (blkType == EOF)
 	{
 		if (!ctx->hasSeek)
-			fatal("could not find block ID %d in archive -- "
-				  "possibly due to out-of-order restore request, "
-				  "which cannot be handled due to non-seekable input file",
-				  te->dumpId);
+			pg_fatal("could not find block ID %d in archive -- "
+					 "possibly due to out-of-order restore request, "
+					 "which cannot be handled due to non-seekable input file",
+					 te->dumpId);
 		else
-			fatal("could not find block ID %d in archive -- "
-				  "possibly corrupt archive",
-				  te->dumpId);
+			pg_fatal("could not find block ID %d in archive -- "
+					 "possibly corrupt archive",
+					 te->dumpId);
 	}
 
 	/* Are we sane? */
 	if (id != te->dumpId)
-		fatal("found unexpected block ID (%d) when reading data -- expected %d",
-			  id, te->dumpId);
+		pg_fatal("found unexpected block ID (%d) when reading data -- expected %d",
+				 id, te->dumpId);
 
 	switch (blkType)
 	{
@@ -540,8 +540,8 @@ _PrintTocData(ArchiveHandle *AH, TocEntry *te)
 			break;
 
 		default:				/* Always have a default */
-			fatal("unrecognized data block type %d while restoring archive",
-				  blkType);
+			pg_fatal("unrecognized data block type %d while restoring archive",
+					 blkType);
 			break;
 	}
 
@@ -626,7 +626,7 @@ _skipData(ArchiveHandle *AH)
 		if (ctx->hasSeek)
 		{
 			if (fseeko(AH->FH, blkLen, SEEK_CUR) != 0)
-				fatal("error during file seek: %m");
+				pg_fatal("error during file seek: %m");
 		}
 		else
 		{
@@ -640,9 +640,9 @@ _skipData(ArchiveHandle *AH)
 			if (fread(buf, 1, blkLen, AH->FH) != blkLen)
 			{
 				if (feof(AH->FH))
-					fatal("could not read from input file: end of file");
+					pg_fatal("could not read from input file: end of file");
 				else
-					fatal("could not read from input file: %m");
+					pg_fatal("could not read from input file: %m");
 			}
 		}
 
@@ -743,7 +743,7 @@ _CloseArchive(ArchiveHandle *AH)
 		/* Remember TOC's seek position for use below */
 		tpos = ftello(AH->FH);
 		if (tpos < 0 && ctx->hasSeek)
-			fatal("could not determine seek position in archive file: %m");
+			pg_fatal("could not determine seek position in archive file: %m");
 		WriteToc(AH);
 		WriteDataChunks(AH, NULL);
 
@@ -759,7 +759,7 @@ _CloseArchive(ArchiveHandle *AH)
 	}
 
 	if (fclose(AH->FH) != 0)
-		fatal("could not close archive file: %m");
+		pg_fatal("could not close archive file: %m");
 
 	/* Sync the output file if one is defined */
 	if (AH->dosync && AH->mode == archModeWrite && AH->fSpec)
@@ -782,32 +782,32 @@ _ReopenArchive(ArchiveHandle *AH)
 	pgoff_t		tpos;
 
 	if (AH->mode == archModeWrite)
-		fatal("can only reopen input archives");
+		pg_fatal("can only reopen input archives");
 
 	/*
 	 * These two cases are user-facing errors since they represent unsupported
 	 * (but not invalid) use-cases.  Word the error messages appropriately.
 	 */
 	if (AH->fSpec == NULL || strcmp(AH->fSpec, "") == 0)
-		fatal("parallel restore from standard input is not supported");
+		pg_fatal("parallel restore from standard input is not supported");
 	if (!ctx->hasSeek)
-		fatal("parallel restore from non-seekable file is not supported");
+		pg_fatal("parallel restore from non-seekable file is not supported");
 
 	tpos = ftello(AH->FH);
 	if (tpos < 0)
-		fatal("could not determine seek position in archive file: %m");
+		pg_fatal("could not determine seek position in archive file: %m");
 
 #ifndef WIN32
 	if (fclose(AH->FH) != 0)
-		fatal("could not close archive file: %m");
+		pg_fatal("could not close archive file: %m");
 #endif
 
 	AH->FH = fopen(AH->fSpec, PG_BINARY_R);
 	if (!AH->FH)
-		fatal("could not open input file \"%s\": %m", AH->fSpec);
+		pg_fatal("could not open input file \"%s\": %m", AH->fSpec);
 
 	if (fseeko(AH->FH, tpos, SEEK_SET) != 0)
-		fatal("could not set seek position in archive file: %m");
+		pg_fatal("could not set seek position in archive file: %m");
 }
 
 /*
@@ -862,7 +862,7 @@ _PrepParallelRestore(ArchiveHandle *AH)
 		pgoff_t		endpos;
 
 		if (fseeko(AH->FH, 0, SEEK_END) != 0)
-			fatal("error during file seek: %m");
+			pg_fatal("error during file seek: %m");
 		endpos = ftello(AH->FH);
 		if (endpos > prev_tctx->dataPos)
 			prev_te->dataLength = endpos - prev_tctx->dataPos;
@@ -886,7 +886,7 @@ _Clone(ArchiveHandle *AH)
 
 	/* sanity check, shouldn't happen */
 	if (ctx->cs != NULL)
-		fatal("compressor active");
+		pg_fatal("compressor active");
 
 	/*
 	 * We intentionally do not clone TOC-entry-local state: it's useful to
@@ -940,7 +940,7 @@ _getFilePos(ArchiveHandle *AH, lclContext *ctx)
 	{
 		/* Not expected if we found we can seek. */
 		if (ctx->hasSeek)
-			fatal("could not determine seek position in archive file: %m");
+			pg_fatal("could not determine seek position in archive file: %m");
 	}
 	return pos;
 }
@@ -956,7 +956,7 @@ _readBlockHeader(ArchiveHandle *AH, int *type, int *id)
 	int			byt;
 
 	/*
-	 * Note: if we are at EOF with a pre-1.3 input file, we'll fatal() inside
+	 * Note: if we are at EOF with a pre-1.3 input file, we'll pg_fatal() inside
 	 * ReadInt rather than returning EOF.  It doesn't seem worth jumping
 	 * through hoops to deal with that case better, because no such files are
 	 * likely to exist in the wild: only some 7.1 development versions of
diff --git a/src/bin/pg_dump/pg_backup_db.c b/src/bin/pg_dump/pg_backup_db.c
index 3184eda3e7..89cdbf80e0 100644
--- a/src/bin/pg_dump/pg_backup_db.c
+++ b/src/bin/pg_dump/pg_backup_db.c
@@ -39,7 +39,7 @@ _check_database_version(ArchiveHandle *AH)
 	remoteversion_str = PQparameterStatus(AH->connection, "server_version");
 	remoteversion = PQserverVersion(AH->connection);
 	if (remoteversion == 0 || !remoteversion_str)
-		fatal("could not get server_version from libpq");
+		pg_fatal("could not get server_version from libpq");
 
 	AH->public.remoteVersionStr = pg_strdup(remoteversion_str);
 	AH->public.remoteVersion = remoteversion;
@@ -50,9 +50,10 @@ _check_database_version(ArchiveHandle *AH)
 		&& (remoteversion < AH->public.minRemoteVersion ||
 			remoteversion > AH->public.maxRemoteVersion))
 	{
-		pg_log_error("server version: %s; %s version: %s",
-					 remoteversion_str, progname, PG_VERSION);
-		fatal("aborting because of server version mismatch");
+		pg_log_error("aborting because of server version mismatch");
+		pg_log_error_detail("server version: %s; %s version: %s",
+							remoteversion_str, progname, PG_VERSION);
+		exit(1);
 	}
 
 	/*
@@ -116,7 +117,7 @@ ConnectDatabase(Archive *AHX,
 	bool		new_pass;
 
 	if (AH->connection)
-		fatal("already connected to a database");
+		pg_fatal("already connected to a database");
 
 	/* Never prompt for a password during a reconnection */
 	prompt_password = isReconnect ? TRI_NO : cparams->promptPassword;
@@ -166,7 +167,7 @@ ConnectDatabase(Archive *AHX,
 		AH->connection = PQconnectdbParams(keywords, values, true);
 
 		if (!AH->connection)
-			fatal("could not connect to database");
+			pg_fatal("could not connect to database");
 
 		if (PQstatus(AH->connection) == CONNECTION_BAD &&
 			PQconnectionNeedsPassword(AH->connection) &&
@@ -183,11 +184,11 @@ ConnectDatabase(Archive *AHX,
 	if (PQstatus(AH->connection) == CONNECTION_BAD)
 	{
 		if (isReconnect)
-			fatal("reconnection failed: %s",
-				  PQerrorMessage(AH->connection));
+			pg_fatal("reconnection failed: %s",
+					 PQerrorMessage(AH->connection));
 		else
-			fatal("%s",
-				  PQerrorMessage(AH->connection));
+			pg_fatal("%s",
+					 PQerrorMessage(AH->connection));
 	}
 
 	/* Start strict; later phases may override this. */
@@ -235,7 +236,7 @@ DisconnectDatabase(Archive *AHX)
 		/*
 		 * If we have an active query, send a cancel before closing, ignoring
 		 * any errors.  This is of no use for a normal exit, but might be
-		 * helpful during fatal().
+		 * helpful during pg_fatal().
 		 */
 		if (PQtransactionStatus(AH->connection) == PQTRANS_ACTIVE)
 			(void) PQcancel(AH->connCancel, errbuf, sizeof(errbuf));
@@ -261,16 +262,17 @@ GetConnection(Archive *AHX)
 static void
 notice_processor(void *arg, const char *message)
 {
-	pg_log_generic(PG_LOG_INFO, "%s", message);
+	pg_log_info("%s", message);
 }
 
-/* Like fatal(), but with a complaint about a particular query. */
+/* Like pg_fatal(), but with a complaint about a particular query. */
 static void
 die_on_query_failure(ArchiveHandle *AH, const char *query)
 {
 	pg_log_error("query failed: %s",
 				 PQerrorMessage(AH->connection));
-	fatal("query was: %s", query);
+	pg_log_error_detail("Query was: %s", query);
+	exit(1);
 }
 
 void
@@ -311,10 +313,10 @@ ExecuteSqlQueryForSingleRow(Archive *fout, const char *query)
 	/* Expecting a single result only */
 	ntups = PQntuples(res);
 	if (ntups != 1)
-		fatal(ngettext("query returned %d row instead of one: %s",
-					   "query returned %d rows instead of one: %s",
-					   ntups),
-			  ntups, query);
+		pg_fatal(ngettext("query returned %d row instead of one: %s",
+						  "query returned %d rows instead of one: %s",
+						  ntups),
+				 ntups, query);
 
 	return res;
 }
@@ -456,8 +458,8 @@ ExecuteSqlCommandBuf(Archive *AHX, const char *buf, size_t bufLen)
 		 */
 		if (AH->pgCopyIn &&
 			PQputCopyData(AH->connection, buf, bufLen) <= 0)
-			fatal("error returned by PQputCopyData: %s",
-				  PQerrorMessage(AH->connection));
+			pg_fatal("error returned by PQputCopyData: %s",
+					 PQerrorMessage(AH->connection));
 	}
 	else if (AH->outputKind == OUTPUT_OTHERDATA)
 	{
@@ -505,8 +507,8 @@ EndDBCopyMode(Archive *AHX, const char *tocEntryTag)
 		PGresult   *res;
 
 		if (PQputCopyEnd(AH->connection, NULL) <= 0)
-			fatal("error returned by PQputCopyEnd: %s",
-				  PQerrorMessage(AH->connection));
+			pg_fatal("error returned by PQputCopyEnd: %s",
+					 PQerrorMessage(AH->connection));
 
 		/* Check command status and return to normal libpq state */
 		res = PQgetResult(AH->connection);
diff --git a/src/bin/pg_dump/pg_backup_directory.c b/src/bin/pg_dump/pg_backup_directory.c
index 7f4e340dea..3f46f7988a 100644
--- a/src/bin/pg_dump/pg_backup_directory.c
+++ b/src/bin/pg_dump/pg_backup_directory.c
@@ -153,7 +153,7 @@ InitArchiveFmt_Directory(ArchiveHandle *AH)
 	 */
 
 	if (!AH->fSpec || strcmp(AH->fSpec, "") == 0)
-		fatal("no output directory specified");
+		pg_fatal("no output directory specified");
 
 	ctx->directory = AH->fSpec;
 
@@ -182,18 +182,18 @@ InitArchiveFmt_Directory(ArchiveHandle *AH)
 				}
 
 				if (errno)
-					fatal("could not read directory \"%s\": %m",
-						  ctx->directory);
+					pg_fatal("could not read directory \"%s\": %m",
+							 ctx->directory);
 
 				if (closedir(dir))
-					fatal("could not close directory \"%s\": %m",
-						  ctx->directory);
+					pg_fatal("could not close directory \"%s\": %m",
+							 ctx->directory);
 			}
 		}
 
 		if (!is_empty && mkdir(ctx->directory, 0700) < 0)
-			fatal("could not create directory \"%s\": %m",
-				  ctx->directory);
+			pg_fatal("could not create directory \"%s\": %m",
+					 ctx->directory);
 	}
 	else
 	{							/* Read Mode */
@@ -204,7 +204,7 @@ InitArchiveFmt_Directory(ArchiveHandle *AH)
 
 		tocFH = cfopen_read(fname, PG_BINARY_R);
 		if (tocFH == NULL)
-			fatal("could not open input file \"%s\": %m", fname);
+			pg_fatal("could not open input file \"%s\": %m", fname);
 
 		ctx->dataFH = tocFH;
 
@@ -219,7 +219,7 @@ InitArchiveFmt_Directory(ArchiveHandle *AH)
 
 		/* Nothing else in the file, so close it again... */
 		if (cfclose(tocFH) != 0)
-			fatal("could not close TOC file: %m");
+			pg_fatal("could not close TOC file: %m");
 		ctx->dataFH = NULL;
 	}
 }
@@ -329,7 +329,7 @@ _StartData(ArchiveHandle *AH, TocEntry *te)
 
 	ctx->dataFH = cfopen_write(fname, PG_BINARY_W, AH->compression);
 	if (ctx->dataFH == NULL)
-		fatal("could not open output file \"%s\": %m", fname);
+		pg_fatal("could not open output file \"%s\": %m", fname);
 }
 
 /*
@@ -352,8 +352,8 @@ _WriteData(ArchiveHandle *AH, const void *data, size_t dLen)
 		/* if write didn't set errno, assume problem is no disk space */
 		if (errno == 0)
 			errno = ENOSPC;
-		fatal("could not write to output file: %s",
-			  get_cfp_error(ctx->dataFH));
+		pg_fatal("could not write to output file: %s",
+				 get_cfp_error(ctx->dataFH));
 	}
 }
 
@@ -370,7 +370,7 @@ _EndData(ArchiveHandle *AH, TocEntry *te)
 
 	/* Close the file */
 	if (cfclose(ctx->dataFH) != 0)
-		fatal("could not close data file: %m");
+		pg_fatal("could not close data file: %m");
 
 	ctx->dataFH = NULL;
 }
@@ -392,7 +392,7 @@ _PrintFileData(ArchiveHandle *AH, char *filename)
 	cfp = cfopen_read(filename, PG_BINARY_R);
 
 	if (!cfp)
-		fatal("could not open input file \"%s\": %m", filename);
+		pg_fatal("could not open input file \"%s\": %m", filename);
 
 	buf = pg_malloc(ZLIB_OUT_SIZE);
 	buflen = ZLIB_OUT_SIZE;
@@ -404,7 +404,7 @@ _PrintFileData(ArchiveHandle *AH, char *filename)
 
 	free(buf);
 	if (cfclose(cfp) != 0)
-		fatal("could not close data file \"%s\": %m", filename);
+		pg_fatal("could not close data file \"%s\": %m", filename);
 }
 
 /*
@@ -444,8 +444,8 @@ _LoadBlobs(ArchiveHandle *AH)
 	ctx->blobsTocFH = cfopen_read(tocfname, PG_BINARY_R);
 
 	if (ctx->blobsTocFH == NULL)
-		fatal("could not open large object TOC file \"%s\" for input: %m",
-			  tocfname);
+		pg_fatal("could not open large object TOC file \"%s\" for input: %m",
+				 tocfname);
 
 	/* Read the blobs TOC file line-by-line, and process each blob */
 	while ((cfgets(ctx->blobsTocFH, line, MAXPGPATH)) != NULL)
@@ -455,8 +455,8 @@ _LoadBlobs(ArchiveHandle *AH)
 
 		/* Can't overflow because line and blobfname are the same length */
 		if (sscanf(line, "%u %" CppAsString2(MAXPGPATH) "s\n", &oid, blobfname) != 2)
-			fatal("invalid line in large object TOC file \"%s\": \"%s\"",
-				  tocfname, line);
+			pg_fatal("invalid line in large object TOC file \"%s\": \"%s\"",
+					 tocfname, line);
 
 		StartRestoreBlob(AH, oid, AH->public.ropt->dropSchema);
 		snprintf(path, MAXPGPATH, "%s/%s", ctx->directory, blobfname);
@@ -464,12 +464,12 @@ _LoadBlobs(ArchiveHandle *AH)
 		EndRestoreBlob(AH, oid);
 	}
 	if (!cfeof(ctx->blobsTocFH))
-		fatal("error reading large object TOC file \"%s\"",
-			  tocfname);
+		pg_fatal("error reading large object TOC file \"%s\"",
+				 tocfname);
 
 	if (cfclose(ctx->blobsTocFH) != 0)
-		fatal("could not close large object TOC file \"%s\": %m",
-			  tocfname);
+		pg_fatal("could not close large object TOC file \"%s\": %m",
+				 tocfname);
 
 	ctx->blobsTocFH = NULL;
 
@@ -494,8 +494,8 @@ _WriteByte(ArchiveHandle *AH, const int i)
 		/* if write didn't set errno, assume problem is no disk space */
 		if (errno == 0)
 			errno = ENOSPC;
-		fatal("could not write to output file: %s",
-			  get_cfp_error(ctx->dataFH));
+		pg_fatal("could not write to output file: %s",
+				 get_cfp_error(ctx->dataFH));
 	}
 
 	return 1;
@@ -530,8 +530,8 @@ _WriteBuf(ArchiveHandle *AH, const void *buf, size_t len)
 		/* if write didn't set errno, assume problem is no disk space */
 		if (errno == 0)
 			errno = ENOSPC;
-		fatal("could not write to output file: %s",
-			  get_cfp_error(ctx->dataFH));
+		pg_fatal("could not write to output file: %s",
+				 get_cfp_error(ctx->dataFH));
 	}
 }
 
@@ -550,7 +550,7 @@ _ReadBuf(ArchiveHandle *AH, void *buf, size_t len)
 	 * exit on short reads.
 	 */
 	if (cfread(buf, len, ctx->dataFH) != len)
-		fatal("could not read from input file: end of file");
+		pg_fatal("could not read from input file: end of file");
 }
 
 /*
@@ -583,7 +583,7 @@ _CloseArchive(ArchiveHandle *AH)
 		/* The TOC is always created uncompressed */
 		tocFH = cfopen_write(fname, PG_BINARY_W, 0);
 		if (tocFH == NULL)
-			fatal("could not open output file \"%s\": %m", fname);
+			pg_fatal("could not open output file \"%s\": %m", fname);
 		ctx->dataFH = tocFH;
 
 		/*
@@ -596,7 +596,7 @@ _CloseArchive(ArchiveHandle *AH)
 		AH->format = archDirectory;
 		WriteToc(AH);
 		if (cfclose(tocFH) != 0)
-			fatal("could not close TOC file: %m");
+			pg_fatal("could not close TOC file: %m");
 		WriteDataChunks(AH, ctx->pstate);
 
 		ParallelBackupEnd(AH, ctx->pstate);
@@ -646,7 +646,7 @@ _StartBlobs(ArchiveHandle *AH, TocEntry *te)
 	/* The blob TOC file is never compressed */
 	ctx->blobsTocFH = cfopen_write(fname, "ab", 0);
 	if (ctx->blobsTocFH == NULL)
-		fatal("could not open output file \"%s\": %m", fname);
+		pg_fatal("could not open output file \"%s\": %m", fname);
 }
 
 /*
@@ -665,7 +665,7 @@ _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
 	ctx->dataFH = cfopen_write(fname, PG_BINARY_W, AH->compression);
 
 	if (ctx->dataFH == NULL)
-		fatal("could not open output file \"%s\": %m", fname);
+		pg_fatal("could not open output file \"%s\": %m", fname);
 }
 
 /*
@@ -682,13 +682,13 @@ _EndBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
 
 	/* Close the BLOB data file itself */
 	if (cfclose(ctx->dataFH) != 0)
-		fatal("could not close blob data file: %m");
+		pg_fatal("could not close blob data file: %m");
 	ctx->dataFH = NULL;
 
 	/* register the blob in blobs.toc */
 	len = snprintf(buf, sizeof(buf), "%u blob_%u.dat\n", oid, oid);
 	if (cfwrite(buf, len, ctx->blobsTocFH) != len)
-		fatal("could not write to blobs TOC file");
+		pg_fatal("could not write to blobs TOC file");
 }
 
 /*
@@ -702,7 +702,7 @@ _EndBlobs(ArchiveHandle *AH, TocEntry *te)
 	lclContext *ctx = (lclContext *) AH->formatData;
 
 	if (cfclose(ctx->blobsTocFH) != 0)
-		fatal("could not close blobs TOC file: %m");
+		pg_fatal("could not close blobs TOC file: %m");
 	ctx->blobsTocFH = NULL;
 }
 
@@ -721,7 +721,7 @@ setFilePath(ArchiveHandle *AH, char *buf, const char *relativeFilename)
 	dname = ctx->directory;
 
 	if (strlen(dname) + 1 + strlen(relativeFilename) + 1 > MAXPGPATH)
-		fatal("file name too long: \"%s\"", dname);
+		pg_fatal("file name too long: \"%s\"", dname);
 
 	strcpy(buf, dname);
 	strcat(buf, "/");
diff --git a/src/bin/pg_dump/pg_backup_null.c b/src/bin/pg_dump/pg_backup_null.c
index 0458979f3c..541306d991 100644
--- a/src/bin/pg_dump/pg_backup_null.c
+++ b/src/bin/pg_dump/pg_backup_null.c
@@ -71,7 +71,7 @@ InitArchiveFmt_Null(ArchiveHandle *AH)
 	 * Now prevent reading...
 	 */
 	if (AH->mode == archModeRead)
-		fatal("this format cannot be read");
+		pg_fatal("this format cannot be read");
 }
 
 /*
@@ -144,7 +144,7 @@ _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
 	bool		old_blob_style = (AH->version < K_VERS_1_12);
 
 	if (oid == 0)
-		fatal("invalid OID for large object");
+		pg_fatal("invalid OID for large object");
 
 	/* With an old archive we must do drop and create logic here */
 	if (old_blob_style && AH->public.ropt->dropSchema)
diff --git a/src/bin/pg_dump/pg_backup_tar.c b/src/bin/pg_dump/pg_backup_tar.c
index 5c351acda0..dd83222f16 100644
--- a/src/bin/pg_dump/pg_backup_tar.c
+++ b/src/bin/pg_dump/pg_backup_tar.c
@@ -174,14 +174,14 @@ InitArchiveFmt_Tar(ArchiveHandle *AH)
 		{
 			ctx->tarFH = fopen(AH->fSpec, PG_BINARY_W);
 			if (ctx->tarFH == NULL)
-				fatal("could not open TOC file \"%s\" for output: %m",
-					  AH->fSpec);
+				pg_fatal("could not open TOC file \"%s\" for output: %m",
+						 AH->fSpec);
 		}
 		else
 		{
 			ctx->tarFH = stdout;
 			if (ctx->tarFH == NULL)
-				fatal("could not open TOC file for output: %m");
+				pg_fatal("could not open TOC file for output: %m");
 		}
 
 		ctx->tarFHpos = 0;
@@ -200,7 +200,7 @@ InitArchiveFmt_Tar(ArchiveHandle *AH)
 		 * positioning.
 		 */
 		if (AH->compression != 0)
-			fatal("compression is not supported by tar archive format");
+			pg_fatal("compression is not supported by tar archive format");
 	}
 	else
 	{							/* Read Mode */
@@ -208,14 +208,14 @@ InitArchiveFmt_Tar(ArchiveHandle *AH)
 		{
 			ctx->tarFH = fopen(AH->fSpec, PG_BINARY_R);
 			if (ctx->tarFH == NULL)
-				fatal("could not open TOC file \"%s\" for input: %m",
-					  AH->fSpec);
+				pg_fatal("could not open TOC file \"%s\" for input: %m",
+						 AH->fSpec);
 		}
 		else
 		{
 			ctx->tarFH = stdin;
 			if (ctx->tarFH == NULL)
-				fatal("could not open TOC file for input: %m");
+				pg_fatal("could not open TOC file for input: %m");
 		}
 
 		/*
@@ -335,7 +335,7 @@ tarOpen(ArchiveHandle *AH, const char *filename, char mode)
 				 * Couldn't find the requested file. Future: do SEEK(0) and
 				 * retry.
 				 */
-				fatal("could not find file \"%s\" in archive", filename);
+				pg_fatal("could not find file \"%s\" in archive", filename);
 			}
 			else
 			{
@@ -349,7 +349,7 @@ tarOpen(ArchiveHandle *AH, const char *filename, char mode)
 		if (AH->compression == 0)
 			tm->nFH = ctx->tarFH;
 		else
-			fatal("compression is not supported by tar archive format");
+			pg_fatal("compression is not supported by tar archive format");
 		/* tm->zFH = gzdopen(dup(fileno(ctx->tarFH)), "rb"); */
 #else
 		tm->nFH = ctx->tarFH;
@@ -401,7 +401,7 @@ tarOpen(ArchiveHandle *AH, const char *filename, char mode)
 #endif
 
 		if (tm->tmpFH == NULL)
-			fatal("could not generate temporary file name: %m");
+			pg_fatal("could not generate temporary file name: %m");
 
 		umask(old_umask);
 
@@ -412,7 +412,7 @@ tarOpen(ArchiveHandle *AH, const char *filename, char mode)
 			sprintf(fmode, "wb%d", AH->compression);
 			tm->zFH = gzdopen(dup(fileno(tm->tmpFH)), fmode);
 			if (tm->zFH == NULL)
-				fatal("could not open temporary file");
+				pg_fatal("could not open temporary file");
 		}
 		else
 			tm->nFH = tm->tmpFH;
@@ -441,7 +441,7 @@ tarClose(ArchiveHandle *AH, TAR_MEMBER *th)
 	{
 		errno = 0;				/* in case gzclose() doesn't set it */
 		if (GZCLOSE(th->zFH) != 0)
-			fatal("could not close tar member: %m");
+			pg_fatal("could not close tar member: %m");
 	}
 
 	if (th->mode == 'w')
@@ -551,11 +551,11 @@ _tarReadRaw(ArchiveHandle *AH, void *buf, size_t len, TAR_MEMBER *th, FILE *fh)
 					int			errnum;
 					const char *errmsg = gzerror(th->zFH, &errnum);
 
-					fatal("could not read from input file: %s",
-						  errnum == Z_ERRNO ? strerror(errno) : errmsg);
+					pg_fatal("could not read from input file: %s",
+							 errnum == Z_ERRNO ? strerror(errno) : errmsg);
 #else
-					fatal("could not read from input file: %s",
-						  strerror(errno));
+					pg_fatal("could not read from input file: %s",
+							 strerror(errno));
 #endif
 				}
 			}
@@ -685,8 +685,8 @@ _PrintTocData(ArchiveHandle *AH, TocEntry *te)
 			pos1 = (int) strlen(te->copyStmt) - 13;
 			if (pos1 < 6 || strncmp(te->copyStmt, "COPY ", 5) != 0 ||
 				strcmp(te->copyStmt + pos1, " FROM stdin;\n") != 0)
-				fatal("unexpected COPY statement syntax: \"%s\"",
-					  te->copyStmt);
+				pg_fatal("unexpected COPY statement syntax: \"%s\"",
+						 te->copyStmt);
 
 			/* Emit all but the FROM part ... */
 			ahwrite(te->copyStmt, 1, pos1, AH);
@@ -787,7 +787,7 @@ _ReadByte(ArchiveHandle *AH)
 	res = tarRead(&c, 1, ctx->FH);
 	if (res != 1)
 		/* We already would have exited for errors on reads, must be EOF */
-		fatal("could not read from input file: end of file");
+		pg_fatal("could not read from input file: end of file");
 	ctx->filePos += 1;
 	return c;
 }
@@ -810,7 +810,7 @@ _ReadBuf(ArchiveHandle *AH, void *buf, size_t len)
 
 	if (tarRead(buf, len, ctx->FH) != len)
 		/* We already would have exited for errors on reads, must be EOF */
-		fatal("could not read from input file: end of file");
+		pg_fatal("could not read from input file: end of file");
 
 	ctx->filePos += len;
 }
@@ -952,7 +952,7 @@ _StartBlob(ArchiveHandle *AH, TocEntry *te, Oid oid)
 	char	   *sfx;
 
 	if (oid == 0)
-		fatal("invalid OID for large object (%u)", oid);
+		pg_fatal("invalid OID for large object (%u)", oid);
 
 	if (AH->compression != 0)
 		sfx = ".gz";
@@ -1080,12 +1080,12 @@ _tarAddFile(ArchiveHandle *AH, TAR_MEMBER *th)
 	 * Find file len & go back to start.
 	 */
 	if (fseeko(tmp, 0, SEEK_END) != 0)
-		fatal("error during file seek: %m");
+		pg_fatal("error during file seek: %m");
 	th->fileLen = ftello(tmp);
 	if (th->fileLen < 0)
-		fatal("could not determine seek position in archive file: %m");
+		pg_fatal("could not determine seek position in archive file: %m");
 	if (fseeko(tmp, 0, SEEK_SET) != 0)
-		fatal("error during file seek: %m");
+		pg_fatal("error during file seek: %m");
 
 	_tarWriteHeader(th);
 
@@ -1099,7 +1099,7 @@ _tarAddFile(ArchiveHandle *AH, TAR_MEMBER *th)
 		READ_ERROR_EXIT(tmp);
 
 	if (fclose(tmp) != 0)		/* This *should* delete it... */
-		fatal("could not close temporary file: %m");
+		pg_fatal("could not close temporary file: %m");
 
 	if (len != th->fileLen)
 	{
@@ -1108,8 +1108,8 @@ _tarAddFile(ArchiveHandle *AH, TAR_MEMBER *th)
 
 		snprintf(buf1, sizeof(buf1), INT64_FORMAT, (int64) len);
 		snprintf(buf2, sizeof(buf2), INT64_FORMAT, (int64) th->fileLen);
-		fatal("actual file length (%s) does not match expected (%s)",
-			  buf1, buf2);
+		pg_fatal("actual file length (%s) does not match expected (%s)",
+				 buf1, buf2);
 	}
 
 	pad = tarPaddingBytesRequired(len);
@@ -1165,7 +1165,7 @@ _tarPositionTo(ArchiveHandle *AH, const char *filename)
 	if (!_tarGetHeader(AH, th))
 	{
 		if (filename)
-			fatal("could not find header for file \"%s\" in tar archive", filename);
+			pg_fatal("could not find header for file \"%s\" in tar archive", filename);
 		else
 		{
 			/*
@@ -1183,9 +1183,9 @@ _tarPositionTo(ArchiveHandle *AH, const char *filename)
 
 		id = atoi(th->targetFile);
 		if ((TocIDRequired(AH, id) & REQ_DATA) != 0)
-			fatal("restoring data out of order is not supported in this archive format: "
-				  "\"%s\" is required, but comes before \"%s\" in the archive file.",
-				  th->targetFile, filename);
+			pg_fatal("restoring data out of order is not supported in this archive format: "
+					 "\"%s\" is required, but comes before \"%s\" in the archive file.",
+					 th->targetFile, filename);
 
 		/* Header doesn't match, so read to next header */
 		len = th->fileLen;
@@ -1196,7 +1196,7 @@ _tarPositionTo(ArchiveHandle *AH, const char *filename)
 			_tarReadRaw(AH, &header[0], TAR_BLOCK_SIZE, NULL, ctx->tarFH);
 
 		if (!_tarGetHeader(AH, th))
-			fatal("could not find header for file \"%s\" in tar archive", filename);
+			pg_fatal("could not find header for file \"%s\" in tar archive", filename);
 	}
 
 	ctx->tarNextMember = ctx->tarFHpos + th->fileLen
@@ -1230,10 +1230,10 @@ _tarGetHeader(ArchiveHandle *AH, TAR_MEMBER *th)
 			return 0;
 
 		if (len != TAR_BLOCK_SIZE)
-			fatal(ngettext("incomplete tar header found (%lu byte)",
-						   "incomplete tar header found (%lu bytes)",
-						   len),
-				  (unsigned long) len);
+			pg_fatal(ngettext("incomplete tar header found (%lu byte)",
+							  "incomplete tar header found (%lu bytes)",
+							  len),
+					 (unsigned long) len);
 
 		/* Calc checksum */
 		chk = tarChecksum(h);
@@ -1281,8 +1281,8 @@ _tarGetHeader(ArchiveHandle *AH, TAR_MEMBER *th)
 
 		snprintf(posbuf, sizeof(posbuf), UINT64_FORMAT,
 				 (uint64) ftello(ctx->tarFH));
-		fatal("corrupt tar header found in %s (expected %d, computed %d) file position %s",
-			  tag, sum, chk, posbuf);
+		pg_fatal("corrupt tar header found in %s (expected %d, computed %d) file position %s",
+				 tag, sum, chk, posbuf);
 	}
 
 	th->targetFile = pg_strdup(tag);
diff --git a/src/bin/pg_dump/pg_backup_utils.c b/src/bin/pg_dump/pg_backup_utils.c
index 57140a5504..e40890cb26 100644
--- a/src/bin/pg_dump/pg_backup_utils.c
+++ b/src/bin/pg_dump/pg_backup_utils.c
@@ -52,8 +52,7 @@ set_dump_section(const char *arg, int *dumpSections)
 	else
 	{
 		pg_log_error("unrecognized section name: \"%s\"", arg);
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit_nicely(1);
 	}
 }
@@ -64,10 +63,7 @@ void
 on_exit_nicely(on_exit_nicely_callback function, void *arg)
 {
 	if (on_exit_nicely_index >= MAX_ON_EXIT_NICELY)
-	{
-		pg_log_fatal("out of on_exit_nicely slots");
-		exit_nicely(1);
-	}
+		pg_fatal("out of on_exit_nicely slots");
 	on_exit_nicely_list[on_exit_nicely_index].function = function;
 	on_exit_nicely_list[on_exit_nicely_index].arg = arg;
 	on_exit_nicely_index++;
diff --git a/src/bin/pg_dump/pg_backup_utils.h b/src/bin/pg_dump/pg_backup_utils.h
index 6ebc3afee4..5b1c51554d 100644
--- a/src/bin/pg_dump/pg_backup_utils.h
+++ b/src/bin/pg_dump/pg_backup_utils.h
@@ -31,6 +31,12 @@ extern void set_dump_section(const char *arg, int *dumpSections);
 extern void on_exit_nicely(on_exit_nicely_callback function, void *arg);
 extern void exit_nicely(int code) pg_attribute_noreturn();
 
-#define fatal(...) do { pg_log_error(__VA_ARGS__); exit_nicely(1); } while(0)
+/* In pg_dump, we modify pg_fatal to call exit_nicely instead of exit */
+#undef pg_fatal
+#define pg_fatal(...) do { \
+		if (likely(__pg_log_level <= PG_LOG_ERROR)) \
+			pg_log_generic(PG_LOG_ERROR, PG_LOG_PRIMARY, __VA_ARGS__); \
+		exit_nicely(1); \
+	} while(0)
 
 #endif							/* PG_BACKUP_UTILS_H */
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index e69dcf8a48..0e83def6b4 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -620,7 +620,8 @@ main(int argc, char **argv)
 				break;
 
 			default:
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+				/* getopt_long already emitted a complaint */
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 				exit_nicely(1);
 		}
 	}
@@ -637,8 +638,7 @@ main(int argc, char **argv)
 	{
 		pg_log_error("too many command-line arguments (first is \"%s\")",
 					 argv[optind]);
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit_nicely(1);
 	}
 
@@ -655,32 +655,26 @@ main(int argc, char **argv)
 		dopt.sequence_data = 1;
 
 	if (dopt.dataOnly && dopt.schemaOnly)
-	{
-		pg_log_error("options -s/--schema-only and -a/--data-only cannot be used together");
-		exit_nicely(1);
-	}
+		pg_fatal("options -s/--schema-only and -a/--data-only cannot be used together");
 
 	if (dopt.schemaOnly && foreign_servers_include_patterns.head != NULL)
-		fatal("options -s/--schema-only and --include-foreign-data cannot be used together");
+		pg_fatal("options -s/--schema-only and --include-foreign-data cannot be used together");
 
 	if (numWorkers > 1 && foreign_servers_include_patterns.head != NULL)
-		fatal("option --include-foreign-data is not supported with parallel backup");
+		pg_fatal("option --include-foreign-data is not supported with parallel backup");
 
 	if (dopt.dataOnly && dopt.outputClean)
-	{
-		pg_log_error("options -c/--clean and -a/--data-only cannot be used together");
-		exit_nicely(1);
-	}
+		pg_fatal("options -c/--clean and -a/--data-only cannot be used together");
 
 	if (dopt.if_exists && !dopt.outputClean)
-		fatal("option --if-exists requires option -c/--clean");
+		pg_fatal("option --if-exists requires option -c/--clean");
 
 	/*
 	 * --inserts are already implied above if --column-inserts or
 	 * --rows-per-insert were specified.
 	 */
 	if (dopt.do_nothing && dopt.dump_inserts == 0)
-		fatal("option --on-conflict-do-nothing requires option --inserts, --rows-per-insert, or --column-inserts");
+		pg_fatal("option --on-conflict-do-nothing requires option --inserts, --rows-per-insert, or --column-inserts");
 
 	/* Identify archive format to emit */
 	archiveFormat = parseArchiveFormat(format, &archiveMode);
@@ -715,7 +709,7 @@ main(int argc, char **argv)
 
 	/* Parallel backup only in the directory archive format so far */
 	if (archiveFormat != archDirectory && numWorkers > 1)
-		fatal("parallel backup only supported by the directory format");
+		pg_fatal("parallel backup only supported by the directory format");
 
 	/* Open the output file */
 	fout = CreateArchive(filename, archiveFormat, compressLevel, dosync,
@@ -770,7 +764,7 @@ main(int argc, char **argv)
 									&schema_include_oids,
 									strict_names);
 		if (schema_include_oids.head == NULL)
-			fatal("no matching schemas were found");
+			pg_fatal("no matching schemas were found");
 	}
 	expand_schema_name_patterns(fout, &schema_exclude_patterns,
 								&schema_exclude_oids,
@@ -784,7 +778,7 @@ main(int argc, char **argv)
 								   &table_include_oids,
 								   strict_names);
 		if (table_include_oids.head == NULL)
-			fatal("no matching tables were found");
+			pg_fatal("no matching tables were found");
 	}
 	expand_table_name_patterns(fout, &table_exclude_patterns,
 							   &table_exclude_oids,
@@ -806,7 +800,7 @@ main(int argc, char **argv)
 									   &extension_include_oids,
 									   strict_names);
 		if (extension_include_oids.head == NULL)
-			fatal("no matching extensions were found");
+			pg_fatal("no matching extensions were found");
 	}
 
 	/*
@@ -1087,8 +1081,8 @@ setup_connection(Archive *AH, const char *dumpencoding,
 	if (dumpencoding)
 	{
 		if (PQsetClientEncoding(conn, dumpencoding) < 0)
-			fatal("invalid client encoding \"%s\" specified",
-				  dumpencoding);
+			pg_fatal("invalid client encoding \"%s\" specified",
+					 dumpencoding);
 	}
 
 	/*
@@ -1225,7 +1219,7 @@ setup_connection(Archive *AH, const char *dumpencoding,
 	else if (AH->numWorkers > 1)
 	{
 		if (AH->isStandby && AH->remoteVersion < 100000)
-			fatal("parallel dumps from standby servers are not supported by this server version");
+			pg_fatal("parallel dumps from standby servers are not supported by this server version");
 		AH->sync_snapshot_id = get_synchronized_snapshot(AH);
 	}
 }
@@ -1290,7 +1284,7 @@ parseArchiveFormat(const char *format, ArchiveMode *mode)
 	else if (pg_strcasecmp(format, "tar") == 0)
 		archiveFormat = archTar;
 	else
-		fatal("invalid output format \"%s\" specified", format);
+		pg_fatal("invalid output format \"%s\" specified", format);
 	return archiveFormat;
 }
 
@@ -1328,7 +1322,7 @@ expand_schema_name_patterns(Archive *fout,
 
 		res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 		if (strict_names && PQntuples(res) == 0)
-			fatal("no matching schemas were found for pattern \"%s\"", cell->val);
+			pg_fatal("no matching schemas were found for pattern \"%s\"", cell->val);
 
 		for (i = 0; i < PQntuples(res); i++)
 		{
@@ -1375,7 +1369,7 @@ expand_extension_name_patterns(Archive *fout,
 
 		res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 		if (strict_names && PQntuples(res) == 0)
-			fatal("no matching extensions were found for pattern \"%s\"", cell->val);
+			pg_fatal("no matching extensions were found for pattern \"%s\"", cell->val);
 
 		for (i = 0; i < PQntuples(res); i++)
 		{
@@ -1422,7 +1416,7 @@ expand_foreign_server_name_patterns(Archive *fout,
 
 		res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 		if (PQntuples(res) == 0)
-			fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
+			pg_fatal("no matching foreign servers were found for pattern \"%s\"", cell->val);
 
 		for (i = 0; i < PQntuples(res); i++)
 			simple_oid_list_append(oids, atooid(PQgetvalue(res, i, 0)));
@@ -1485,7 +1479,7 @@ expand_table_name_patterns(Archive *fout,
 		PQclear(ExecuteSqlQueryForSingleRow(fout,
 											ALWAYS_SECURE_SEARCH_PATH_SQL));
 		if (strict_names && PQntuples(res) == 0)
-			fatal("no matching tables were found for pattern \"%s\"", cell->val);
+			pg_fatal("no matching tables were found for pattern \"%s\"", cell->val);
 
 		for (i = 0; i < PQntuples(res); i++)
 		{
@@ -2033,8 +2027,8 @@ dumpTableData_copy(Archive *fout, const void *dcontext)
 	{
 		/* copy data transfer failed */
 		pg_log_error("Dumping the contents of table \"%s\" failed: PQgetCopyData() failed.", classname);
-		pg_log_error("Error message from server: %s", PQerrorMessage(conn));
-		pg_log_error("The command was: %s", q->data);
+		pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
+		pg_log_error_detail("Command was: %s", q->data);
 		exit_nicely(1);
 	}
 
@@ -2043,8 +2037,8 @@ dumpTableData_copy(Archive *fout, const void *dcontext)
 	if (PQresultStatus(res) != PGRES_COMMAND_OK)
 	{
 		pg_log_error("Dumping the contents of table \"%s\" failed: PQgetResult() failed.", classname);
-		pg_log_error("Error message from server: %s", PQerrorMessage(conn));
-		pg_log_error("The command was: %s", q->data);
+		pg_log_error_detail("Error message from server: %s", PQerrorMessage(conn));
+		pg_log_error_detail("Command was: %s", q->data);
 		exit_nicely(1);
 	}
 	PQclear(res);
@@ -2124,8 +2118,8 @@ dumpTableData_insert(Archive *fout, const void *dcontext)
 		/* cross-check field count, allowing for dummy NULL if any */
 		if (nfields != PQnfields(res) &&
 			!(nfields == 0 && PQnfields(res) == 1))
-			fatal("wrong number of fields retrieved from table \"%s\"",
-				  tbinfo->dobj.name);
+			pg_fatal("wrong number of fields retrieved from table \"%s\"",
+					 tbinfo->dobj.name);
 
 		/*
 		 * First time through, we build as much of the INSERT statement as
@@ -3231,7 +3225,7 @@ dumpSearchPath(Archive *AH)
 									  "SELECT pg_catalog.current_schemas(false)");
 
 	if (!parsePGArray(PQgetvalue(res, 0, 0), &schemanames, &nschemanames))
-		fatal("could not parse result of current_schemas()");
+		pg_fatal("could not parse result of current_schemas()");
 
 	/*
 	 * We use set_config(), not a simple "SET search_path" command, because
@@ -3457,8 +3451,8 @@ dumpBlobs(Archive *fout, const void *arg)
 			/* Open the BLOB */
 			loFd = lo_open(conn, blobOid, INV_READ);
 			if (loFd == -1)
-				fatal("could not open large object %u: %s",
-					  blobOid, PQerrorMessage(conn));
+				pg_fatal("could not open large object %u: %s",
+						 blobOid, PQerrorMessage(conn));
 
 			StartBlob(fout, blobOid);
 
@@ -3467,8 +3461,8 @@ dumpBlobs(Archive *fout, const void *arg)
 			{
 				cnt = lo_read(conn, loFd, buf, LOBBUFSIZE);
 				if (cnt < 0)
-					fatal("error reading large object %u: %s",
-						  blobOid, PQerrorMessage(conn));
+					pg_fatal("error reading large object %u: %s",
+							 blobOid, PQerrorMessage(conn));
 
 				WriteData(fout, buf, cnt);
 			} while (cnt > 0);
@@ -3714,11 +3708,8 @@ dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
 	else if (polinfo->polcmd == 'd')
 		cmd = " FOR DELETE";
 	else
-	{
-		pg_log_error("unexpected policy command type: %c",
-					 polinfo->polcmd);
-		exit_nicely(1);
-	}
+		pg_fatal("unexpected policy command type: %c",
+				 polinfo->polcmd);
 
 	query = createPQExpBuffer();
 	delqry = createPQExpBuffer();
@@ -4437,7 +4428,7 @@ dumpSubscription(Archive *fout, const SubscriptionInfo *subinfo)
 
 	/* Build list of quoted publications and append them to query. */
 	if (!parsePGArray(subinfo->subpublications, &pubnames, &npubnames))
-		fatal("could not parse %s array", "subpublications");
+		pg_fatal("could not parse %s array", "subpublications");
 
 	publications = createPQExpBuffer();
 	for (i = 0; i < npubnames; i++)
@@ -4816,8 +4807,8 @@ binary_upgrade_extension_member(PQExpBuffer upgrade_buffer,
 		extobj = NULL;
 	}
 	if (extobj == NULL)
-		fatal("could not find parent extension for %s %s",
-			  objtype, objname);
+		pg_fatal("could not find parent extension for %s %s",
+				 objtype, objname);
 
 	appendPQExpBufferStr(upgrade_buffer,
 						 "\n-- For binary upgrade, handle extension membership the hard way\n");
@@ -4961,7 +4952,7 @@ findNamespace(Oid nsoid)
 
 	nsinfo = findNamespaceByOid(nsoid);
 	if (nsinfo == NULL)
-		fatal("schema with OID %u does not exist", nsoid);
+		pg_fatal("schema with OID %u does not exist", nsoid);
 	return nsinfo;
 }
 
@@ -6415,8 +6406,8 @@ getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables)
 
 		owning_tab = findTableByOid(seqinfo->owning_tab);
 		if (owning_tab == NULL)
-			fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
-				  seqinfo->owning_tab, seqinfo->dobj.catId.oid);
+			pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
+					 seqinfo->owning_tab, seqinfo->dobj.catId.oid);
 
 		/*
 		 * Only dump identity sequences if we're going to dump the table that
@@ -6719,12 +6710,12 @@ getIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
 				break;
 		}
 		if (curtblindx >= numTables)
-			fatal("unrecognized table OID %u", indrelid);
+			pg_fatal("unrecognized table OID %u", indrelid);
 		/* cross-check that we only got requested tables */
 		if (!tbinfo->hasindex ||
 			!tbinfo->interesting)
-			fatal("unexpected index data for table \"%s\"",
-				  tbinfo->dobj.name);
+			pg_fatal("unexpected index data for table \"%s\"",
+					 tbinfo->dobj.name);
 
 		/* Save data for this table */
 		tbinfo->indexes = indxinfo + j;
@@ -6986,7 +6977,7 @@ getConstraints(Archive *fout, TableInfo tblinfo[], int numTables)
 					break;
 			}
 			if (curtblindx >= numTables)
-				fatal("unrecognized table OID %u", conrelid);
+				pg_fatal("unrecognized table OID %u", conrelid);
 		}
 
 		constrinfo[j].dobj.objType = DO_FK_CONSTRAINT;
@@ -7218,8 +7209,8 @@ getRules(Archive *fout, int *numRules)
 		ruletableoid = atooid(PQgetvalue(res, i, i_ruletable));
 		ruleinfo[i].ruletable = findTableByOid(ruletableoid);
 		if (ruleinfo[i].ruletable == NULL)
-			fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
-				  ruletableoid, ruleinfo[i].dobj.catId.oid);
+			pg_fatal("failed sanity check, parent table with OID %u of pg_rewrite entry with OID %u not found",
+					 ruletableoid, ruleinfo[i].dobj.catId.oid);
 		ruleinfo[i].dobj.namespace = ruleinfo[i].ruletable->dobj.namespace;
 		ruleinfo[i].dobj.dump = ruleinfo[i].ruletable->dobj.dump;
 		ruleinfo[i].ev_type = *(PQgetvalue(res, i, i_ev_type));
@@ -7457,7 +7448,7 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
 				break;
 		}
 		if (curtblindx >= numTables)
-			fatal("unrecognized table OID %u", tgrelid);
+			pg_fatal("unrecognized table OID %u", tgrelid);
 
 		/* Save data for this table */
 		tbinfo->triggers = tginfo + j;
@@ -7509,10 +7500,10 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables)
 					if (OidIsValid(tginfo[j].tgconstrrelid))
 					{
 						if (PQgetisnull(res, j, i_tgconstrrelname))
-							fatal("query produced null referenced table name for foreign key trigger \"%s\" on table \"%s\" (OID of table: %u)",
-								  tginfo[j].dobj.name,
-								  tbinfo->dobj.name,
-								  tginfo[j].tgconstrrelid);
+							pg_fatal("query produced null referenced table name for foreign key trigger \"%s\" on table \"%s\" (OID of table: %u)",
+									 tginfo[j].dobj.name,
+									 tbinfo->dobj.name,
+									 tginfo[j].tgconstrrelid);
 						tginfo[j].tgconstrrelname = pg_strdup(PQgetvalue(res, j, i_tgconstrrelname));
 					}
 					else
@@ -8124,12 +8115,12 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
 				break;
 		}
 		if (curtblindx >= numTables)
-			fatal("unrecognized table OID %u", attrelid);
+			pg_fatal("unrecognized table OID %u", attrelid);
 		/* cross-check that we only got requested tables */
 		if (tbinfo->relkind == RELKIND_SEQUENCE ||
 			!tbinfo->interesting)
-			fatal("unexpected column data for table \"%s\"",
-				  tbinfo->dobj.name);
+			pg_fatal("unexpected column data for table \"%s\"",
+					 tbinfo->dobj.name);
 
 		/* Save data for this table */
 		tbinfo->numatts = numatts;
@@ -8158,8 +8149,8 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
 		for (int j = 0; j < numatts; j++, r++)
 		{
 			if (j + 1 != atoi(PQgetvalue(res, r, i_attnum)))
-				fatal("invalid column numbering in table \"%s\"",
-					  tbinfo->dobj.name);
+				pg_fatal("invalid column numbering in table \"%s\"",
+						 tbinfo->dobj.name);
 			tbinfo->attnames[j] = pg_strdup(PQgetvalue(res, r, i_attname));
 			tbinfo->atttypnames[j] = pg_strdup(PQgetvalue(res, r, i_atttypname));
 			tbinfo->atttypmod[j] = atoi(PQgetvalue(res, r, i_atttypmod));
@@ -8245,12 +8236,12 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
 						break;
 				}
 				if (curtblindx >= numTables)
-					fatal("unrecognized table OID %u", adrelid);
+					pg_fatal("unrecognized table OID %u", adrelid);
 			}
 
 			if (adnum <= 0 || adnum > tbinfo->numatts)
-				fatal("invalid adnum value %d for table \"%s\"",
-					  adnum, tbinfo->dobj.name);
+				pg_fatal("invalid adnum value %d for table \"%s\"",
+						 adnum, tbinfo->dobj.name);
 
 			/*
 			 * dropped columns shouldn't have defaults, but just in case,
@@ -8399,7 +8390,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
 					break;
 			}
 			if (curtblindx >= numTables)
-				fatal("unrecognized table OID %u", conrelid);
+				pg_fatal("unrecognized table OID %u", conrelid);
 
 			if (numcons != tbinfo->ncheck)
 			{
@@ -8407,7 +8398,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
 									  "expected %d check constraints on table \"%s\" but found %d",
 									  tbinfo->ncheck),
 							 tbinfo->ncheck, tbinfo->dobj.name, numcons);
-				pg_log_error("(The system catalogs might be corrupted.)");
+				pg_log_error_hint("The system catalogs might be corrupted.");
 				exit_nicely(1);
 			}
 
@@ -9097,7 +9088,7 @@ getRoleName(const char *roleoid_str)
 		}
 	}
 
-	fatal("role with OID %u does not exist", roleoid);
+	pg_fatal("role with OID %u does not exist", roleoid);
 	return NULL;				/* keep compiler quiet */
 }
 
@@ -11565,7 +11556,7 @@ dumpFunc(Archive *fout, const FuncInfo *finfo)
 	if (*proconfig)
 	{
 		if (!parsePGArray(proconfig, &configitems, &nconfigitems))
-			fatal("could not parse %s array", "proconfig");
+			pg_fatal("could not parse %s array", "proconfig");
 	}
 	else
 	{
@@ -11634,8 +11625,8 @@ dumpFunc(Archive *fout, const FuncInfo *finfo)
 		else if (provolatile[0] == PROVOLATILE_STABLE)
 			appendPQExpBufferStr(q, " STABLE");
 		else if (provolatile[0] != PROVOLATILE_VOLATILE)
-			fatal("unrecognized provolatile value for function \"%s\"",
-				  finfo->dobj.name);
+			pg_fatal("unrecognized provolatile value for function \"%s\"",
+					 finfo->dobj.name);
 	}
 
 	if (proisstrict[0] == 't')
@@ -11684,8 +11675,8 @@ dumpFunc(Archive *fout, const FuncInfo *finfo)
 		else if (proparallel[0] == PROPARALLEL_RESTRICTED)
 			appendPQExpBufferStr(q, " PARALLEL RESTRICTED");
 		else if (proparallel[0] != PROPARALLEL_UNSAFE)
-			fatal("unrecognized proparallel value for function \"%s\"",
-				  finfo->dobj.name);
+			pg_fatal("unrecognized proparallel value for function \"%s\"",
+					 finfo->dobj.name);
 	}
 
 	for (i = 0; i < nconfigitems; i++)
@@ -11815,8 +11806,8 @@ dumpCast(Archive *fout, const CastInfo *cast)
 	{
 		funcInfo = findFuncByOid(cast->castfunc);
 		if (funcInfo == NULL)
-			fatal("could not find function definition for function with OID %u",
-				  cast->castfunc);
+			pg_fatal("could not find function definition for function with OID %u",
+					 cast->castfunc);
 	}
 
 	defqry = createPQExpBuffer();
@@ -11921,15 +11912,15 @@ dumpTransform(Archive *fout, const TransformInfo *transform)
 	{
 		fromsqlFuncInfo = findFuncByOid(transform->trffromsql);
 		if (fromsqlFuncInfo == NULL)
-			fatal("could not find function definition for function with OID %u",
-				  transform->trffromsql);
+			pg_fatal("could not find function definition for function with OID %u",
+					 transform->trffromsql);
 	}
 	if (OidIsValid(transform->trftosql))
 	{
 		tosqlFuncInfo = findFuncByOid(transform->trftosql);
 		if (tosqlFuncInfo == NULL)
-			fatal("could not find function definition for function with OID %u",
-				  transform->trftosql);
+			pg_fatal("could not find function definition for function with OID %u",
+					 transform->trftosql);
 	}
 
 	defqry = createPQExpBuffer();
@@ -12987,8 +12978,8 @@ dumpCollation(Archive *fout, const CollInfo *collinfo)
 		/* to allow dumping pg_catalog; not accepted on input */
 		appendPQExpBufferStr(q, "default");
 	else
-		fatal("unrecognized collation provider: %s",
-			  collprovider);
+		pg_fatal("unrecognized collation provider: %s",
+				 collprovider);
 
 	if (strcmp(PQgetvalue(res, 0, i_collisdeterministic), "f") == 0)
 		appendPQExpBufferStr(q, ", deterministic = false");
@@ -13394,8 +13385,8 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
 					appendPQExpBufferStr(details, ",\n    FINALFUNC_MODIFY = READ_WRITE");
 					break;
 				default:
-					fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
-						  agginfo->aggfn.dobj.name);
+					pg_fatal("unrecognized aggfinalmodify value for aggregate \"%s\"",
+							 agginfo->aggfn.dobj.name);
 					break;
 			}
 		}
@@ -13450,8 +13441,8 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
 					appendPQExpBufferStr(details, ",\n    MFINALFUNC_MODIFY = READ_WRITE");
 					break;
 				default:
-					fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
-						  agginfo->aggfn.dobj.name);
+					pg_fatal("unrecognized aggmfinalmodify value for aggregate \"%s\"",
+							 agginfo->aggfn.dobj.name);
 					break;
 			}
 		}
@@ -13475,8 +13466,8 @@ dumpAgg(Archive *fout, const AggInfo *agginfo)
 		else if (proparallel[0] == PROPARALLEL_RESTRICTED)
 			appendPQExpBufferStr(details, ",\n    PARALLEL = restricted");
 		else if (proparallel[0] != PROPARALLEL_UNSAFE)
-			fatal("unrecognized proparallel value for function \"%s\"",
-				  agginfo->aggfn.dobj.name);
+			pg_fatal("unrecognized proparallel value for function \"%s\"",
+					 agginfo->aggfn.dobj.name);
 	}
 
 	appendPQExpBuffer(delq, "DROP AGGREGATE %s.%s;\n",
@@ -14168,8 +14159,8 @@ dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
 			break;
 		default:
 			/* shouldn't get here */
-			fatal("unrecognized object type in default privileges: %d",
-				  (int) daclinfo->defaclobjtype);
+			pg_fatal("unrecognized object type in default privileges: %d",
+					 (int) daclinfo->defaclobjtype);
 			type = "";			/* keep compiler quiet */
 	}
 
@@ -14184,8 +14175,8 @@ dumpDefaultACL(Archive *fout, const DefaultACLInfo *daclinfo)
 								 daclinfo->defaclrole,
 								 fout->remoteVersion,
 								 q))
-		fatal("could not parse default ACL list (%s)",
-			  daclinfo->dacl.acl);
+		pg_fatal("could not parse default ACL list (%s)",
+				 daclinfo->dacl.acl);
 
 	if (daclinfo->dobj.dump & DUMP_COMPONENT_ACL)
 		ArchiveEntry(fout, daclinfo->dobj.catId, daclinfo->dobj.dumpId,
@@ -14266,8 +14257,8 @@ dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
 		if (!buildACLCommands(name, subname, nspname, type,
 							  initprivs, acldefault, owner,
 							  "", fout->remoteVersion, sql))
-			fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
-				  initprivs, acldefault, name, type);
+			pg_fatal("could not parse initial ACL list (%s) or default (%s) for object \"%s\" (%s)",
+					 initprivs, acldefault, name, type);
 		appendPQExpBufferStr(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
 	}
 
@@ -14291,8 +14282,8 @@ dumpACL(Archive *fout, DumpId objDumpId, DumpId altDumpId,
 	if (!buildACLCommands(name, subname, nspname, type,
 						  acls, baseacls, owner,
 						  "", fout->remoteVersion, sql))
-		fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
-			  acls, baseacls, name, type);
+		pg_fatal("could not parse ACL list (%s) or default (%s) for object \"%s\" (%s)",
+				 acls, baseacls, name, type);
 
 	if (sql->len > 0)
 	{
@@ -14829,18 +14820,18 @@ createViewAsClause(Archive *fout, const TableInfo *tbinfo)
 	if (PQntuples(res) != 1)
 	{
 		if (PQntuples(res) < 1)
-			fatal("query to obtain definition of view \"%s\" returned no data",
-				  tbinfo->dobj.name);
+			pg_fatal("query to obtain definition of view \"%s\" returned no data",
+					 tbinfo->dobj.name);
 		else
-			fatal("query to obtain definition of view \"%s\" returned more than one definition",
-				  tbinfo->dobj.name);
+			pg_fatal("query to obtain definition of view \"%s\" returned more than one definition",
+					 tbinfo->dobj.name);
 	}
 
 	len = PQgetlength(res, 0, 0);
 
 	if (len == 0)
-		fatal("definition of view \"%s\" appears to be empty (length zero)",
-			  tbinfo->dobj.name);
+		pg_fatal("definition of view \"%s\" appears to be empty (length zero)",
+				 tbinfo->dobj.name);
 
 	/* Strip off the trailing semicolon so that other things may follow. */
 	Assert(PQgetvalue(res, 0, 0)[len - 1] == ';');
@@ -15852,8 +15843,8 @@ getAttrName(int attrnum, const TableInfo *tblInfo)
 		case TableOidAttributeNumber:
 			return "tableoid";
 	}
-	fatal("invalid column number %d for table \"%s\"",
-		  attrnum, tblInfo->dobj.name);
+	pg_fatal("invalid column number %d for table \"%s\"",
+			 attrnum, tblInfo->dobj.name);
 	return NULL;				/* keep compiler quiet */
 }
 
@@ -15930,11 +15921,11 @@ dumpIndex(Archive *fout, const IndxInfo *indxinfo)
 			int			j;
 
 			if (!parsePGArray(indstatcols, &indstatcolsarray, &nstatcols))
-				fatal("could not parse index statistic columns");
+				pg_fatal("could not parse index statistic columns");
 			if (!parsePGArray(indstatvals, &indstatvalsarray, &nstatvals))
-				fatal("could not parse index statistic values");
+				pg_fatal("could not parse index statistic values");
 			if (nstatcols != nstatvals)
-				fatal("mismatched number of columns and values for index statistics");
+				pg_fatal("mismatched number of columns and values for index statistics");
 
 			for (j = 0; j < nstatcols; j++)
 			{
@@ -16152,8 +16143,8 @@ dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
 		indxinfo = (IndxInfo *) findObjectByDumpId(coninfo->conindex);
 
 		if (indxinfo == NULL)
-			fatal("missing index for constraint \"%s\"",
-				  coninfo->dobj.name);
+			pg_fatal("missing index for constraint \"%s\"",
+					 coninfo->dobj.name);
 
 		if (dopt->binary_upgrade)
 			binary_upgrade_set_pg_class_oids(fout, q,
@@ -16380,8 +16371,8 @@ dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
 	}
 	else
 	{
-		fatal("unrecognized constraint type: %c",
-			  coninfo->contype);
+		pg_fatal("unrecognized constraint type: %c",
+				 coninfo->contype);
 	}
 
 	/* Dump Constraint Comments --- only works for table constraints */
@@ -16480,13 +16471,10 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
 	if (PQntuples(res) != 1)
-	{
-		pg_log_error(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
-							  "query to get data of sequence \"%s\" returned %d rows (expected 1)",
-							  PQntuples(res)),
-					 tbinfo->dobj.name, PQntuples(res));
-		exit_nicely(1);
-	}
+		pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
+						  "query to get data of sequence \"%s\" returned %d rows (expected 1)",
+						  PQntuples(res)),
+				 tbinfo->dobj.name, PQntuples(res));
 
 	seqtype = PQgetvalue(res, 0, 0);
 	startv = PQgetvalue(res, 0, 1);
@@ -16515,7 +16503,7 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
 	}
 	else
 	{
-		fatal("unrecognized sequence type: %s", seqtype);
+		pg_fatal("unrecognized sequence type: %s", seqtype);
 		default_minv = default_maxv = 0;	/* keep compiler quiet */
 	}
 
@@ -16638,8 +16626,8 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
 		TableInfo  *owning_tab = findTableByOid(tbinfo->owning_tab);
 
 		if (owning_tab == NULL)
-			fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
-				  tbinfo->owning_tab, tbinfo->dobj.catId.oid);
+			pg_fatal("failed sanity check, parent table with OID %u of sequence with OID %u not found",
+					 tbinfo->owning_tab, tbinfo->dobj.catId.oid);
 
 		if (owning_tab->dobj.dump & DUMP_COMPONENT_DEFINITION)
 		{
@@ -16702,13 +16690,10 @@ dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
 	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
 	if (PQntuples(res) != 1)
-	{
-		pg_log_error(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
-							  "query to get data of sequence \"%s\" returned %d rows (expected 1)",
-							  PQntuples(res)),
-					 tbinfo->dobj.name, PQntuples(res));
-		exit_nicely(1);
-	}
+		pg_fatal(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
+						  "query to get data of sequence \"%s\" returned %d rows (expected 1)",
+						  PQntuples(res)),
+				 tbinfo->dobj.name, PQntuples(res));
 
 	last = PQgetvalue(res, 0, 0);
 	called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
@@ -16797,10 +16782,7 @@ dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
 		else if (TRIGGER_FOR_INSTEAD(tginfo->tgtype))
 			appendPQExpBufferStr(query, "INSTEAD OF");
 		else
-		{
-			pg_log_error("unexpected tgtype value: %d", tginfo->tgtype);
-			exit_nicely(1);
-		}
+			pg_fatal("unexpected tgtype value: %d", tginfo->tgtype);
 
 		findx = 0;
 		if (TRIGGER_FOR_INSERT(tginfo->tgtype))
@@ -16872,11 +16854,10 @@ dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
 			if (p + tlen >= tgargs + lentgargs)
 			{
 				/* hm, not found before end of bytea value... */
-				pg_log_error("invalid argument string (%s) for trigger \"%s\" on table \"%s\"",
-							 tginfo->tgargs,
-							 tginfo->dobj.name,
-							 tbinfo->dobj.name);
-				exit_nicely(1);
+				pg_fatal("invalid argument string (%s) for trigger \"%s\" on table \"%s\"",
+						 tginfo->tgargs,
+						 tginfo->dobj.name,
+						 tbinfo->dobj.name);
 			}
 
 			if (findx > 0)
@@ -17142,11 +17123,8 @@ dumpRule(Archive *fout, const RuleInfo *rinfo)
 		res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
 
 		if (PQntuples(res) != 1)
-		{
-			pg_log_error("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
-						 rinfo->dobj.name, tbinfo->dobj.name);
-			exit_nicely(1);
-		}
+			pg_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
+					 rinfo->dobj.name, tbinfo->dobj.name);
 
 		printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));
 
@@ -17384,11 +17362,11 @@ processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
 			int			j;
 
 			if (!parsePGArray(extconfig, &extconfigarray, &nconfigitems))
-				fatal("could not parse %s array", "extconfig");
+				pg_fatal("could not parse %s array", "extconfig");
 			if (!parsePGArray(extcondition, &extconditionarray, &nconditionitems))
-				fatal("could not parse %s array", "extcondition");
+				pg_fatal("could not parse %s array", "extcondition");
 			if (nconfigitems != nconditionitems)
-				fatal("mismatched number of configurations and conditions for extension");
+				pg_fatal("mismatched number of configurations and conditions for extension");
 
 			for (j = 0; j < nconfigitems; j++)
 			{
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index d979f93b3d..7656b26e8f 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -419,13 +419,13 @@ TopoSort(DumpableObject **objs,
 		obj = objs[i];
 		j = obj->dumpId;
 		if (j <= 0 || j > maxDumpId)
-			fatal("invalid dumpId %d", j);
+			pg_fatal("invalid dumpId %d", j);
 		idMap[j] = i;
 		for (j = 0; j < obj->nDeps; j++)
 		{
 			k = obj->dependencies[j];
 			if (k <= 0 || k > maxDumpId)
-				fatal("invalid dependency %d", k);
+				pg_fatal("invalid dependency %d", k);
 			beforeConstraints[k]++;
 		}
 	}
@@ -658,7 +658,7 @@ findDependencyLoops(DumpableObject **objs, int nObjs, int totObjs)
 
 	/* We'd better have fixed at least one loop */
 	if (!fixedloop)
-		fatal("could not identify dependency loop");
+		pg_fatal("could not identify dependency loop");
 
 	free(workspace);
 	free(searchFailed);
@@ -1233,9 +1233,9 @@ repairDependencyLoop(DumpableObject **loop,
 								"there are circular foreign-key constraints among these tables:",
 								nLoop));
 		for (i = 0; i < nLoop; i++)
-			pg_log_generic(PG_LOG_INFO, "  %s", loop[i]->name);
-		pg_log_generic(PG_LOG_INFO, "You might not be able to restore the dump without using --disable-triggers or temporarily dropping the constraints.");
-		pg_log_generic(PG_LOG_INFO, "Consider using a full dump instead of a --data-only dump to avoid this problem.");
+			pg_log_info("  %s", loop[i]->name);
+		pg_log_info("You might not be able to restore the dump without using --disable-triggers or temporarily dropping the constraints.");
+		pg_log_info("Consider using a full dump instead of a --data-only dump to avoid this problem.");
 		if (nLoop > 1)
 			removeObjectDependency(loop[0], loop[1]->dumpId);
 		else					/* must be a self-dependency */
@@ -1253,7 +1253,7 @@ repairDependencyLoop(DumpableObject **loop,
 		char		buf[1024];
 
 		describeDumpableObject(loop[i], buf, sizeof(buf));
-		pg_log_generic(PG_LOG_INFO, "  %s", buf);
+		pg_log_info("  %s", buf);
 	}
 
 	if (nLoop > 1)
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 9c9f7c6d63..b5beb96b43 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -200,16 +200,15 @@ main(int argc, char *argv[])
 			strlcpy(full_path, progname, sizeof(full_path));
 
 		if (ret == -1)
-			pg_log_error("The program \"%s\" is needed by %s but was not found in the\n"
-						 "same directory as \"%s\".\n"
-						 "Check your installation.",
-						 "pg_dump", progname, full_path);
+			pg_fatal("The program \"%s\" is needed by %s but was not found in the\n"
+					 "same directory as \"%s\".\n"
+					 "Check your installation.",
+					 "pg_dump", progname, full_path);
 		else
-			pg_log_error("The program \"%s\" was found by \"%s\"\n"
-						 "but was not the same version as %s.\n"
-						 "Check your installation.",
-						 "pg_dump", full_path, progname);
-		exit_nicely(1);
+			pg_fatal("The program \"%s\" was found by \"%s\"\n"
+					 "but was not the same version as %s.\n"
+					 "Check your installation.",
+					 "pg_dump", full_path, progname);
 	}
 
 	pgdumpopts = createPQExpBuffer();
@@ -339,7 +338,8 @@ main(int argc, char *argv[])
 				break;
 
 			default:
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+				/* getopt_long already emitted a complaint */
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 				exit_nicely(1);
 		}
 	}
@@ -349,8 +349,7 @@ main(int argc, char *argv[])
 	{
 		pg_log_error("too many command-line arguments (first is \"%s\")",
 					 argv[optind]);
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit_nicely(1);
 	}
 
@@ -358,8 +357,7 @@ main(int argc, char *argv[])
 		(globals_only || roles_only || tablespaces_only))
 	{
 		pg_log_error("option --exclude-database cannot be used together with -g/--globals-only, -r/--roles-only, or -t/--tablespaces-only");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit_nicely(1);
 	}
 
@@ -367,30 +365,24 @@ main(int argc, char *argv[])
 	if (globals_only && roles_only)
 	{
 		pg_log_error("options -g/--globals-only and -r/--roles-only cannot be used together");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit_nicely(1);
 	}
 
 	if (globals_only && tablespaces_only)
 	{
 		pg_log_error("options -g/--globals-only and -t/--tablespaces-only cannot be used together");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit_nicely(1);
 	}
 
 	if (if_exists && !output_clean)
-	{
-		pg_log_error("option --if-exists requires option -c/--clean");
-		exit_nicely(1);
-	}
+		pg_fatal("option --if-exists requires option -c/--clean");
 
 	if (roles_only && tablespaces_only)
 	{
 		pg_log_error("options -r/--roles-only and -t/--tablespaces-only cannot be used together");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit_nicely(1);
 	}
 
@@ -451,10 +443,7 @@ main(int argc, char *argv[])
 							   prompt_password, false);
 
 		if (!conn)
-		{
-			pg_log_error("could not connect to database \"%s\"", pgdb);
-			exit_nicely(1);
-		}
+			pg_fatal("could not connect to database \"%s\"", pgdb);
 	}
 	else
 	{
@@ -468,8 +457,7 @@ main(int argc, char *argv[])
 		{
 			pg_log_error("could not connect to databases \"postgres\" or \"template1\"\n"
 						 "Please specify an alternative database.");
-			fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-					progname);
+			pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 			exit_nicely(1);
 		}
 	}
@@ -487,11 +475,8 @@ main(int argc, char *argv[])
 	{
 		OPF = fopen(filename, PG_BINARY_W);
 		if (!OPF)
-		{
-			pg_log_error("could not open output file \"%s\": %m",
-						 filename);
-			exit_nicely(1);
-		}
+			pg_fatal("could not open output file \"%s\": %m",
+					 filename);
 	}
 	else
 		OPF = stdout;
@@ -502,11 +487,8 @@ main(int argc, char *argv[])
 	if (dumpencoding)
 	{
 		if (PQsetClientEncoding(conn, dumpencoding) < 0)
-		{
-			pg_log_error("invalid client encoding \"%s\" specified",
-						 dumpencoding);
-			exit_nicely(1);
-		}
+			pg_fatal("invalid client encoding \"%s\" specified",
+					 dumpencoding);
 	}
 
 	/*
@@ -1321,20 +1303,14 @@ dumpDatabases(PGconn *conn)
 
 		ret = runPgDump(dbname, create_opts);
 		if (ret != 0)
-		{
-			pg_log_error("pg_dump failed on database \"%s\", exiting", dbname);
-			exit_nicely(1);
-		}
+			pg_fatal("pg_dump failed on database \"%s\", exiting", dbname);
 
 		if (filename)
 		{
 			OPF = fopen(filename, PG_BINARY_A);
 			if (!OPF)
-			{
-				pg_log_error("could not re-open the output file \"%s\": %m",
-							 filename);
-				exit_nicely(1);
-			}
+				pg_fatal("could not re-open the output file \"%s\": %m",
+						 filename);
 		}
 
 	}
@@ -1470,10 +1446,7 @@ connectDatabase(const char *dbname, const char *connection_string,
 		{
 			conn_opts = PQconninfoParse(connection_string, &err_msg);
 			if (conn_opts == NULL)
-			{
-				pg_log_error("%s", err_msg);
-				exit_nicely(1);
-			}
+				pg_fatal("%s", err_msg);
 
 			for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++)
 			{
@@ -1540,10 +1513,7 @@ connectDatabase(const char *dbname, const char *connection_string,
 		conn = PQconnectdbParams(keywords, values, true);
 
 		if (!conn)
-		{
-			pg_log_error("could not connect to database \"%s\"", dbname);
-			exit_nicely(1);
-		}
+			pg_fatal("could not connect to database \"%s\"", dbname);
 
 		if (PQstatus(conn) == CONNECTION_BAD &&
 			PQconnectionNeedsPassword(conn) &&
@@ -1560,10 +1530,7 @@ connectDatabase(const char *dbname, const char *connection_string,
 	if (PQstatus(conn) == CONNECTION_BAD)
 	{
 		if (fail_on_error)
-		{
-			pg_log_error("%s", PQerrorMessage(conn));
-			exit_nicely(1);
-		}
+			pg_fatal("%s", PQerrorMessage(conn));
 		else
 		{
 			PQfinish(conn);
@@ -1589,17 +1556,11 @@ connectDatabase(const char *dbname, const char *connection_string,
 	/* Check version */
 	remoteversion_str = PQparameterStatus(conn, "server_version");
 	if (!remoteversion_str)
-	{
-		pg_log_error("could not get server version");
-		exit_nicely(1);
-	}
+		pg_fatal("could not get server version");
 	server_version = PQserverVersion(conn);
 	if (server_version == 0)
-	{
-		pg_log_error("could not parse server version \"%s\"",
-					 remoteversion_str);
-		exit_nicely(1);
-	}
+		pg_fatal("could not parse server version \"%s\"",
+				 remoteversion_str);
 
 	my_version = PG_VERSION_NUM;
 
@@ -1611,9 +1572,9 @@ connectDatabase(const char *dbname, const char *connection_string,
 		&& (server_version < 90200 ||
 			(server_version / 100) > (my_version / 100)))
 	{
-		pg_log_error("server version: %s; %s version: %s",
-					 remoteversion_str, progname, PG_VERSION);
 		pg_log_error("aborting because of server version mismatch");
+		pg_log_error_detail("server version: %s; %s version: %s",
+							remoteversion_str, progname, PG_VERSION);
 		exit_nicely(1);
 	}
 
@@ -1675,7 +1636,7 @@ executeQuery(PGconn *conn, const char *query)
 		PQresultStatus(res) != PGRES_TUPLES_OK)
 	{
 		pg_log_error("query failed: %s", PQerrorMessage(conn));
-		pg_log_error("query was: %s", query);
+		pg_log_error_detail("Query was: %s", query);
 		PQfinish(conn);
 		exit_nicely(1);
 	}
@@ -1698,7 +1659,7 @@ executeCommand(PGconn *conn, const char *query)
 		PQresultStatus(res) != PGRES_COMMAND_OK)
 	{
 		pg_log_error("query failed: %s", PQerrorMessage(conn));
-		pg_log_error("query was: %s", query);
+		pg_log_error_detail("Query was: %s", query);
 		PQfinish(conn);
 		exit_nicely(1);
 	}
diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c
index 55bf1b6975..049a100634 100644
--- a/src/bin/pg_dump/pg_restore.c
+++ b/src/bin/pg_dump/pg_restore.c
@@ -287,7 +287,8 @@ main(int argc, char **argv)
 				break;
 
 			default:
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+				/* getopt_long already emitted a complaint */
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 				exit_nicely(1);
 		}
 	}
@@ -303,17 +304,13 @@ main(int argc, char **argv)
 	{
 		pg_log_error("too many command-line arguments (first is \"%s\")",
 					 argv[optind]);
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit_nicely(1);
 	}
 
 	/* Complain if neither -f nor -d was specified (except if dumping TOC) */
 	if (!opts->cparams.dbname && !opts->filename && !opts->tocSummary)
-	{
-		pg_log_error("one of -d/--dbname and -f/--file must be specified");
-		exit_nicely(1);
-	}
+		pg_fatal("one of -d/--dbname and -f/--file must be specified");
 
 	/* Should get at most one of -d and -f, else user is confused */
 	if (opts->cparams.dbname)
@@ -321,41 +318,28 @@ main(int argc, char **argv)
 		if (opts->filename)
 		{
 			pg_log_error("options -d/--dbname and -f/--file cannot be used together");
-			fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-					progname);
+			pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 			exit_nicely(1);
 		}
 		opts->useDB = 1;
 	}
 
 	if (opts->dataOnly && opts->schemaOnly)
-	{
-		pg_log_error("options -s/--schema-only and -a/--data-only cannot be used together");
-		exit_nicely(1);
-	}
+		pg_fatal("options -s/--schema-only and -a/--data-only cannot be used together");
 
 	if (opts->dataOnly && opts->dropSchema)
-	{
-		pg_log_error("options -c/--clean and -a/--data-only cannot be used together");
-		exit_nicely(1);
-	}
+		pg_fatal("options -c/--clean and -a/--data-only cannot be used together");
 
 	/*
 	 * -C is not compatible with -1, because we can't create a database inside
 	 * a transaction block.
 	 */
 	if (opts->createDB && opts->single_txn)
-	{
-		pg_log_error("options -C/--create and -1/--single-transaction cannot be used together");
-		exit_nicely(1);
-	}
+		pg_fatal("options -C/--create and -1/--single-transaction cannot be used together");
 
 	/* Can't do single-txn mode with multiple connections */
 	if (opts->single_txn && numWorkers > 1)
-	{
-		pg_log_error("cannot specify both --single-transaction and multiple jobs");
-		exit_nicely(1);
-	}
+		pg_fatal("cannot specify both --single-transaction and multiple jobs");
 
 	opts->disable_triggers = disable_triggers;
 	opts->enable_row_security = enable_row_security;
@@ -369,10 +353,7 @@ main(int argc, char **argv)
 	opts->no_subscriptions = no_subscriptions;
 
 	if (if_exists && !opts->dropSchema)
-	{
-		pg_log_error("option --if-exists requires option -c/--clean");
-		exit_nicely(1);
-	}
+		pg_fatal("option --if-exists requires option -c/--clean");
 	opts->if_exists = if_exists;
 	opts->strict_names = strict_names;
 
@@ -396,9 +377,8 @@ main(int argc, char **argv)
 				break;
 
 			default:
-				pg_log_error("unrecognized archive format \"%s\"; please specify \"c\", \"d\", or \"t\"",
-							 opts->formatName);
-				exit_nicely(1);
+				pg_fatal("unrecognized archive format \"%s\"; please specify \"c\", \"d\", or \"t\"",
+						 opts->formatName);
 		}
 	}
 
diff --git a/src/bin/pg_dump/t/003_pg_dump_with_server.pl b/src/bin/pg_dump/t/003_pg_dump_with_server.pl
index 528db179cb..c284866326 100644
--- a/src/bin/pg_dump/t/003_pg_dump_with_server.pl
+++ b/src/bin/pg_dump/t/003_pg_dump_with_server.pl
@@ -30,7 +30,7 @@ my ($cmd, $stdout, $stderr, $result);
 
 command_fails_like(
 	[ "pg_dump", '-p', $port, '--include-foreign-data=s0', 'postgres' ],
-	qr/foreign-data wrapper \"dummy\" has no handler\r?\npg_dump: error: query was:.*t0/,
+	qr/foreign-data wrapper \"dummy\" has no handler\r?\ndetail: Query was: .*t0/,
 	"correctly fails to dump a foreign table from a dummy FDW");
 
 command_ok(
diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
index 1eb4509fca..d4772a2965 100644
--- a/src/bin/pg_resetwal/pg_resetwal.c
+++ b/src/bin/pg_resetwal/pg_resetwal.c
@@ -161,14 +161,11 @@ main(int argc, char *argv[])
 					/*------
 					  translator: the second %s is a command line argument (-e, etc) */
 					pg_log_error("invalid argument for option %s", "-e");
-					fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+					pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 					exit(1);
 				}
 				if (set_xid_epoch == -1)
-				{
-					pg_log_error("transaction ID epoch (-e) must not be -1");
-					exit(1);
-				}
+					pg_fatal("transaction ID epoch (-e) must not be -1");
 				break;
 
 			case 'u':
@@ -177,14 +174,11 @@ main(int argc, char *argv[])
 				if (endptr == optarg || *endptr != '\0' || errno != 0)
 				{
 					pg_log_error("invalid argument for option %s", "-u");
-					fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+					pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 					exit(1);
 				}
 				if (!TransactionIdIsNormal(set_oldest_xid))
-				{
-					pg_log_error("oldest transaction ID (-u) must be greater than or equal to %u", FirstNormalTransactionId);
-					exit(1);
-				}
+					pg_fatal("oldest transaction ID (-u) must be greater than or equal to %u", FirstNormalTransactionId);
 				break;
 
 			case 'x':
@@ -193,14 +187,11 @@ main(int argc, char *argv[])
 				if (endptr == optarg || *endptr != '\0' || errno != 0)
 				{
 					pg_log_error("invalid argument for option %s", "-x");
-					fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+					pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 					exit(1);
 				}
 				if (!TransactionIdIsNormal(set_xid))
-				{
-					pg_log_error("transaction ID (-x) must be greater than or equal to %u", FirstNormalTransactionId);
-					exit(1);
-				}
+					pg_fatal("transaction ID (-x) must be greater than or equal to %u", FirstNormalTransactionId);
 				break;
 
 			case 'c':
@@ -209,30 +200,24 @@ main(int argc, char *argv[])
 				if (endptr == optarg || *endptr != ',' || errno != 0)
 				{
 					pg_log_error("invalid argument for option %s", "-c");
-					fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+					pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 					exit(1);
 				}
 				set_newest_commit_ts_xid = strtoul(endptr + 1, &endptr2, 0);
 				if (endptr2 == endptr + 1 || *endptr2 != '\0' || errno != 0)
 				{
 					pg_log_error("invalid argument for option %s", "-c");
-					fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+					pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 					exit(1);
 				}
 
 				if (set_oldest_commit_ts_xid < 2 &&
 					set_oldest_commit_ts_xid != 0)
-				{
-					pg_log_error("transaction ID (-c) must be either 0 or greater than or equal to 2");
-					exit(1);
-				}
+					pg_fatal("transaction ID (-c) must be either 0 or greater than or equal to 2");
 
 				if (set_newest_commit_ts_xid < 2 &&
 					set_newest_commit_ts_xid != 0)
-				{
-					pg_log_error("transaction ID (-c) must be either 0 or greater than or equal to 2");
-					exit(1);
-				}
+					pg_fatal("transaction ID (-c) must be either 0 or greater than or equal to 2");
 				break;
 
 			case 'o':
@@ -241,14 +226,11 @@ main(int argc, char *argv[])
 				if (endptr == optarg || *endptr != '\0' || errno != 0)
 				{
 					pg_log_error("invalid argument for option %s", "-o");
-					fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+					pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 					exit(1);
 				}
 				if (set_oid == 0)
-				{
-					pg_log_error("OID (-o) must not be 0");
-					exit(1);
-				}
+					pg_fatal("OID (-o) must not be 0");
 				break;
 
 			case 'm':
@@ -257,7 +239,7 @@ main(int argc, char *argv[])
 				if (endptr == optarg || *endptr != ',' || errno != 0)
 				{
 					pg_log_error("invalid argument for option %s", "-m");
-					fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+					pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 					exit(1);
 				}
 
@@ -265,24 +247,18 @@ main(int argc, char *argv[])
 				if (endptr2 == endptr + 1 || *endptr2 != '\0' || errno != 0)
 				{
 					pg_log_error("invalid argument for option %s", "-m");
-					fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+					pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 					exit(1);
 				}
 				if (set_mxid == 0)
-				{
-					pg_log_error("multitransaction ID (-m) must not be 0");
-					exit(1);
-				}
+					pg_fatal("multitransaction ID (-m) must not be 0");
 
 				/*
 				 * XXX It'd be nice to have more sanity checks here, e.g. so
 				 * that oldest is not wrapped around w.r.t. nextMulti.
 				 */
 				if (set_oldestmxid == 0)
-				{
-					pg_log_error("oldest multitransaction ID (-m) must not be 0");
-					exit(1);
-				}
+					pg_fatal("oldest multitransaction ID (-m) must not be 0");
 				break;
 
 			case 'O':
@@ -291,21 +267,18 @@ main(int argc, char *argv[])
 				if (endptr == optarg || *endptr != '\0' || errno != 0)
 				{
 					pg_log_error("invalid argument for option %s", "-O");
-					fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+					pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 					exit(1);
 				}
 				if (set_mxoff == -1)
-				{
-					pg_log_error("multitransaction offset (-O) must not be -1");
-					exit(1);
-				}
+					pg_fatal("multitransaction offset (-O) must not be -1");
 				break;
 
 			case 'l':
 				if (strspn(optarg, "01234567890ABCDEFabcdef") != XLOG_FNAME_LEN)
 				{
 					pg_log_error("invalid argument for option %s", "-l");
-					fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+					pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 					exit(1);
 				}
 
@@ -320,19 +293,14 @@ main(int argc, char *argv[])
 				errno = 0;
 				set_wal_segsize = strtol(optarg, &endptr, 10) * 1024 * 1024;
 				if (endptr == optarg || *endptr != '\0' || errno != 0)
-				{
-					pg_log_error("argument of --wal-segsize must be a number");
-					exit(1);
-				}
+					pg_fatal("argument of --wal-segsize must be a number");
 				if (!IsValidWalSegSize(set_wal_segsize))
-				{
-					pg_log_error("argument of --wal-segsize must be a power of 2 between 1 and 1024");
-					exit(1);
-				}
+					pg_fatal("argument of --wal-segsize must be a power of 2 between 1 and 1024");
 				break;
 
 			default:
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+				/* getopt_long already emitted a complaint */
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 				exit(1);
 		}
 	}
@@ -345,15 +313,14 @@ main(int argc, char *argv[])
 	{
 		pg_log_error("too many command-line arguments (first is \"%s\")",
 					 argv[optind]);
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
 	if (DataDir == NULL)
 	{
 		pg_log_error("no data directory specified");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
@@ -367,8 +334,8 @@ main(int argc, char *argv[])
 	if (geteuid() == 0)
 	{
 		pg_log_error("cannot be executed by \"root\"");
-		pg_log_info("You must run %s as the PostgreSQL superuser.",
-					progname);
+		pg_log_error_hint("You must run %s as the PostgreSQL superuser.",
+						  progname);
 		exit(1);
 	}
 #endif
@@ -377,20 +344,14 @@ main(int argc, char *argv[])
 
 	/* Set mask based on PGDATA permissions */
 	if (!GetDataDirectoryCreatePerm(DataDir))
-	{
-		pg_log_error("could not read permissions of directory \"%s\": %m",
-					 DataDir);
-		exit(1);
-	}
+		pg_fatal("could not read permissions of directory \"%s\": %m",
+				 DataDir);
 
 	umask(pg_mode_mask);
 
 	if (chdir(DataDir) < 0)
-	{
-		pg_log_error("could not change directory to \"%s\": %m",
-					 DataDir);
-		exit(1);
-	}
+		pg_fatal("could not change directory to \"%s\": %m",
+				 DataDir);
 
 	/* Check that data directory matches our server version */
 	CheckDataVersion();
@@ -402,16 +363,13 @@ main(int argc, char *argv[])
 	if ((fd = open("postmaster.pid", O_RDONLY, 0)) < 0)
 	{
 		if (errno != ENOENT)
-		{
-			pg_log_error("could not open file \"%s\" for reading: %m",
-						 "postmaster.pid");
-			exit(1);
-		}
+			pg_fatal("could not open file \"%s\" for reading: %m",
+					 "postmaster.pid");
 	}
 	else
 	{
 		pg_log_error("lock file \"%s\" exists", "postmaster.pid");
-		pg_log_info("Is a server running?  If not, delete the lock file and try again.");
+		pg_log_error_hint("Is a server running?  If not, delete the lock file and try again.");
 		exit(1);
 	}
 
@@ -557,20 +515,16 @@ CheckDataVersion(void)
 	char		rawline[64];
 
 	if ((ver_fd = fopen(ver_file, "r")) == NULL)
-	{
-		pg_log_error("could not open file \"%s\" for reading: %m",
-					 ver_file);
-		exit(1);
-	}
+		pg_fatal("could not open file \"%s\" for reading: %m",
+				 ver_file);
 
 	/* version number has to be the first line read */
 	if (!fgets(rawline, sizeof(rawline), ver_fd))
 	{
 		if (!ferror(ver_fd))
-			pg_log_error("unexpected empty file \"%s\"", ver_file);
+			pg_fatal("unexpected empty file \"%s\"", ver_file);
 		else
-			pg_log_error("could not read file \"%s\": %m", ver_file);
-		exit(1);
+			pg_fatal("could not read file \"%s\": %m", ver_file);
 	}
 
 	/* strip trailing newline and carriage return */
@@ -579,8 +533,8 @@ CheckDataVersion(void)
 	if (strcmp(rawline, PG_MAJORVERSION) != 0)
 	{
 		pg_log_error("data directory is of wrong version");
-		pg_log_info("File \"%s\" contains \"%s\", which is not compatible with this program's version \"%s\".",
-					ver_file, rawline, PG_MAJORVERSION);
+		pg_log_error_detail("File \"%s\" contains \"%s\", which is not compatible with this program's version \"%s\".",
+							ver_file, rawline, PG_MAJORVERSION);
 		exit(1);
 	}
 
@@ -612,10 +566,10 @@ read_controlfile(void)
 		pg_log_error("could not open file \"%s\" for reading: %m",
 					 XLOG_CONTROL_FILE);
 		if (errno == ENOENT)
-			pg_log_info("If you are sure the data directory path is correct, execute\n"
-						"  touch %s\n"
-						"and try again.",
-						XLOG_CONTROL_FILE);
+			pg_log_error_hint("If you are sure the data directory path is correct, execute\n"
+							  "  touch %s\n"
+							  "and try again.",
+							  XLOG_CONTROL_FILE);
 		exit(1);
 	}
 
@@ -624,10 +578,7 @@ read_controlfile(void)
 
 	len = read(fd, buffer, PG_CONTROL_FILE_SIZE);
 	if (len < 0)
-	{
-		pg_log_error("could not read file \"%s\": %m", XLOG_CONTROL_FILE);
-		exit(1);
-	}
+		pg_fatal("could not read file \"%s\": %m", XLOG_CONTROL_FILE);
 	close(fd);
 
 	if (len >= sizeof(ControlFileData) &&
@@ -968,10 +919,7 @@ FindEndOfXLOG(void)
 	 */
 	xldir = opendir(XLOGDIR);
 	if (xldir == NULL)
-	{
-		pg_log_error("could not open directory \"%s\": %m", XLOGDIR);
-		exit(1);
-	}
+		pg_fatal("could not open directory \"%s\": %m", XLOGDIR);
 
 	while (errno = 0, (xlde = readdir(xldir)) != NULL)
 	{
@@ -1003,16 +951,10 @@ FindEndOfXLOG(void)
 	}
 
 	if (errno)
-	{
-		pg_log_error("could not read directory \"%s\": %m", XLOGDIR);
-		exit(1);
-	}
+		pg_fatal("could not read directory \"%s\": %m", XLOGDIR);
 
 	if (closedir(xldir))
-	{
-		pg_log_error("could not close directory \"%s\": %m", XLOGDIR);
-		exit(1);
-	}
+		pg_fatal("could not close directory \"%s\": %m", XLOGDIR);
 
 	/*
 	 * Finally, convert to new xlog seg size, and advance by one to ensure we
@@ -1036,10 +978,7 @@ KillExistingXLOG(void)
 
 	xldir = opendir(XLOGDIR);
 	if (xldir == NULL)
-	{
-		pg_log_error("could not open directory \"%s\": %m", XLOGDIR);
-		exit(1);
-	}
+		pg_fatal("could not open directory \"%s\": %m", XLOGDIR);
 
 	while (errno = 0, (xlde = readdir(xldir)) != NULL)
 	{
@@ -1048,24 +987,15 @@ KillExistingXLOG(void)
 		{
 			snprintf(path, sizeof(path), "%s/%s", XLOGDIR, xlde->d_name);
 			if (unlink(path) < 0)
-			{
-				pg_log_error("could not delete file \"%s\": %m", path);
-				exit(1);
-			}
+				pg_fatal("could not delete file \"%s\": %m", path);
 		}
 	}
 
 	if (errno)
-	{
-		pg_log_error("could not read directory \"%s\": %m", XLOGDIR);
-		exit(1);
-	}
+		pg_fatal("could not read directory \"%s\": %m", XLOGDIR);
 
 	if (closedir(xldir))
-	{
-		pg_log_error("could not close directory \"%s\": %m", XLOGDIR);
-		exit(1);
-	}
+		pg_fatal("could not close directory \"%s\": %m", XLOGDIR);
 }

@@ -1083,10 +1013,7 @@ KillExistingArchiveStatus(void)
 
 	xldir = opendir(ARCHSTATDIR);
 	if (xldir == NULL)
-	{
-		pg_log_error("could not open directory \"%s\": %m", ARCHSTATDIR);
-		exit(1);
-	}
+		pg_fatal("could not open directory \"%s\": %m", ARCHSTATDIR);
 
 	while (errno = 0, (xlde = readdir(xldir)) != NULL)
 	{
@@ -1098,24 +1025,15 @@ KillExistingArchiveStatus(void)
 		{
 			snprintf(path, sizeof(path), "%s/%s", ARCHSTATDIR, xlde->d_name);
 			if (unlink(path) < 0)
-			{
-				pg_log_error("could not delete file \"%s\": %m", path);
-				exit(1);
-			}
+				pg_fatal("could not delete file \"%s\": %m", path);
 		}
 	}
 
 	if (errno)
-	{
-		pg_log_error("could not read directory \"%s\": %m", ARCHSTATDIR);
-		exit(1);
-	}
+		pg_fatal("could not read directory \"%s\": %m", ARCHSTATDIR);
 
 	if (closedir(xldir))
-	{
-		pg_log_error("could not close directory \"%s\": %m", ARCHSTATDIR);
-		exit(1);
-	}
+		pg_fatal("could not close directory \"%s\": %m", ARCHSTATDIR);
 }

@@ -1179,10 +1097,7 @@ WriteEmptyXLOG(void)
 	fd = open(path, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
 			  pg_file_create_mode);
 	if (fd < 0)
-	{
-		pg_log_error("could not open file \"%s\": %m", path);
-		exit(1);
-	}
+		pg_fatal("could not open file \"%s\": %m", path);
 
 	errno = 0;
 	if (write(fd, buffer.data, XLOG_BLCKSZ) != XLOG_BLCKSZ)
@@ -1190,8 +1105,7 @@ WriteEmptyXLOG(void)
 		/* if write didn't set errno, assume problem is no disk space */
 		if (errno == 0)
 			errno = ENOSPC;
-		pg_log_error("could not write file \"%s\": %m", path);
-		exit(1);
+		pg_fatal("could not write file \"%s\": %m", path);
 	}
 
 	/* Fill the rest of the file with zeroes */
@@ -1203,16 +1117,12 @@ WriteEmptyXLOG(void)
 		{
 			if (errno == 0)
 				errno = ENOSPC;
-			pg_log_error("could not write file \"%s\": %m", path);
-			exit(1);
+			pg_fatal("could not write file \"%s\": %m", path);
 		}
 	}
 
 	if (fsync(fd) != 0)
-	{
-		pg_log_error("fsync error: %m");
-		exit(1);
-	}
+		pg_fatal("fsync error: %m");
 
 	close(fd);
 }
diff --git a/src/bin/pg_rewind/nls.mk b/src/bin/pg_rewind/nls.mk
index a561f965df..a50f9139df 100644
--- a/src/bin/pg_rewind/nls.mk
+++ b/src/bin/pg_rewind/nls.mk
@@ -2,7 +2,6 @@
 CATALOG_NAME     = pg_rewind
 AVAIL_LANGUAGES  = cs de es fr it ja ko pl pt_BR ru sv tr uk zh_CN
 GETTEXT_FILES    = $(FRONTEND_COMMON_GETTEXT_FILES) datapagemap.c file_ops.c filemap.c libpq_source.c local_source.c parsexlog.c pg_rewind.c timeline.c xlogreader.c ../../common/fe_memutils.c ../../common/restricted_token.c ../../fe_utils/archive.c ../../fe_utils/recovery_gen.c
-GETTEXT_TRIGGERS = $(FRONTEND_COMMON_GETTEXT_TRIGGERS) pg_fatal report_invalid_record:2
+GETTEXT_TRIGGERS = $(FRONTEND_COMMON_GETTEXT_TRIGGERS) report_invalid_record:2
 GETTEXT_FLAGS    = $(FRONTEND_COMMON_GETTEXT_FLAGS) \
-    pg_fatal:1:c-format \
     report_invalid_record:2:c-format
diff --git a/src/bin/pg_rewind/pg_rewind.c b/src/bin/pg_rewind/pg_rewind.c
index efb82a4034..ab62fe2e83 100644
--- a/src/bin/pg_rewind/pg_rewind.c
+++ b/src/bin/pg_rewind/pg_rewind.c
@@ -160,10 +160,6 @@ main(int argc, char **argv)
 	{
 		switch (c)
 		{
-			case '?':
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
-				exit(1);
-
 			case 'c':
 				restore_wal = true;
 				break;
@@ -204,34 +200,39 @@ main(int argc, char **argv)
 			case 4:
 				no_ensure_shutdown = true;
 				break;
+
+			default:
+				/* getopt_long already emitted a complaint */
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+				exit(1);
 		}
 	}
 
 	if (datadir_source == NULL && connstr_source == NULL)
 	{
 		pg_log_error("no source specified (--source-pgdata or --source-server)");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
 	if (datadir_source != NULL && connstr_source != NULL)
 	{
 		pg_log_error("only one of --source-pgdata or --source-server can be specified");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
 	if (datadir_target == NULL)
 	{
 		pg_log_error("no target data directory specified (--target-pgdata)");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
 	if (writerecoveryconf && connstr_source == NULL)
 	{
 		pg_log_error("no source server information (--source-server) specified for --write-recovery-conf");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
@@ -239,7 +240,7 @@ main(int argc, char **argv)
 	{
 		pg_log_error("too many command-line arguments (first is \"%s\")",
 					 argv[optind]);
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
@@ -253,8 +254,8 @@ main(int argc, char **argv)
 	if (geteuid() == 0)
 	{
 		pg_log_error("cannot be executed by \"root\"");
-		fprintf(stderr, _("You must run %s as the PostgreSQL superuser.\n"),
-				progname);
+		pg_log_error_hint("You must run %s as the PostgreSQL superuser.",
+						  progname);
 		exit(1);
 	}
 #endif
@@ -263,11 +264,8 @@ main(int argc, char **argv)
 
 	/* Set mask based on PGDATA permissions */
 	if (!GetDataDirectoryCreatePerm(datadir_target))
-	{
-		pg_log_error("could not read permissions of directory \"%s\": %m",
-					 datadir_target);
-		exit(1);
-	}
+		pg_fatal("could not read permissions of directory \"%s\": %m",
+				 datadir_target);
 
 	umask(pg_mode_mask);
 
@@ -1035,16 +1033,15 @@ getRestoreCommand(const char *argv0)
 			strlcpy(full_path, progname, sizeof(full_path));
 
 		if (rc == -1)
-			pg_log_error("The program \"%s\" is needed by %s but was not found in the\n"
-						 "same directory as \"%s\".\n"
-						 "Check your installation.",
-						 "postgres", progname, full_path);
+			pg_fatal("The program \"%s\" is needed by %s but was not found in the\n"
+					 "same directory as \"%s\".\n"
+					 "Check your installation.",
+					 "postgres", progname, full_path);
 		else
-			pg_log_error("The program \"%s\" was found by \"%s\"\n"
-						 "but was not the same version as %s.\n"
-						 "Check your installation.",
-						 "postgres", full_path, progname);
-		exit(1);
+			pg_fatal("The program \"%s\" was found by \"%s\"\n"
+					 "but was not the same version as %s.\n"
+					 "Check your installation.",
+					 "postgres", full_path, progname);
 	}
 
 	/*
@@ -1125,7 +1122,8 @@ ensureCleanShutdown(const char *argv0)
 	if (system(cmd) != 0)
 	{
 		pg_log_error("postgres single-user mode in target cluster failed");
-		pg_fatal("Command was: %s", cmd);
+		pg_log_error_detail("Command was: %s", cmd);
+		exit(1);
 	}
 }
 
diff --git a/src/bin/pg_rewind/pg_rewind.h b/src/bin/pg_rewind/pg_rewind.h
index 388870ce95..393182fe2a 100644
--- a/src/bin/pg_rewind/pg_rewind.h
+++ b/src/bin/pg_rewind/pg_rewind.h
@@ -33,9 +33,6 @@ extern int	targetNentries;
 extern uint64 fetch_size;
 extern uint64 fetch_done;
 
-/* logging support */
-#define pg_fatal(...) do { pg_log_fatal(__VA_ARGS__); exit(1); } while(0)
-
 /* in parsexlog.c */
 extern void extractPageMap(const char *datadir, XLogRecPtr startpoint,
 						   int tliIndex, XLogRecPtr endpoint,
diff --git a/src/bin/pg_rewind/timeline.c b/src/bin/pg_rewind/timeline.c
index df8f82a50c..983388c92b 100644
--- a/src/bin/pg_rewind/timeline.c
+++ b/src/bin/pg_rewind/timeline.c
@@ -73,19 +73,19 @@ rewind_parseTimeLineHistory(char *buffer, TimeLineID targetTLI, int *nentries)
 		{
 			/* expect a numeric timeline ID as first field of line */
 			pg_log_error("syntax error in history file: %s", fline);
-			pg_log_error("Expected a numeric timeline ID.");
+			pg_log_error_detail("Expected a numeric timeline ID.");
 			exit(1);
 		}
 		if (nfields != 3)
 		{
 			pg_log_error("syntax error in history file: %s", fline);
-			pg_log_error("Expected a write-ahead log switchpoint location.");
+			pg_log_error_detail("Expected a write-ahead log switchpoint location.");
 			exit(1);
 		}
 		if (entries && tli <= lasttli)
 		{
 			pg_log_error("invalid data in history file: %s", fline);
-			pg_log_error("Timeline IDs must be in increasing sequence.");
+			pg_log_error_detail("Timeline IDs must be in increasing sequence.");
 			exit(1);
 		}
 
@@ -106,7 +106,7 @@ rewind_parseTimeLineHistory(char *buffer, TimeLineID targetTLI, int *nentries)
 	if (entries && targetTLI <= lasttli)
 	{
 		pg_log_error("invalid data in history file");
-		pg_log_error("Timeline IDs must be less than child timeline's ID.");
+		pg_log_error_detail("Timeline IDs must be less than child timeline's ID.");
 		exit(1);
 	}
 
diff --git a/src/bin/pg_test_fsync/pg_test_fsync.c b/src/bin/pg_test_fsync/pg_test_fsync.c
index ddabf64c58..f7bc199a30 100644
--- a/src/bin/pg_test_fsync/pg_test_fsync.c
+++ b/src/bin/pg_test_fsync/pg_test_fsync.c
@@ -47,10 +47,7 @@ do { \
 	alarm_triggered = false; \
 	if (CreateThread(NULL, 0, process_alarm, NULL, 0, NULL) == \
 		INVALID_HANDLE_VALUE) \
-	{ \
-		pg_log_error("could not create thread for alarm"); \
-		exit(1); \
-	} \
+		pg_fatal("could not create thread for alarm"); \
 	gettimeofday(&start_t, NULL); \
 } while (0)
 #endif
@@ -95,7 +92,7 @@ static int	pg_fsync_writethrough(int fd);
 #endif
 static void print_elapse(struct timeval start_t, struct timeval stop_t, int ops);
 
-#define die(msg) do { pg_log_error("%s: %m", _(msg)); exit(1); } while(0)
+#define die(msg) pg_fatal("%s: %m", _(msg))

 int
@@ -186,24 +183,20 @@ handle_args(int argc, char *argv[])
 					errno != 0 || optval != (unsigned int) optval)
 				{
 					pg_log_error("invalid argument for option %s", "--secs-per-test");
-					fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+					pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 					exit(1);
 				}
 
 				secs_per_test = (unsigned int) optval;
 				if (secs_per_test == 0)
-				{
-					pg_log_error("%s must be in range %u..%u",
-								 "--secs-per-test", 1, UINT_MAX);
-					exit(1);
-				}
+					pg_fatal("%s must be in range %u..%u",
+							 "--secs-per-test", 1, UINT_MAX);
 				break;
 
 			default:
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-						progname);
+				/* getopt_long already emitted a complaint */
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 				exit(1);
-				break;
 		}
 	}
 
@@ -211,8 +204,7 @@ handle_args(int argc, char *argv[])
 	{
 		pg_log_error("too many command-line arguments (first is \"%s\")",
 					 argv[optind]);
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ca86c11292..4ea9217884 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -12,6 +12,9 @@
 
 #include "libpq-fe.h"
 
+/* For now, pg_upgrade does not use common/logging.c; use our own pg_fatal */
+#undef pg_fatal
+
 /* Use port in the private/dynamic port number range */
 #define DEF_PGUPORT			50432
 
diff --git a/src/bin/pg_verifybackup/pg_verifybackup.c b/src/bin/pg_verifybackup/pg_verifybackup.c
index 05cb520c11..299aba7c2c 100644
--- a/src/bin/pg_verifybackup/pg_verifybackup.c
+++ b/src/bin/pg_verifybackup/pg_verifybackup.c
@@ -252,8 +252,8 @@ main(int argc, char **argv)
 				canonicalize_path(wal_directory);
 				break;
 			default:
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-						progname);
+				/* getopt_long already emitted a complaint */
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 				exit(1);
 		}
 	}
@@ -261,9 +261,8 @@ main(int argc, char **argv)
 	/* Get backup directory name */
 	if (optind >= argc)
 	{
-		pg_log_fatal("no backup directory specified");
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error("no backup directory specified");
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 	context.backup_directory = pstrdup(argv[optind++]);
@@ -272,10 +271,9 @@ main(int argc, char **argv)
 	/* Complain if any arguments remain */
 	if (optind < argc)
 	{
-		pg_log_fatal("too many command-line arguments (first is \"%s\")",
+		pg_log_error("too many command-line arguments (first is \"%s\")",
 					 argv[optind]);
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-				progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
@@ -295,16 +293,15 @@ main(int argc, char **argv)
 			if (find_my_exec(argv[0], full_path) < 0)
 				strlcpy(full_path, progname, sizeof(full_path));
 			if (ret == -1)
-				pg_log_fatal("The program \"%s\" is needed by %s but was not found in the\n"
-							 "same directory as \"%s\".\n"
-							 "Check your installation.",
-							 "pg_waldump", "pg_verifybackup", full_path);
+				pg_fatal("The program \"%s\" is needed by %s but was not found in the\n"
+						 "same directory as \"%s\".\n"
+						 "Check your installation.",
+						 "pg_waldump", "pg_verifybackup", full_path);
 			else
-				pg_log_fatal("The program \"%s\" was found by \"%s\"\n"
-							 "but was not the same version as %s.\n"
-							 "Check your installation.",
-							 "pg_waldump", full_path, "pg_verifybackup");
-			exit(1);
+				pg_fatal("The program \"%s\" was found by \"%s\"\n"
+						 "but was not the same version as %s.\n"
+						 "Check your installation.",
+						 "pg_waldump", full_path, "pg_verifybackup");
 		}
 	}
 
@@ -449,7 +446,7 @@ report_manifest_error(JsonManifestParseContext *context, const char *fmt,...)
 	va_list		ap;
 
 	va_start(ap, fmt);
-	pg_log_generic_v(PG_LOG_FATAL, gettext(fmt), ap);
+	pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, gettext(fmt), ap);
 	va_end(ap);
 
 	exit(1);
@@ -840,7 +837,7 @@ report_backup_error(verifier_context *context, const char *pg_restrict fmt,...)
 	va_list		ap;
 
 	va_start(ap, fmt);
-	pg_log_generic_v(PG_LOG_ERROR, gettext(fmt), ap);
+	pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, gettext(fmt), ap);
 	va_end(ap);
 
 	context->saw_any_error = true;
@@ -857,7 +854,7 @@ report_fatal_error(const char *pg_restrict fmt,...)
 	va_list		ap;
 
 	va_start(ap, fmt);
-	pg_log_generic_v(PG_LOG_FATAL, gettext(fmt), ap);
+	pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, gettext(fmt), ap);
 	va_end(ap);
 
 	exit(1);
diff --git a/src/bin/pg_verifybackup/t/005_bad_manifest.pl b/src/bin/pg_verifybackup/t/005_bad_manifest.pl
index 118beb53d7..7890645161 100644
--- a/src/bin/pg_verifybackup/t/005_bad_manifest.pl
+++ b/src/bin/pg_verifybackup/t/005_bad_manifest.pl
@@ -192,7 +192,7 @@ sub test_fatal_error
 
 	my ($test_name, $manifest_contents) = @_;
 
-	test_bad_manifest($test_name, qr/fatal: $test_name/, $manifest_contents);
+	test_bad_manifest($test_name, qr/error: $test_name/, $manifest_contents);
 	return;
 }
 
diff --git a/src/bin/pg_waldump/nls.mk b/src/bin/pg_waldump/nls.mk
index a3d5e88e4f..159638fc00 100644
--- a/src/bin/pg_waldump/nls.mk
+++ b/src/bin/pg_waldump/nls.mk
@@ -2,5 +2,5 @@
 CATALOG_NAME     = pg_waldump
 AVAIL_LANGUAGES  = cs de el es fr ja ko ru sv tr uk vi zh_CN
 GETTEXT_FILES    = $(FRONTEND_COMMON_GETTEXT_FILES) pg_waldump.c
-GETTEXT_TRIGGERS = $(FRONTEND_COMMON_GETTEXT_TRIGGERS) fatal_error
-GETTEXT_FLAGS    = $(FRONTEND_COMMON_GETTEXT_FLAGS) fatal_error:1:c-format
+GETTEXT_TRIGGERS = $(FRONTEND_COMMON_GETTEXT_TRIGGERS)
+GETTEXT_FLAGS    = $(FRONTEND_COMMON_GETTEXT_FLAGS)
diff --git a/src/bin/pg_waldump/pg_waldump.c b/src/bin/pg_waldump/pg_waldump.c
index a6251e1a96..0076cbe1d1 100644
--- a/src/bin/pg_waldump/pg_waldump.c
+++ b/src/bin/pg_waldump/pg_waldump.c
@@ -75,7 +75,6 @@ typedef struct XLogDumpStats
 	Stats		record_stats[RM_NEXT_ID][MAX_XLINFO_TYPES];
 } XLogDumpStats;
 
-#define fatal_error(...) do { pg_log_fatal(__VA_ARGS__); exit(EXIT_FAILURE); } while(0)
 
 /*
  * When sigint is called, just tell the system to exit at the next possible
@@ -161,7 +160,7 @@ open_file_in_directory(const char *directory, const char *fname)
 	fd = open(fpath, O_RDONLY | PG_BINARY, 0);
 
 	if (fd < 0 && errno != ENOENT)
-		fatal_error("could not open file \"%s\": %m", fname);
+		pg_fatal("could not open file \"%s\": %m", fname);
 	return fd;
 }
 
@@ -217,19 +216,19 @@ search_directory(const char *directory, const char *fname)
 			WalSegSz = longhdr->xlp_seg_size;
 
 			if (!IsValidWalSegSize(WalSegSz))
-				fatal_error(ngettext("WAL segment size must be a power of two between 1 MB and 1 GB, but the WAL file \"%s\" header specifies %d byte",
-									 "WAL segment size must be a power of two between 1 MB and 1 GB, but the WAL file \"%s\" header specifies %d bytes",
-									 WalSegSz),
-							fname, WalSegSz);
+				pg_fatal(ngettext("WAL segment size must be a power of two between 1 MB and 1 GB, but the WAL file \"%s\" header specifies %d byte",
+								  "WAL segment size must be a power of two between 1 MB and 1 GB, but the WAL file \"%s\" header specifies %d bytes",
+								  WalSegSz),
+						 fname, WalSegSz);
 		}
 		else
 		{
 			if (errno != 0)
-				fatal_error("could not read file \"%s\": %m",
-							fname);
+				pg_fatal("could not read file \"%s\": %m",
+						 fname);
 			else
-				fatal_error("could not read file \"%s\": read %d of %d",
-							fname, r, XLOG_BLCKSZ);
+				pg_fatal("could not read file \"%s\": read %d of %d",
+						 fname, r, XLOG_BLCKSZ);
 		}
 		close(fd);
 		return true;
@@ -290,9 +289,9 @@ identify_target_directory(char *directory, char *fname)
 
 	/* could not locate WAL file */
 	if (fname)
-		fatal_error("could not locate WAL file \"%s\"", fname);
+		pg_fatal("could not locate WAL file \"%s\"", fname);
 	else
-		fatal_error("could not find any WAL file");
+		pg_fatal("could not find any WAL file");
 
 	return NULL;				/* not reached */
 }
@@ -333,7 +332,7 @@ WALDumpOpenSegment(XLogReaderState *state, XLogSegNo nextSegNo,
 		break;
 	}
 
-	fatal_error("could not find file \"%s\": %m", fname);
+	pg_fatal("could not find file \"%s\": %m", fname);
 }
 
 /*
@@ -382,13 +381,13 @@ WALDumpReadPage(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen,
 		if (errinfo.wre_errno != 0)
 		{
 			errno = errinfo.wre_errno;
-			fatal_error("could not read from file %s, offset %d: %m",
-						fname, errinfo.wre_off);
+			pg_fatal("could not read from file %s, offset %d: %m",
+					 fname, errinfo.wre_off);
 		}
 		else
-			fatal_error("could not read from file %s, offset %d: read %d of %d",
-						fname, errinfo.wre_off, errinfo.wre_read,
-						errinfo.wre_req);
+			pg_fatal("could not read from file %s, offset %d: read %d of %d",
+					 fname, errinfo.wre_off, errinfo.wre_read,
+					 errinfo.wre_req);
 	}
 
 	return count;
@@ -1010,13 +1009,13 @@ main(int argc, char **argv)
 			waldir = directory;
 
 			if (!verify_directory(waldir))
-				fatal_error("could not open directory \"%s\": %m", waldir);
+				pg_fatal("could not open directory \"%s\": %m", waldir);
 		}
 
 		waldir = identify_target_directory(waldir, fname);
 		fd = open_file_in_directory(waldir, fname);
 		if (fd < 0)
-			fatal_error("could not open file \"%s\"", fname);
+			pg_fatal("could not open file \"%s\"", fname);
 		close(fd);
 
 		/* parse position from file */
@@ -1046,15 +1045,15 @@ main(int argc, char **argv)
 
 			fd = open_file_in_directory(waldir, fname);
 			if (fd < 0)
-				fatal_error("could not open file \"%s\"", fname);
+				pg_fatal("could not open file \"%s\"", fname);
 			close(fd);
 
 			/* parse position from file */
 			XLogFromFileName(fname, &private.timeline, &endsegno, WalSegSz);
 
 			if (endsegno < segno)
-				fatal_error("ENDSEG %s is before STARTSEG %s",
-							argv[optind + 1], argv[optind]);
+				pg_fatal("ENDSEG %s is before STARTSEG %s",
+						 argv[optind + 1], argv[optind]);
 
 			if (XLogRecPtrIsInvalid(private.endptr))
 				XLogSegNoOffsetToRecPtr(endsegno + 1, 0, WalSegSz,
@@ -1094,14 +1093,14 @@ main(int argc, char **argv)
 									  .segment_close = WALDumpCloseSegment),
 						   &private);
 	if (!xlogreader_state)
-		fatal_error("out of memory while allocating a WAL reading processor");
+		pg_fatal("out of memory while allocating a WAL reading processor");
 
 	/* first find a valid recptr to start from */
 	first_record = XLogFindNextRecord(xlogreader_state, private.startptr);
 
 	if (first_record == InvalidXLogRecPtr)
-		fatal_error("could not find a valid record after %X/%X",
-					LSN_FORMAT_ARGS(private.startptr));
+		pg_fatal("could not find a valid record after %X/%X",
+				 LSN_FORMAT_ARGS(private.startptr));
 
 	/*
 	 * Display a message that we're skipping data if `from` wasn't a pointer
@@ -1176,15 +1175,15 @@ main(int argc, char **argv)
 		exit(0);
 
 	if (errormsg)
-		fatal_error("error in WAL record at %X/%X: %s",
-					LSN_FORMAT_ARGS(xlogreader_state->ReadRecPtr),
-					errormsg);
+		pg_fatal("error in WAL record at %X/%X: %s",
+				 LSN_FORMAT_ARGS(xlogreader_state->ReadRecPtr),
+				 errormsg);
 
 	XLogReaderFree(xlogreader_state);
 
 	return EXIT_SUCCESS;
 
 bad_argument:
-	fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+	pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 	return EXIT_FAILURE;
 }
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index f166a77e3a..1779e3911c 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -1301,8 +1301,8 @@ executeStatement(PGconn *con, const char *sql)
 	res = PQexec(con, sql);
 	if (PQresultStatus(res) != PGRES_COMMAND_OK)
 	{
-		pg_log_fatal("query failed: %s", PQerrorMessage(con));
-		pg_log_info("query was: %s", sql);
+		pg_log_error("query failed: %s", PQerrorMessage(con));
+		pg_log_error_detail("Query was: %s", sql);
 		exit(1);
 	}
 	PQclear(res);
@@ -1318,7 +1318,7 @@ tryExecuteStatement(PGconn *con, const char *sql)
 	if (PQresultStatus(res) != PGRES_COMMAND_OK)
 	{
 		pg_log_error("%s", PQerrorMessage(con));
-		pg_log_info("(ignoring this error and continuing anyway)");
+		pg_log_error_detail("(ignoring this error and continuing anyway)");
 	}
 	PQclear(res);
 }
@@ -2650,8 +2650,7 @@ evaluateExpr(CState *st, PgBenchExpr *expr, PgBenchValue *retval)
 
 		default:
 			/* internal error which should never occur */
-			pg_log_fatal("unexpected enode type in evaluation: %d", expr->etype);
-			exit(1);
+			pg_fatal("unexpected enode type in evaluation: %d", expr->etype);
 	}
 }
 
@@ -4192,10 +4191,7 @@ initGenerateDataClientSide(PGconn *con)
 	res = PQexec(con, copy_statement);
 
 	if (PQresultStatus(res) != PGRES_COPY_IN)
-	{
-		pg_log_fatal("unexpected copy in result: %s", PQerrorMessage(con));
-		exit(1);
-	}
+		pg_fatal("unexpected copy in result: %s", PQerrorMessage(con));
 	PQclear(res);
 
 	start = pg_time_now();
@@ -4209,10 +4205,7 @@ initGenerateDataClientSide(PGconn *con)
 						  INT64_FORMAT "\t" INT64_FORMAT "\t%d\t\n",
 						  j, k / naccounts + 1, 0);
 		if (PQputline(con, sql.data))
-		{
-			pg_log_fatal("PQputline failed");
-			exit(1);
-		}
+			pg_fatal("PQputline failed");
 
 		if (CancelRequested)
 			break;
@@ -4254,15 +4247,9 @@ initGenerateDataClientSide(PGconn *con)
 		fputc('\n', stderr);	/* Need to move to next line */
 
 	if (PQputline(con, "\\.\n"))
-	{
-		pg_log_fatal("very last PQputline failed");
-		exit(1);
-	}
+		pg_fatal("very last PQputline failed");
 	if (PQendcopy(con))
-	{
-		pg_log_fatal("PQendcopy failed");
-		exit(1);
-	}
+		pg_fatal("PQendcopy failed");
 
 	termPQExpBuffer(&sql);
 
@@ -4402,17 +4389,14 @@ static void
 checkInitSteps(const char *initialize_steps)
 {
 	if (initialize_steps[0] == '\0')
-	{
-		pg_log_fatal("no initialization steps specified");
-		exit(1);
-	}
+		pg_fatal("no initialization steps specified");
 
 	for (const char *step = initialize_steps; *step != '\0'; step++)
 	{
 		if (strchr(ALL_INIT_STEPS " ", *step) == NULL)
 		{
-			pg_log_fatal("unrecognized initialization step \"%c\"", *step);
-			pg_log_info("Allowed step characters are: \"" ALL_INIT_STEPS "\".");
+			pg_log_error("unrecognized initialization step \"%c\"", *step);
+			pg_log_error_detail("Allowed step characters are: \"" ALL_INIT_STEPS "\".");
 			exit(1);
 		}
 	}
@@ -4433,10 +4417,7 @@ runInitSteps(const char *initialize_steps)
 	initPQExpBuffer(&stats);
 
 	if ((con = doConnect()) == NULL)
-	{
-		pg_log_fatal("could not create connection for initialization");
-		exit(1);
-	}
+		pg_fatal("could not create connection for initialization");
 
 	setup_cancel_handler(NULL);
 	SetCancelConn(con);
@@ -4479,7 +4460,7 @@ runInitSteps(const char *initialize_steps)
 			case ' ':
 				break;			/* ignore */
 			default:
-				pg_log_fatal("unrecognized initialization step \"%c\"", *step);
+				pg_log_error("unrecognized initialization step \"%c\"", *step);
 				PQfinish(con);
 				exit(1);
 		}
@@ -4523,21 +4504,18 @@ GetTableInfo(PGconn *con, bool scale_given)
 	{
 		char	   *sqlState = PQresultErrorField(res, PG_DIAG_SQLSTATE);
 
-		pg_log_fatal("could not count number of branches: %s", PQerrorMessage(con));
+		pg_log_error("could not count number of branches: %s", PQerrorMessage(con));
 
 		if (sqlState && strcmp(sqlState, ERRCODE_UNDEFINED_TABLE) == 0)
-			pg_log_info("Perhaps you need to do initialization (\"pgbench -i\") in database \"%s\"",
-						PQdb(con));
+			pg_log_error_hint("Perhaps you need to do initialization (\"pgbench -i\") in database \"%s\".",
+							  PQdb(con));
 
 		exit(1);
 	}
 	scale = atoi(PQgetvalue(res, 0, 0));
 	if (scale < 0)
-	{
-		pg_log_fatal("invalid count(*) from pgbench_branches: \"%s\"",
-					 PQgetvalue(res, 0, 0));
-		exit(1);
-	}
+		pg_fatal("invalid count(*) from pgbench_branches: \"%s\"",
+				 PQgetvalue(res, 0, 0));
 	PQclear(res);
 
 	/* warn if we override user-given -s switch */
@@ -4584,8 +4562,8 @@ GetTableInfo(PGconn *con, bool scale_given)
 		 * This case is unlikely as pgbench already found "pgbench_branches"
 		 * above to compute the scale.
 		 */
-		pg_log_fatal("no pgbench_accounts table found in search_path");
-		pg_log_info("Perhaps you need to do initialization (\"pgbench -i\") in database \"%s\".", PQdb(con));
+		pg_log_error("no pgbench_accounts table found in search_path");
+		pg_log_error_hint("Perhaps you need to do initialization (\"pgbench -i\") in database \"%s\".", PQdb(con));
 		exit(1);
 	}
 	else						/* PQntupes(res) == 1 */
@@ -4607,8 +4585,7 @@ GetTableInfo(PGconn *con, bool scale_given)
 			else
 			{
 				/* possibly a newer version with new partition method */
-				pg_log_fatal("unexpected partition method: \"%s\"", ps);
-				exit(1);
+				pg_fatal("unexpected partition method: \"%s\"", ps);
 			}
 		}
 
@@ -4700,7 +4677,7 @@ syntax_error(const char *source, int lineno,
 	if (command != NULL)
 		appendPQExpBuffer(&buf, " in command \"%s\"", command);
 
-	pg_log_fatal("%s", buf.data);
+	pg_log_error("%s", buf.data);
 
 	termPQExpBuffer(&buf);
 
@@ -5048,9 +5025,8 @@ process_backslash_command(PsqlScanState sstate, const char *source)
 static void
 ConditionError(const char *desc, int cmdn, const char *msg)
 {
-	pg_log_fatal("condition error in script \"%s\" command %d: %s",
-				 desc, cmdn, msg);
-	exit(1);
+	pg_fatal("condition error in script \"%s\" command %d: %s",
+			 desc, cmdn, msg);
 }
 
 /*
@@ -5286,18 +5262,12 @@ process_file(const char *filename, int weight)
 	if (strcmp(filename, "-") == 0)
 		fd = stdin;
 	else if ((fd = fopen(filename, "r")) == NULL)
-	{
-		pg_log_fatal("could not open file \"%s\": %m", filename);
-		exit(1);
-	}
+		pg_fatal("could not open file \"%s\": %m", filename);
 
 	buf = read_file_contents(fd);
 
 	if (ferror(fd))
-	{
-		pg_log_fatal("could not read file \"%s\": %m", filename);
-		exit(1);
-	}
+		pg_fatal("could not read file \"%s\": %m", filename);
 
 	if (fd != stdin)
 		fclose(fd);
@@ -5350,9 +5320,9 @@ findBuiltin(const char *name)
 
 	/* error cases */
 	if (found == 0)
-		pg_log_fatal("no builtin script found for name \"%s\"", name);
+		pg_log_error("no builtin script found for name \"%s\"", name);
 	else						/* found > 1 */
-		pg_log_fatal("ambiguous builtin name: %d builtin scripts found for prefix \"%s\"", found, name);
+		pg_log_error("ambiguous builtin name: %d builtin scripts found for prefix \"%s\"", found, name);
 
 	listAvailableScripts();
 	exit(1);
@@ -5384,16 +5354,10 @@ parseScriptWeight(const char *option, char **script)
 		errno = 0;
 		wtmp = strtol(sep + 1, &badp, 10);
 		if (errno != 0 || badp == sep + 1 || *badp != '\0')
-		{
-			pg_log_fatal("invalid weight specification: %s", sep);
-			exit(1);
-		}
+			pg_fatal("invalid weight specification: %s", sep);
 		if (wtmp > INT_MAX || wtmp < 0)
-		{
-			pg_log_fatal("weight specification out of range (0 .. %d): %lld",
-						 INT_MAX, (long long) wtmp);
-			exit(1);
-		}
+			pg_fatal("weight specification out of range (0 .. %d): %lld",
+					 INT_MAX, (long long) wtmp);
 		weight = wtmp;
 	}
 	else
@@ -5410,16 +5374,10 @@ static void
 addScript(ParsedScript script)
 {
 	if (script.commands == NULL || script.commands[0] == NULL)
-	{
-		pg_log_fatal("empty command list for script \"%s\"", script.desc);
-		exit(1);
-	}
+		pg_fatal("empty command list for script \"%s\"", script.desc);
 
 	if (num_scripts >= MAX_SCRIPTS)
-	{
-		pg_log_fatal("at most %d SQL scripts are allowed", MAX_SCRIPTS);
-		exit(1);
-	}
+		pg_fatal("at most %d SQL scripts are allowed", MAX_SCRIPTS);
 
 	CheckConditional(script);
 
@@ -5735,7 +5693,7 @@ set_random_seed(const char *seed)
 		if (sscanf(seed, "%lu%c", &ulseed, &garbage) != 1)
 		{
 			pg_log_error("unrecognized random seed option \"%s\"", seed);
-			pg_log_info("Expecting an unsigned integer, \"time\" or \"rand\"");
+			pg_log_error_detail("Expecting an unsigned integer, \"time\" or \"rand\".");
 			return false;
 		}
 		iseed = (uint64) ulseed;
@@ -5866,10 +5824,7 @@ main(int argc, char **argv)
 
 	/* set random seed early, because it may be used while parsing scripts. */
 	if (!set_random_seed(getenv("PGBENCH_RANDOM_SEED")))
-	{
-		pg_log_fatal("error while setting random seed from PGBENCH_RANDOM_SEED environment variable");
-		exit(1);
-	}
+		pg_fatal("error while setting random seed from PGBENCH_RANDOM_SEED environment variable");
 
 	while ((c = getopt_long(argc, argv, "iI:h:nvp:dqb:SNc:j:Crs:t:T:U:lf:D:F:M:P:R:L:", long_options, &optindex)) != -1)
 	{
@@ -5916,15 +5871,12 @@ main(int argc, char **argv)
 #else							/* but BSD doesn't ... */
 				if (getrlimit(RLIMIT_OFILE, &rlim) == -1)
 #endif							/* RLIMIT_NOFILE */
-				{
-					pg_log_fatal("getrlimit failed: %m");
-					exit(1);
-				}
+					pg_fatal("getrlimit failed: %m");
 				if (rlim.rlim_cur < nclients + 3)
 				{
-					pg_log_fatal("need at least %d open files, but system limit is %ld",
+					pg_log_error("need at least %d open files, but system limit is %ld",
 								 nclients + 3, (long) rlim.rlim_cur);
-					pg_log_info("Reduce number of clients, or use limit/ulimit to increase the system limit.");
+					pg_log_error_hint("Reduce number of clients, or use limit/ulimit to increase the system limit.");
 					exit(1);
 				}
 #endif							/* HAVE_GETRLIMIT */
@@ -5938,10 +5890,7 @@ main(int argc, char **argv)
 				}
 #ifndef ENABLE_THREAD_SAFETY
 				if (nthreads != 1)
-				{
-					pg_log_fatal("threads are not supported on this platform; use -j1");
-					exit(1);
-				}
+					pg_fatal("threads are not supported on this platform; use -j1");
 #endif							/* !ENABLE_THREAD_SAFETY */
 				break;
 			case 'C':
@@ -6014,10 +5963,7 @@ main(int argc, char **argv)
 					benchmarking_option_set = true;
 
 					if ((p = strchr(optarg, '=')) == NULL || p == optarg || *(p + 1) == '\0')
-					{
-						pg_log_fatal("invalid variable definition: \"%s\"", optarg);
-						exit(1);
-					}
+						pg_fatal("invalid variable definition: \"%s\"", optarg);
 
 					*p++ = '\0';
 					if (!putVariable(&state[0], "option", optarg, p))
@@ -6036,10 +5982,7 @@ main(int argc, char **argv)
 					if (strcmp(optarg, QUERYMODE[querymode]) == 0)
 						break;
 				if (querymode >= NUM_QUERYMODE)
-				{
-					pg_log_fatal("invalid query mode (-M): \"%s\"", optarg);
-					exit(1);
-				}
+					pg_fatal("invalid query mode (-M): \"%s\"", optarg);
 				break;
 			case 'P':
 				benchmarking_option_set = true;
@@ -6055,10 +5998,7 @@ main(int argc, char **argv)
 					benchmarking_option_set = true;
 
 					if (throttle_value <= 0.0)
-					{
-						pg_log_fatal("invalid rate limit: \"%s\"", optarg);
-						exit(1);
-					}
+						pg_fatal("invalid rate limit: \"%s\"", optarg);
 					/* Invert rate limit into per-transaction delay in usec */
 					throttle_delay = 1000000.0 / throttle_value;
 				}
@@ -6068,10 +6008,7 @@ main(int argc, char **argv)
 					double		limit_ms = atof(optarg);
 
 					if (limit_ms <= 0.0)
-					{
-						pg_log_fatal("invalid latency limit: \"%s\"", optarg);
-						exit(1);
-					}
+						pg_fatal("invalid latency limit: \"%s\"", optarg);
 					benchmarking_option_set = true;
 					latency_limit = (int64) (limit_ms * 1000);
 				}
@@ -6092,10 +6029,7 @@ main(int argc, char **argv)
 				benchmarking_option_set = true;
 				sample_rate = atof(optarg);
 				if (sample_rate <= 0.0 || sample_rate > 1.0)
-				{
-					pg_log_fatal("invalid sampling rate: \"%s\"", optarg);
-					exit(1);
-				}
+					pg_fatal("invalid sampling rate: \"%s\"", optarg);
 				break;
 			case 5:				/* aggregate-interval */
 				benchmarking_option_set = true;
@@ -6118,10 +6052,7 @@ main(int argc, char **argv)
 			case 9:				/* random-seed */
 				benchmarking_option_set = true;
 				if (!set_random_seed(optarg))
-				{
-					pg_log_fatal("error while setting random seed from --random-seed option");
-					exit(1);
-				}
+					pg_fatal("error while setting random seed from --random-seed option");
 				break;
 			case 10:			/* list */
 				{
@@ -6144,16 +6075,13 @@ main(int argc, char **argv)
 				else if (pg_strcasecmp(optarg, "hash") == 0)
 					partition_method = PART_HASH;
 				else
-				{
-					pg_log_fatal("invalid partition method, expecting \"range\" or \"hash\", got: \"%s\"",
-								 optarg);
-					exit(1);
-				}
+					pg_fatal("invalid partition method, expecting \"range\" or \"hash\", got: \"%s\"",
+							 optarg);
 				break;
 			default:
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+				/* getopt_long already emitted a complaint */
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 				exit(1);
-				break;
 		}
 	}
 
@@ -6179,10 +6107,7 @@ main(int argc, char **argv)
 	}
 
 	if (total_weight == 0 && !is_init_mode)
-	{
-		pg_log_fatal("total script weight must not be zero");
-		exit(1);
-	}
+		pg_fatal("total script weight must not be zero");
 
 	/* show per script stats if several scripts are used */
 	if (num_scripts > 1)
@@ -6217,25 +6142,19 @@ main(int argc, char **argv)
 
 	if (optind < argc)
 	{
-		pg_log_fatal("too many command-line arguments (first is \"%s\")",
+		pg_log_error("too many command-line arguments (first is \"%s\")",
 					 argv[optind]);
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
 	if (is_init_mode)
 	{
 		if (benchmarking_option_set)
-		{
-			pg_log_fatal("some of the specified options cannot be used in initialization (-i) mode");
-			exit(1);
-		}
+			pg_fatal("some of the specified options cannot be used in initialization (-i) mode");
 
 		if (partitions == 0 && partition_method != PART_NONE)
-		{
-			pg_log_fatal("--partition-method requires greater than zero --partitions");
-			exit(1);
-		}
+			pg_fatal("--partition-method requires greater than zero --partitions");
 
 		/* set default method */
 		if (partitions > 0 && partition_method == PART_NONE)
@@ -6271,17 +6190,11 @@ main(int argc, char **argv)
 	else
 	{
 		if (initialization_option_set)
-		{
-			pg_log_fatal("some of the specified options cannot be used in benchmarking mode");
-			exit(1);
-		}
+			pg_fatal("some of the specified options cannot be used in benchmarking mode");
 	}
 
 	if (nxacts > 0 && duration > 0)
-	{
-		pg_log_fatal("specify either a number of transactions (-t) or a duration (-T), not both");
-		exit(1);
-	}
+		pg_fatal("specify either a number of transactions (-t) or a duration (-T), not both");
 
 	/* Use DEFAULT_NXACTS if neither nxacts nor duration is specified. */
 	if (nxacts <= 0 && duration <= 0)
@@ -6289,47 +6202,26 @@ main(int argc, char **argv)
 
 	/* --sampling-rate may be used only with -l */
 	if (sample_rate > 0.0 && !use_log)
-	{
-		pg_log_fatal("log sampling (--sampling-rate) is allowed only when logging transactions (-l)");
-		exit(1);
-	}
+		pg_fatal("log sampling (--sampling-rate) is allowed only when logging transactions (-l)");
 
 	/* --sampling-rate may not be used with --aggregate-interval */
 	if (sample_rate > 0.0 && agg_interval > 0)
-	{
-		pg_log_fatal("log sampling (--sampling-rate) and aggregation (--aggregate-interval) cannot be used at the same time");
-		exit(1);
-	}
+		pg_fatal("log sampling (--sampling-rate) and aggregation (--aggregate-interval) cannot be used at the same time");
 
 	if (agg_interval > 0 && !use_log)
-	{
-		pg_log_fatal("log aggregation is allowed only when actually logging transactions");
-		exit(1);
-	}
+		pg_fatal("log aggregation is allowed only when actually logging transactions");
 
 	if (!use_log && logfile_prefix)
-	{
-		pg_log_fatal("log file prefix (--log-prefix) is allowed only when logging transactions (-l)");
-		exit(1);
-	}
+		pg_fatal("log file prefix (--log-prefix) is allowed only when logging transactions (-l)");
 
 	if (duration > 0 && agg_interval > duration)
-	{
-		pg_log_fatal("number of seconds for aggregation (%d) must not be higher than test duration (%d)", agg_interval, duration);
-		exit(1);
-	}
+		pg_fatal("number of seconds for aggregation (%d) must not be higher than test duration (%d)", agg_interval, duration);
 
 	if (duration > 0 && agg_interval > 0 && duration % agg_interval != 0)
-	{
-		pg_log_fatal("duration (%d) must be a multiple of aggregation interval (%d)", duration, agg_interval);
-		exit(1);
-	}
+		pg_fatal("duration (%d) must be a multiple of aggregation interval (%d)", duration, agg_interval);
 
 	if (progress_timestamp && progress == 0)
-	{
-		pg_log_fatal("--progress-timestamp is allowed only under --progress");
-		exit(1);
-	}
+		pg_fatal("--progress-timestamp is allowed only under --progress");
 
 	/*
 	 * save main process id in the global variable because process id will be
@@ -6378,10 +6270,7 @@ main(int argc, char **argv)
 	/* opening connection... */
 	con = doConnect();
 	if (con == NULL)
-	{
-		pg_log_fatal("could not create connection for setup");
-		exit(1);
-	}
+		pg_fatal("could not create connection for setup");
 
 	/* report pgbench and server versions */
 	printVersion(con);
@@ -6487,10 +6376,7 @@ main(int argc, char **argv)
 
 	errno = THREAD_BARRIER_INIT(&barrier, nthreads);
 	if (errno != 0)
-	{
-		pg_log_fatal("could not initialize barrier: %m");
-		exit(1);
-	}
+		pg_fatal("could not initialize barrier: %m");
 
 #ifdef ENABLE_THREAD_SAFETY
 	/* start all threads but thread 0 which is executed directly later */
@@ -6502,10 +6388,7 @@ main(int argc, char **argv)
 		errno = THREAD_CREATE(&thread->thread, threadRun, thread);
 
 		if (errno != 0)
-		{
-			pg_log_fatal("could not create thread: %m");
-			exit(1);
-		}
+			pg_fatal("could not create thread: %m");
 	}
 #else
 	Assert(nthreads == 1);
@@ -6569,7 +6452,7 @@ main(int argc, char **argv)
 	THREAD_BARRIER_DESTROY(&barrier);
 
 	if (exit_code != 0)
-		pg_log_fatal("Run was aborted; the above results are incomplete.");
+		pg_log_error("Run was aborted; the above results are incomplete.");
 
 	return exit_code;
 }
@@ -6603,10 +6486,7 @@ threadRun(void *arg)
 		thread->logfile = fopen(logpath, "w");
 
 		if (thread->logfile == NULL)
-		{
-			pg_log_fatal("could not open logfile \"%s\": %m", logpath);
-			exit(1);
-		}
+			pg_fatal("could not open logfile \"%s\": %m", logpath);
 	}
 
 	/* explicitly initialize the state machines */
@@ -6631,9 +6511,8 @@ threadRun(void *arg)
 			if ((state[i].con = doConnect()) == NULL)
 			{
 				/* coldly abort on initial connection failure */
-				pg_log_fatal("could not create connection for client %d",
-							 state[i].id);
-				exit(1);
+				pg_fatal("could not create connection for client %d",
+						 state[i].id);
 			}
 		}
 	}
@@ -6901,10 +6780,7 @@ setalarm(int seconds)
 		!CreateTimerQueueTimer(&timer, queue,
 							   win32_timer_callback, NULL, seconds * 1000, 0,
 							   WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE))
-	{
-		pg_log_fatal("failed to set timer");
-		exit(1);
-	}
+		pg_fatal("failed to set timer");
 }
 
 #endif							/* WIN32 */
@@ -7048,8 +6924,7 @@ add_socket_to_set(socket_set *sa, int fd, int idx)
 		 * Doing a hard exit here is a bit grotty, but it doesn't seem worth
 		 * complicating the API to make it less grotty.
 		 */
-		pg_log_fatal("too many client connections for select()");
-		exit(1);
+		pg_fatal("too many client connections for select()");
 	}
 	FD_SET(fd, &sa->fds);
 	if (fd > sa->maxfd)
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 292cff5df9..1380a95b3e 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -232,7 +232,7 @@ HandleSlashCmds(PsqlScanState scan_state,
 	{
 		pg_log_error("invalid command \\%s", cmd);
 		if (pset.cur_cmd_interactive)
-			pg_log_info("Try \\? for help.");
+			pg_log_error_hint("Try \\? for help.");
 		status = PSQL_CMD_ERROR;
 	}
 
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index d65b9a124f..f9c0a2a4d2 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -302,7 +302,7 @@ CheckConnection(void)
 	{
 		if (!pset.cur_cmd_interactive)
 		{
-			pg_log_fatal("connection to server was lost");
+			pg_log_error("connection to server was lost");
 			exit(EXIT_BADCONN);
 		}
 
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 56afa6817e..e24979cba4 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -58,10 +58,7 @@ usage(unsigned short int pager)
 	{
 		user = get_user_name(&errstr);
 		if (!user)
-		{
-			pg_log_fatal("%s", errstr);
-			exit(EXIT_FAILURE);
-		}
+			pg_fatal("%s", errstr);
 	}
 
 	/*
diff --git a/src/bin/psql/mainloop.c b/src/bin/psql/mainloop.c
index e5c976fc4f..b0c4177a20 100644
--- a/src/bin/psql/mainloop.c
+++ b/src/bin/psql/mainloop.c
@@ -77,10 +77,7 @@ MainLoop(FILE *source)
 	if (PQExpBufferBroken(query_buf) ||
 		PQExpBufferBroken(previous_buf) ||
 		PQExpBufferBroken(history_buf))
-	{
-		pg_log_error("out of memory");
-		exit(EXIT_FAILURE);
-	}
+		pg_fatal("out of memory");
 
 	/* main loop to get queries and execute them */
 	while (successResult == EXIT_SUCCESS)
@@ -398,10 +395,7 @@ MainLoop(FILE *source)
 			prompt_status = prompt_tmp;
 
 			if (PQExpBufferBroken(query_buf))
-			{
-				pg_log_error("out of memory");
-				exit(EXIT_FAILURE);
-			}
+				pg_fatal("out of memory");
 
 			/*
 			 * Increase statement line number counter for each linebreak added
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
index be9dec749d..127b578b34 100644
--- a/src/bin/psql/startup.c
+++ b/src/bin/psql/startup.c
@@ -216,10 +216,7 @@ main(int argc, char *argv[])
 
 	/* Bail out if -1 was specified but will be ignored. */
 	if (options.single_txn && options.actions.head == NULL)
-	{
-		pg_log_fatal("-1 can only be used in non-interactive mode");
-		exit(EXIT_FAILURE);
-	}
+		pg_fatal("-1 can only be used in non-interactive mode");
 
 	if (!pset.popt.topt.fieldSep.separator &&
 		!pset.popt.topt.fieldSep.separator_zero)
@@ -342,11 +339,8 @@ main(int argc, char *argv[])
 	{
 		pset.logfile = fopen(options.logfilename, "a");
 		if (!pset.logfile)
-		{
-			pg_log_fatal("could not open log file \"%s\": %m",
-						 options.logfilename);
-			exit(EXIT_FAILURE);
-		}
+			pg_fatal("could not open log file \"%s\": %m",
+					 options.logfilename);
 	}
 
 	if (!options.no_psqlrc)
@@ -607,10 +601,7 @@ parse_psql_options(int argc, char *argv[], struct adhoc_opts *options)
 					}
 
 					if (!result)
-					{
-						pg_log_fatal("could not set printing parameter \"%s\"", value);
-						exit(EXIT_FAILURE);
-					}
+						pg_fatal("could not set printing parameter \"%s\"", value);
 
 					free(value);
 					break;
@@ -716,10 +707,10 @@ parse_psql_options(int argc, char *argv[], struct adhoc_opts *options)
 				break;
 			default:
 		unknown_option:
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-						pset.progname);
+				/* getopt_long already emitted a complaint */
+				pg_log_error_hint("Try \"%s --help\" for more information.",
+								  pset.progname);
 				exit(EXIT_FAILURE);
-				break;
 		}
 	}
 
@@ -781,10 +772,7 @@ process_psqlrc(char *argv0)
 	char	   *envrc = getenv("PSQLRC");
 
 	if (find_my_exec(argv0, my_exec_path) < 0)
-	{
-		pg_log_fatal("could not find own program executable");
-		exit(EXIT_FAILURE);
-	}
+		pg_fatal("could not find own program executable");
 
 	get_etc_path(my_exec_path, etc_path);
 
diff --git a/src/bin/scripts/clusterdb.c b/src/bin/scripts/clusterdb.c
index 4c97bd41d7..df1766679b 100644
--- a/src/bin/scripts/clusterdb.c
+++ b/src/bin/scripts/clusterdb.c
@@ -109,7 +109,8 @@ main(int argc, char *argv[])
 				maintenance_db = pg_strdup(optarg);
 				break;
 			default:
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+				/* getopt_long already emitted a complaint */
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 				exit(1);
 		}
 	}
@@ -128,7 +129,7 @@ main(int argc, char *argv[])
 	{
 		pg_log_error("too many command-line arguments (first is \"%s\")",
 					 argv[optind]);
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
@@ -144,16 +145,10 @@ main(int argc, char *argv[])
 	if (alldb)
 	{
 		if (dbname)
-		{
-			pg_log_error("cannot cluster all databases and a specific one at the same time");
-			exit(1);
-		}
+			pg_fatal("cannot cluster all databases and a specific one at the same time");
 
 		if (tables.head != NULL)
-		{
-			pg_log_error("cannot cluster specific table(s) in all databases");
-			exit(1);
-		}
+			pg_fatal("cannot cluster specific table(s) in all databases");
 
 		cparams.dbname = maintenance_db;
 
diff --git a/src/bin/scripts/createdb.c b/src/bin/scripts/createdb.c
index b0c6805bc9..89a1a49bd5 100644
--- a/src/bin/scripts/createdb.c
+++ b/src/bin/scripts/createdb.c
@@ -120,7 +120,8 @@ main(int argc, char *argv[])
 				maintenance_db = pg_strdup(optarg);
 				break;
 			default:
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+				/* getopt_long already emitted a complaint */
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 				exit(1);
 		}
 	}
@@ -139,22 +140,16 @@ main(int argc, char *argv[])
 		default:
 			pg_log_error("too many command-line arguments (first is \"%s\")",
 						 argv[optind + 2]);
-			fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+			pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 			exit(1);
 	}
 
 	if (locale)
 	{
 		if (lc_ctype)
-		{
-			pg_log_error("only one of --locale and --lc-ctype can be specified");
-			exit(1);
-		}
+			pg_fatal("only one of --locale and --lc-ctype can be specified");
 		if (lc_collate)
-		{
-			pg_log_error("only one of --locale and --lc-collate can be specified");
-			exit(1);
-		}
+			pg_fatal("only one of --locale and --lc-collate can be specified");
 		lc_ctype = locale;
 		lc_collate = locale;
 	}
@@ -162,10 +157,7 @@ main(int argc, char *argv[])
 	if (encoding)
 	{
 		if (pg_char_to_encoding(encoding) < 0)
-		{
-			pg_log_error("\"%s\" is not a valid encoding name", encoding);
-			exit(1);
-		}
+			pg_fatal("\"%s\" is not a valid encoding name", encoding);
 	}
 
 	if (dbname == NULL)
diff --git a/src/bin/scripts/createuser.c b/src/bin/scripts/createuser.c
index d6ce04a809..bfba0d09d1 100644
--- a/src/bin/scripts/createuser.c
+++ b/src/bin/scripts/createuser.c
@@ -166,7 +166,8 @@ main(int argc, char *argv[])
 				interactive = true;
 				break;
 			default:
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+				/* getopt_long already emitted a complaint */
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 				exit(1);
 		}
 	}
@@ -181,7 +182,7 @@ main(int argc, char *argv[])
 		default:
 			pg_log_error("too many command-line arguments (first is \"%s\")",
 						 argv[optind + 1]);
-			fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+			pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 			exit(1);
 	}
 
@@ -274,11 +275,8 @@ main(int argc, char *argv[])
 												   newuser,
 												   NULL);
 		if (!encrypted_password)
-		{
-			pg_log_error("password encryption failed: %s",
-						 PQerrorMessage(conn));
-			exit(1);
-		}
+			pg_fatal("password encryption failed: %s",
+					 PQerrorMessage(conn));
 		appendStringLiteralConn(&sql, encrypted_password, conn);
 		PQfreemem(encrypted_password);
 	}
diff --git a/src/bin/scripts/dropdb.c b/src/bin/scripts/dropdb.c
index 7e321dd11b..afc00dac78 100644
--- a/src/bin/scripts/dropdb.c
+++ b/src/bin/scripts/dropdb.c
@@ -100,7 +100,8 @@ main(int argc, char *argv[])
 				maintenance_db = pg_strdup(optarg);
 				break;
 			default:
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+				/* getopt_long already emitted a complaint */
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 				exit(1);
 		}
 	}
@@ -109,7 +110,7 @@ main(int argc, char *argv[])
 	{
 		case 0:
 			pg_log_error("missing required argument database name");
-			fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+			pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 			exit(1);
 		case 1:
 			dbname = argv[optind];
@@ -117,7 +118,7 @@ main(int argc, char *argv[])
 		default:
 			pg_log_error("too many command-line arguments (first is \"%s\")",
 						 argv[optind + 1]);
-			fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+			pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 			exit(1);
 	}
 
diff --git a/src/bin/scripts/dropuser.c b/src/bin/scripts/dropuser.c
index dfe4a5088c..82c1f35ab2 100644
--- a/src/bin/scripts/dropuser.c
+++ b/src/bin/scripts/dropuser.c
@@ -91,7 +91,8 @@ main(int argc, char *argv[])
 				/* this covers the long options */
 				break;
 			default:
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+				/* getopt_long already emitted a complaint */
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 				exit(1);
 		}
 	}
@@ -106,7 +107,7 @@ main(int argc, char *argv[])
 		default:
 			pg_log_error("too many command-line arguments (first is \"%s\")",
 						 argv[optind + 1]);
-			fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+			pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 			exit(1);
 	}
 
@@ -119,7 +120,7 @@ main(int argc, char *argv[])
 		else
 		{
 			pg_log_error("missing required argument role name");
-			fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+			pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 			exit(1);
 		}
 	}
diff --git a/src/bin/scripts/pg_isready.c b/src/bin/scripts/pg_isready.c
index a7653b3eaf..1aa834742d 100644
--- a/src/bin/scripts/pg_isready.c
+++ b/src/bin/scripts/pg_isready.c
@@ -93,7 +93,8 @@ main(int argc, char **argv)
 				pguser = pg_strdup(optarg);
 				break;
 			default:
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+				/* getopt_long already emitted a complaint */
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 
 				/*
 				 * We need to make sure we don't return 1 here because someone
@@ -107,7 +108,7 @@ main(int argc, char **argv)
 	{
 		pg_log_error("too many command-line arguments (first is \"%s\")",
 					 argv[optind]);
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 
 		/*
 		 * We need to make sure we don't return 1 here because someone
diff --git a/src/bin/scripts/reindexdb.c b/src/bin/scripts/reindexdb.c
index c292d43203..f3b03ec325 100644
--- a/src/bin/scripts/reindexdb.c
+++ b/src/bin/scripts/reindexdb.c
@@ -170,7 +170,8 @@ main(int argc, char *argv[])
 				tablespace = pg_strdup(optarg);
 				break;
 			default:
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+				/* getopt_long already emitted a complaint */
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 				exit(1);
 		}
 	}
@@ -189,7 +190,7 @@ main(int argc, char *argv[])
 	{
 		pg_log_error("too many command-line arguments (first is \"%s\")",
 					 argv[optind]);
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
@@ -205,30 +206,15 @@ main(int argc, char *argv[])
 	if (alldb)
 	{
 		if (dbname)
-		{
-			pg_log_error("cannot reindex all databases and a specific one at the same time");
-			exit(1);
-		}
+			pg_fatal("cannot reindex all databases and a specific one at the same time");
 		if (syscatalog)
-		{
-			pg_log_error("cannot reindex all databases and system catalogs at the same time");
-			exit(1);
-		}
+			pg_fatal("cannot reindex all databases and system catalogs at the same time");
 		if (schemas.head != NULL)
-		{
-			pg_log_error("cannot reindex specific schema(s) in all databases");
-			exit(1);
-		}
+			pg_fatal("cannot reindex specific schema(s) in all databases");
 		if (tables.head != NULL)
-		{
-			pg_log_error("cannot reindex specific table(s) in all databases");
-			exit(1);
-		}
+			pg_fatal("cannot reindex specific table(s) in all databases");
 		if (indexes.head != NULL)
-		{
-			pg_log_error("cannot reindex specific index(es) in all databases");
-			exit(1);
-		}
+			pg_fatal("cannot reindex specific index(es) in all databases");
 
 		cparams.dbname = maintenance_db;
 
@@ -238,26 +224,14 @@ main(int argc, char *argv[])
 	else if (syscatalog)
 	{
 		if (schemas.head != NULL)
-		{
-			pg_log_error("cannot reindex specific schema(s) and system catalogs at the same time");
-			exit(1);
-		}
+			pg_fatal("cannot reindex specific schema(s) and system catalogs at the same time");
 		if (tables.head != NULL)
-		{
-			pg_log_error("cannot reindex specific table(s) and system catalogs at the same time");
-			exit(1);
-		}
+			pg_fatal("cannot reindex specific table(s) and system catalogs at the same time");
 		if (indexes.head != NULL)
-		{
-			pg_log_error("cannot reindex specific index(es) and system catalogs at the same time");
-			exit(1);
-		}
+			pg_fatal("cannot reindex specific index(es) and system catalogs at the same time");
 
 		if (concurrentCons > 1)
-		{
-			pg_log_error("cannot use multiple jobs to reindex system catalogs");
-			exit(1);
-		}
+			pg_fatal("cannot use multiple jobs to reindex system catalogs");
 
 		if (dbname == NULL)
 		{
@@ -283,10 +257,7 @@ main(int argc, char *argv[])
 		 * depending on the same relation.
 		 */
 		if (concurrentCons > 1 && indexes.head != NULL)
-		{
-			pg_log_error("cannot use multiple jobs to reindex indexes");
-			exit(1);
-		}
+			pg_fatal("cannot use multiple jobs to reindex indexes");
 
 		if (dbname == NULL)
 		{
@@ -349,17 +320,15 @@ reindex_one_database(ConnParams *cparams, ReindexType type,
 	if (concurrently && PQserverVersion(conn) < 120000)
 	{
 		PQfinish(conn);
-		pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-					 "concurrently", "12");
-		exit(1);
+		pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+				 "concurrently", "12");
 	}
 
 	if (tablespace && PQserverVersion(conn) < 140000)
 	{
 		PQfinish(conn);
-		pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-					 "tablespace", "14");
-		exit(1);
+		pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+				 "tablespace", "14");
 	}
 
 	if (!parallel)
diff --git a/src/bin/scripts/vacuumdb.c b/src/bin/scripts/vacuumdb.c
index 4f6917fd39..92f1ffe147 100644
--- a/src/bin/scripts/vacuumdb.c
+++ b/src/bin/scripts/vacuumdb.c
@@ -237,7 +237,8 @@ main(int argc, char *argv[])
 				vacopts.process_toast = false;
 				break;
 			default:
-				fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+				/* getopt_long already emitted a complaint */
+				pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 				exit(1);
 		}
 	}
@@ -256,54 +257,33 @@ main(int argc, char *argv[])
 	{
 		pg_log_error("too many command-line arguments (first is \"%s\")",
 					 argv[optind]);
-		fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+		pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 		exit(1);
 	}
 
 	if (vacopts.analyze_only)
 	{
 		if (vacopts.full)
-		{
-			pg_log_error("cannot use the \"%s\" option when performing only analyze",
-						 "full");
-			exit(1);
-		}
+			pg_fatal("cannot use the \"%s\" option when performing only analyze",
+					 "full");
 		if (vacopts.freeze)
-		{
-			pg_log_error("cannot use the \"%s\" option when performing only analyze",
-						 "freeze");
-			exit(1);
-		}
+			pg_fatal("cannot use the \"%s\" option when performing only analyze",
+					 "freeze");
 		if (vacopts.disable_page_skipping)
-		{
-			pg_log_error("cannot use the \"%s\" option when performing only analyze",
-						 "disable-page-skipping");
-			exit(1);
-		}
+			pg_fatal("cannot use the \"%s\" option when performing only analyze",
+					 "disable-page-skipping");
 		if (vacopts.no_index_cleanup)
-		{
-			pg_log_error("cannot use the \"%s\" option when performing only analyze",
-						 "no-index-cleanup");
-			exit(1);
-		}
+			pg_fatal("cannot use the \"%s\" option when performing only analyze",
+					 "no-index-cleanup");
 		if (vacopts.force_index_cleanup)
-		{
-			pg_log_error("cannot use the \"%s\" option when performing only analyze",
-						 "force-index-cleanup");
-			exit(1);
-		}
+			pg_fatal("cannot use the \"%s\" option when performing only analyze",
+					 "force-index-cleanup");
 		if (!vacopts.do_truncate)
-		{
-			pg_log_error("cannot use the \"%s\" option when performing only analyze",
-						 "no-truncate");
-			exit(1);
-		}
+			pg_fatal("cannot use the \"%s\" option when performing only analyze",
+					 "no-truncate");
 		if (!vacopts.process_toast)
-		{
-			pg_log_error("cannot use the \"%s\" option when performing only analyze",
-						 "no-process-toast");
-			exit(1);
-		}
+			pg_fatal("cannot use the \"%s\" option when performing only analyze",
+					 "no-process-toast");
 		/* allow 'and_analyze' with 'analyze_only' */
 	}
 
@@ -311,26 +291,17 @@ main(int argc, char *argv[])
 	if (vacopts.parallel_workers >= 0)
 	{
 		if (vacopts.analyze_only)
-		{
-			pg_log_error("cannot use the \"%s\" option when performing only analyze",
-						 "parallel");
-			exit(1);
-		}
+			pg_fatal("cannot use the \"%s\" option when performing only analyze",
+					 "parallel");
 		if (vacopts.full)
-		{
-			pg_log_error("cannot use the \"%s\" option when performing full vacuum",
-						 "parallel");
-			exit(1);
-		}
+			pg_fatal("cannot use the \"%s\" option when performing full vacuum",
+					 "parallel");
 	}
 
 	/* Prohibit --no-index-cleanup and --force-index-cleanup together */
 	if (vacopts.no_index_cleanup && vacopts.force_index_cleanup)
-	{
-		pg_log_error("cannot use the \"%s\" option with the \"%s\" option",
-					 "no-index-cleanup", "force-index-cleanup");
-		exit(1);
-	}
+		pg_fatal("cannot use the \"%s\" option with the \"%s\" option",
+				 "no-index-cleanup", "force-index-cleanup");
 
 	/* fill cparams except for dbname, which is set below */
 	cparams.pghost = host;
@@ -348,15 +319,9 @@ main(int argc, char *argv[])
 	if (alldb)
 	{
 		if (dbname)
-		{
-			pg_log_error("cannot vacuum all databases and a specific one at the same time");
-			exit(1);
-		}
+			pg_fatal("cannot vacuum all databases and a specific one at the same time");
 		if (tables.head != NULL)
-		{
-			pg_log_error("cannot vacuum specific table(s) in all databases");
-			exit(1);
-		}
+			pg_fatal("cannot vacuum specific table(s) in all databases");
 
 		cparams.dbname = maintenance_db;
 
@@ -457,71 +422,56 @@ vacuum_one_database(ConnParams *cparams,
 	if (vacopts->disable_page_skipping && PQserverVersion(conn) < 90600)
 	{
 		PQfinish(conn);
-		pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-					 "disable-page-skipping", "9.6");
-		exit(1);
+		pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+				 "disable-page-skipping", "9.6");
 	}
 
 	if (vacopts->no_index_cleanup && PQserverVersion(conn) < 120000)
 	{
 		PQfinish(conn);
-		pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-					 "no-index-cleanup", "12");
-		exit(1);
+		pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+				 "no-index-cleanup", "12");
 	}
 
 	if (vacopts->force_index_cleanup && PQserverVersion(conn) < 120000)
 	{
 		PQfinish(conn);
-		pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-					 "force-index-cleanup", "12");
-		exit(1);
+		pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+				 "force-index-cleanup", "12");
 	}
 
 	if (!vacopts->do_truncate && PQserverVersion(conn) < 120000)
 	{
 		PQfinish(conn);
-		pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-					 "no-truncate", "12");
-		exit(1);
+		pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+				 "no-truncate", "12");
 	}
 
 	if (!vacopts->process_toast && PQserverVersion(conn) < 140000)
 	{
 		PQfinish(conn);
-		pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-					 "no-process-toast", "14");
-		exit(1);
+		pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+				 "no-process-toast", "14");
 	}
 
 	if (vacopts->skip_locked && PQserverVersion(conn) < 120000)
 	{
 		PQfinish(conn);
-		pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-					 "skip-locked", "12");
-		exit(1);
+		pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+				 "skip-locked", "12");
 	}
 
 	if (vacopts->min_xid_age != 0 && PQserverVersion(conn) < 90600)
-	{
-		pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-					 "--min-xid-age", "9.6");
-		exit(1);
-	}
+		pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+				 "--min-xid-age", "9.6");
 
 	if (vacopts->min_mxid_age != 0 && PQserverVersion(conn) < 90600)
-	{
-		pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-					 "--min-mxid-age", "9.6");
-		exit(1);
-	}
+		pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+				 "--min-mxid-age", "9.6");
 
 	if (vacopts->parallel_workers >= 0 && PQserverVersion(conn) < 130000)
-	{
-		pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
-					 "--parallel", "13");
-		exit(1);
-	}
+		pg_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+				 "--parallel", "13");
 
 	if (!quiet)
 	{
diff --git a/src/common/controldata_utils.c b/src/common/controldata_utils.c
index 348f046a44..4c0da6e124 100644
--- a/src/common/controldata_utils.c
+++ b/src/common/controldata_utils.c
@@ -70,11 +70,8 @@ get_controlfile(const char *DataDir, bool *crc_ok_p)
 						ControlFilePath)));
 #else
 	if ((fd = open(ControlFilePath, O_RDONLY | PG_BINARY, 0)) == -1)
-	{
-		pg_log_fatal("could not open file \"%s\" for reading: %m",
-					 ControlFilePath);
-		exit(EXIT_FAILURE);
-	}
+		pg_fatal("could not open file \"%s\" for reading: %m",
+				 ControlFilePath);
 #endif
 
 	r = read(fd, ControlFile, sizeof(ControlFileData));
@@ -86,10 +83,7 @@ get_controlfile(const char *DataDir, bool *crc_ok_p)
 					(errcode_for_file_access(),
 					 errmsg("could not read file \"%s\": %m", ControlFilePath)));
 #else
-		{
-			pg_log_fatal("could not read file \"%s\": %m", ControlFilePath);
-			exit(EXIT_FAILURE);
-		}
+			pg_fatal("could not read file \"%s\": %m", ControlFilePath);
 #endif
 		else
 #ifndef FRONTEND
@@ -98,11 +92,8 @@ get_controlfile(const char *DataDir, bool *crc_ok_p)
 					 errmsg("could not read file \"%s\": read %d of %zu",
 							ControlFilePath, r, sizeof(ControlFileData))));
 #else
-		{
-			pg_log_fatal("could not read file \"%s\": read %d of %zu",
-						 ControlFilePath, r, sizeof(ControlFileData));
-			exit(EXIT_FAILURE);
-		}
+			pg_fatal("could not read file \"%s\": read %d of %zu",
+					 ControlFilePath, r, sizeof(ControlFileData));
 #endif
 	}
 
@@ -114,10 +105,7 @@ get_controlfile(const char *DataDir, bool *crc_ok_p)
 						ControlFilePath)));
 #else
 	if (close(fd) != 0)
-	{
-		pg_log_fatal("could not close file \"%s\": %m", ControlFilePath);
-		exit(EXIT_FAILURE);
-	}
+		pg_fatal("could not close file \"%s\": %m", ControlFilePath);
 #endif
 
 	/* Check the CRC. */
@@ -203,10 +191,7 @@ update_controlfile(const char *DataDir,
 #else
 	if ((fd = open(ControlFilePath, O_WRONLY | PG_BINARY,
 				   pg_file_create_mode)) == -1)
-	{
-		pg_log_fatal("could not open file \"%s\": %m", ControlFilePath);
-		exit(EXIT_FAILURE);
-	}
+		pg_fatal("could not open file \"%s\": %m", ControlFilePath);
 #endif
 
 	errno = 0;
@@ -225,8 +210,7 @@ update_controlfile(const char *DataDir,
 				 errmsg("could not write file \"%s\": %m",
 						ControlFilePath)));
 #else
-		pg_log_fatal("could not write file \"%s\": %m", ControlFilePath);
-		exit(EXIT_FAILURE);
+		pg_fatal("could not write file \"%s\": %m", ControlFilePath);
 #endif
 	}
 #ifndef FRONTEND
@@ -245,10 +229,7 @@ update_controlfile(const char *DataDir,
 		pgstat_report_wait_end();
 #else
 		if (fsync(fd) != 0)
-		{
-			pg_log_fatal("could not fsync file \"%s\": %m", ControlFilePath);
-			exit(EXIT_FAILURE);
-		}
+			pg_fatal("could not fsync file \"%s\": %m", ControlFilePath);
 #endif
 	}
 
@@ -260,8 +241,7 @@ update_controlfile(const char *DataDir,
 				 errmsg("could not close file \"%s\": %m",
 						ControlFilePath)));
 #else
-		pg_log_fatal("could not close file \"%s\": %m", ControlFilePath);
-		exit(EXIT_FAILURE);
+		pg_fatal("could not close file \"%s\": %m", ControlFilePath);
 #endif
 	}
 }
diff --git a/src/common/file_utils.c b/src/common/file_utils.c
index 7138068633..19d308ad1f 100644
--- a/src/common/file_utils.c
+++ b/src/common/file_utils.c
@@ -300,7 +300,7 @@ fsync_fname(const char *fname, bool isdir)
 	 */
 	if (returncode != 0 && !(isdir && (errno == EBADF || errno == EINVAL)))
 	{
-		pg_log_fatal("could not fsync file \"%s\": %m", fname);
+		pg_log_error("could not fsync file \"%s\": %m", fname);
 		(void) close(fd);
 		exit(EXIT_FAILURE);
 	}
@@ -370,7 +370,7 @@ durable_rename(const char *oldfile, const char *newfile)
 	{
 		if (fsync(fd) != 0)
 		{
-			pg_log_fatal("could not fsync file \"%s\": %m", newfile);
+			pg_log_error("could not fsync file \"%s\": %m", newfile);
 			close(fd);
 			exit(EXIT_FAILURE);
 		}
@@ -448,7 +448,7 @@ get_dirent_type(const char *path,
 		{
 			result = PGFILETYPE_ERROR;
 #ifdef FRONTEND
-			pg_log_generic(elevel, "could not stat file \"%s\": %m", path);
+			pg_log_generic(elevel, PG_LOG_PRIMARY, "could not stat file \"%s\": %m", path);
 #else
 			ereport(elevel,
 					(errcode_for_file_access(),
diff --git a/src/common/logging.c b/src/common/logging.c
index 9a076bb812..18d6669f27 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/common/restricted_token.c b/src/common/restricted_token.c
index 48b1ce0585..82b74b565e 100644
--- a/src/common/restricted_token.c
+++ b/src/common/restricted_token.c
@@ -190,10 +190,7 @@ get_restricted_token(void)
 			WaitForSingleObject(pi.hProcess, INFINITE);
 
 			if (!GetExitCodeProcess(pi.hProcess, &x))
-			{
-				pg_log_error("could not get exit code from subprocess: error code %lu", GetLastError());
-				exit(1);
-			}
+				pg_fatal("could not get exit code from subprocess: error code %lu", GetLastError());
 			exit(x);
 		}
 		pg_free(cmdline);
diff --git a/src/fe_utils/archive.c b/src/fe_utils/archive.c
index 361c1c25ea..53d42c2be4 100644
--- a/src/fe_utils/archive.c
+++ b/src/fe_utils/archive.c
@@ -49,10 +49,7 @@ RestoreArchivedFile(const char *path, const char *xlogfname,
 	xlogRestoreCmd = BuildRestoreCommand(restoreCommand, xlogpath,
 										 xlogfname, NULL);
 	if (xlogRestoreCmd == NULL)
-	{
-		pg_log_fatal("cannot use restore_command with %%r placeholder");
-		exit(1);
-	}
+		pg_fatal("cannot use restore_command with %%r placeholder");
 
 	/*
 	 * Execute restore_command, which should copy the missing file from
@@ -70,22 +67,16 @@ RestoreArchivedFile(const char *path, const char *xlogfname,
 		if (stat(xlogpath, &stat_buf) == 0)
 		{
 			if (expectedSize > 0 && stat_buf.st_size != expectedSize)
-			{
-				pg_log_fatal("unexpected file size for \"%s\": %lld instead of %lld",
-							 xlogfname, (long long int) stat_buf.st_size,
-							 (long long int) expectedSize);
-				exit(1);
-			}
+				pg_fatal("unexpected file size for \"%s\": %lld instead of %lld",
+						 xlogfname, (long long int) stat_buf.st_size,
+						 (long long int) expectedSize);
 			else
 			{
 				int			xlogfd = open(xlogpath, O_RDONLY | PG_BINARY, 0);
 
 				if (xlogfd < 0)
-				{
-					pg_log_fatal("could not open file \"%s\" restored from archive: %m",
-								 xlogpath);
-					exit(1);
-				}
+					pg_fatal("could not open file \"%s\" restored from archive: %m",
+							 xlogpath);
 				else
 					return xlogfd;
 			}
@@ -93,11 +84,8 @@ RestoreArchivedFile(const char *path, const char *xlogfname,
 		else
 		{
 			if (errno != ENOENT)
-			{
-				pg_log_fatal("could not stat file \"%s\": %m",
-							 xlogpath);
-				exit(1);
-			}
+				pg_fatal("could not stat file \"%s\": %m",
+						 xlogpath);
 		}
 	}
 
@@ -108,11 +96,8 @@ RestoreArchivedFile(const char *path, const char *xlogfname,
 	 * fatal too.
 	 */
 	if (wait_result_is_any_signal(rc, true))
-	{
-		pg_log_fatal("restore_command failed: %s",
-					 wait_result_to_str(rc));
-		exit(1);
-	}
+		pg_fatal("restore_command failed: %s",
+				 wait_result_to_str(rc));
 
 	/*
 	 * The file is not available, so just let the caller decide what to do
diff --git a/src/fe_utils/connect_utils.c b/src/fe_utils/connect_utils.c
index a30c66f13a..f2e583f9fa 100644
--- a/src/fe_utils/connect_utils.c
+++ b/src/fe_utils/connect_utils.c
@@ -88,11 +88,8 @@ connectDatabase(const ConnParams *cparams, const char *progname,
 		conn = PQconnectdbParams(keywords, values, true);
 
 		if (!conn)
-		{
-			pg_log_error("could not connect to database %s: out of memory",
-						 cparams->dbname);
-			exit(1);
-		}
+			pg_fatal("could not connect to database %s: out of memory",
+					 cparams->dbname);
 
 		/*
 		 * No luck?  Trying asking (again) for a password.
@@ -117,8 +114,7 @@ connectDatabase(const ConnParams *cparams, const char *progname,
 			PQfinish(conn);
 			return NULL;
 		}
-		pg_log_error("%s", PQerrorMessage(conn));
-		exit(1);
+		pg_fatal("%s", PQerrorMessage(conn));
 	}
 
 	/* Start strict; callers may override this. */
diff --git a/src/fe_utils/parallel_slot.c b/src/fe_utils/parallel_slot.c
index 5896a8a6ca..684327885d 100644
--- a/src/fe_utils/parallel_slot.c
+++ b/src/fe_utils/parallel_slot.c
@@ -298,10 +298,7 @@ connect_slot(ParallelSlotArray *sa, int slotno, const char *dbname)
 	sa->cparams->override_dbname = old_override;
 
 	if (PQsocket(slot->connection) >= FD_SETSIZE)
-	{
-		pg_log_fatal("too many jobs for this platform");
-		exit(1);
-	}
+		pg_fatal("too many jobs for this platform");
 
 	/* Setup the connection using the supplied command, if any. */
 	if (sa->initcmd)
diff --git a/src/fe_utils/query_utils.c b/src/fe_utils/query_utils.c
index 0b31b33f17..2fc6e2405b 100644
--- a/src/fe_utils/query_utils.c
+++ b/src/fe_utils/query_utils.c
@@ -31,7 +31,7 @@ executeQuery(PGconn *conn, const char *query, bool echo)
 		PQresultStatus(res) != PGRES_TUPLES_OK)
 	{
 		pg_log_error("query failed: %s", PQerrorMessage(conn));
-		pg_log_info("query was: %s", query);
+		pg_log_error_detail("Query was: %s", query);
 		PQfinish(conn);
 		exit(1);
 	}
@@ -56,7 +56,7 @@ executeCommand(PGconn *conn, const char *query, bool echo)
 		PQresultStatus(res) != PGRES_COMMAND_OK)
 	{
 		pg_log_error("query failed: %s", PQerrorMessage(conn));
-		pg_log_info("query was: %s", query);
+		pg_log_error_detail("Query was: %s", query);
 		PQfinish(conn);
 		exit(1);
 	}
diff --git a/src/fe_utils/recovery_gen.c b/src/fe_utils/recovery_gen.c
index 9407e76bba..c9a423038a 100644
--- a/src/fe_utils/recovery_gen.c
+++ b/src/fe_utils/recovery_gen.c
@@ -31,10 +31,7 @@ GenerateRecoveryConfig(PGconn *pgconn, char *replication_slot)
 
 	contents = createPQExpBuffer();
 	if (!contents)
-	{
-		pg_log_error("out of memory");
-		exit(1);
-	}
+		pg_fatal("out of memory");
 
 	/*
 	 * In PostgreSQL 12 and newer versions, standby_mode is gone, replaced by
@@ -45,10 +42,7 @@ GenerateRecoveryConfig(PGconn *pgconn, char *replication_slot)
 
 	connOptions = PQconninfo(pgconn);
 	if (connOptions == NULL)
-	{
-		pg_log_error("out of memory");
-		exit(1);
-	}
+		pg_fatal("out of memory");
 
 	initPQExpBuffer(&conninfo_buf);
 	for (PQconninfoOption *opt = connOptions; opt && opt->keyword; opt++)
@@ -73,10 +67,7 @@ GenerateRecoveryConfig(PGconn *pgconn, char *replication_slot)
 		appendConnStrVal(&conninfo_buf, opt->val);
 	}
 	if (PQExpBufferDataBroken(conninfo_buf))
-	{
-		pg_log_error("out of memory");
-		exit(1);
-	}
+		pg_fatal("out of memory");
 
 	/*
 	 * Escape the connection string, so that it can be put in the config file.
@@ -96,10 +87,7 @@ GenerateRecoveryConfig(PGconn *pgconn, char *replication_slot)
 	}
 
 	if (PQExpBufferBroken(contents))
-	{
-		pg_log_error("out of memory");
-		exit(1);
-	}
+		pg_fatal("out of memory");
 
 	PQconninfoFree(connOptions);
 
@@ -130,16 +118,10 @@ WriteRecoveryConfig(PGconn *pgconn, char *target_dir, PQExpBuffer contents)
 
 	cf = fopen(filename, use_recovery_conf ? "w" : "a");
 	if (cf == NULL)
-	{
-		pg_log_error("could not open file \"%s\": %m", filename);
-		exit(1);
-	}
+		pg_fatal("could not open file \"%s\": %m", filename);
 
 	if (fwrite(contents->data, contents->len, 1, cf) != 1)
-	{
-		pg_log_error("could not write to file \"%s\": %m", filename);
-		exit(1);
-	}
+		pg_fatal("could not write to file \"%s\": %m", filename);
 
 	fclose(cf);
 
@@ -148,10 +130,7 @@ WriteRecoveryConfig(PGconn *pgconn, char *target_dir, PQExpBuffer contents)
 		snprintf(filename, MAXPGPATH, "%s/%s", target_dir, "standby.signal");
 		cf = fopen(filename, "w");
 		if (cf == NULL)
-		{
-			pg_log_error("could not create file \"%s\": %m", filename);
-			exit(1);
-		}
+			pg_fatal("could not create file \"%s\": %m", filename);
 
 		fclose(cf);
 	}
@@ -167,9 +146,6 @@ escape_quotes(const char *src)
 	char	   *result = escape_single_quotes_ascii(src);
 
 	if (!result)
-	{
-		pg_log_error("out of memory");
-		exit(1);
-	}
+		pg_fatal("out of memory");
 	return result;
 }
diff --git a/src/include/common/logging.h b/src/include/common/logging.h
index 43cc79afa8..e213bb70d0 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,84 @@ 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);
+
+/*
+ * 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_fatal(...) do { \
-		if (likely(__pg_log_level <= PG_LOG_FATAL)) pg_log_generic(PG_LOG_FATAL, __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(...) do { \
-		if (likely(__pg_log_level <= PG_LOG_ERROR)) pg_log_generic(PG_LOG_ERROR, __VA_ARGS__); \
+#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 shortcut: pg_log_error() and immediately exit(1).
+ */
+#define pg_fatal(...) do { \
+		if (likely(__pg_log_level <= PG_LOG_ERROR)) \
+			pg_log_generic(PG_LOG_ERROR, PG_LOG_PRIMARY, __VA_ARGS__); \
+		exit(1); \
 	} while(0)
 
 #endif							/* COMMON_LOGGING_H */
diff --git a/src/include/lib/simplehash.h b/src/include/lib/simplehash.h
index 8192927010..4a3d0ec2c5 100644
--- a/src/include/lib/simplehash.h
+++ b/src/include/lib/simplehash.h
@@ -293,8 +293,7 @@ SH_SCOPE void SH_STAT(SH_TYPE * tb);
 #define SIMPLEHASH_H
 
 #ifdef FRONTEND
-#define sh_error(...) \
-	do { pg_log_fatal(__VA_ARGS__); exit(1); } while(0)
+#define sh_error(...) pg_fatal(__VA_ARGS__)
 #define sh_log(...) pg_log_info(__VA_ARGS__)
 #else
 #define sh_error(...) elog(ERROR, __VA_ARGS__)
diff --git a/src/nls-global.mk b/src/nls-global.mk
index 53129f0a04..c1f7982300 100644
--- a/src/nls-global.mk
+++ b/src/nls-global.mk
@@ -72,10 +72,16 @@ BACKEND_COMMON_GETTEXT_FLAGS = \
 FRONTEND_COMMON_GETTEXT_FILES = $(top_srcdir)/src/common/logging.c
 
 FRONTEND_COMMON_GETTEXT_TRIGGERS = \
-    pg_log_fatal pg_log_error pg_log_warning pg_log_info pg_log_generic:2 pg_log_generic_v:2
+    pg_log_error pg_log_error_detail pg_log_error_hint \
+    pg_log_warning pg_log_warning_detail pg_log_warning_hint \
+    pg_log_info pg_log_info_detail pg_log_info_hint \
+    pg_fatal pg_log_generic:3 pg_log_generic_v:3
 
 FRONTEND_COMMON_GETTEXT_FLAGS = \
-    pg_log_fatal:1:c-format pg_log_error:1:c-format pg_log_warning:1:c-format pg_log_info:1:c-format pg_log_generic:2:c-format pg_log_generic_v:2:c-format
+    pg_log_error:1:c-format pg_log_error_detail:1:c-format pg_log_error_hint:1:c-format \
+    pg_log_warning:1:c-format pg_log_warning_detail:1:c-format pg_log_warning_hint:1:c-format \
+    pg_log_info:1:c-format pg_log_info_detail:1:c-format pg_log_info_hint:1:c-format \
+    pg_fatal:1:c-format pg_log_generic:3:c-format pg_log_generic_v:3:c-format

 all-po: $(MO_FILES)

---

From: Michael Paquier <[email-masked]>
Date: Sat, 26 Feb 2022 14:21:01 +0900

> on internal implementation details.

Nice code cut.

> I conclude that we ought to drop the separate FATAL level and just

FWIW, I have found the uses of pg_log_error() and pg_log_fatal()
rather confusing in some of the src/bin/ tools, so I am in favor of
removing completely one or the other, and getting rid of FATAL is the
best choice.

+#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)

Hm.  I am not sure to like much this abstraction by having so many
macros that understand the log level through their name.  Wouldn't it
be cleaner to have only one pg_log_hint() and one pg_log_detail() with
the log level passed as argument of the macro?
--
Michael