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 5e36943ef3..55257e35cb 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -331,10 +331,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;
 }
 
@@ -464,10 +461,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);
 
@@ -508,24 +502,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);
 }
 
 /*
@@ -611,9 +596,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
@@ -645,9 +628,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)");
 }
 
 /*
@@ -791,25 +773,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);
 	}
 }
@@ -830,16 +806,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);
 }
 
@@ -856,15 +826,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);
 }
 
@@ -1218,10 +1182,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
@@ -1237,10 +1198,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);
 
@@ -1323,10 +1281,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);
 
@@ -1338,10 +1293,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);
 
@@ -1375,9 +1327,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);
 	}
 
@@ -1503,21 +1454,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);
 
@@ -2057,10 +2004,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);
@@ -2078,17 +2022,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
 		{
 			/*
@@ -2099,9 +2040,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);
 	}
 }
 
@@ -2127,15 +2067,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;
@@ -2195,10 +2134,7 @@ setlocales(void)
 	if (locale_provider == COLLPROVIDER_ICU)
 	{
 		if (!icu_locale)
-		{
-			pg_log_error("ICU locale must be specified");
-			exit(1);
-		}
+			pg_fatal("ICU locale must be specified");
 
 		/*
 		 * In supported builds, the ICU locale ID will be checked by the
@@ -2206,7 +2142,8 @@ setlocales(void)
 		 */
 #ifndef USE_ICU
 		pg_log_error("ICU is not supported in this build");
-		fprintf(stderr, _("You need to rebuild PostgreSQL using %s.\n"), "--with-icu");
+		pg_log_error_hint("You need to rebuild PostgreSQL using %s.",
+						  "--with-icu");
 		exit(1);
 #endif
 	}
@@ -2288,9 +2225,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
@@ -2303,10 +2239,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");
 }
 
 
@@ -2326,10 +2259,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);
 		}
 	}
@@ -2344,10 +2276,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");
 }
 
 
@@ -2365,15 +2294,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);
 	}
 
@@ -2388,10 +2315,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);
 }
@@ -2442,9 +2366,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))
@@ -2463,10 +2386,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
 		}
@@ -2609,10 +2532,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();
 
@@ -2626,11 +2546,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();
 
@@ -2645,17 +2562,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);
 	}
 }
 
@@ -2676,10 +2591,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)))
@@ -2691,11 +2603,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();
 
@@ -2709,11 +2618,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();
 
@@ -2728,39 +2634,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);
@@ -2771,15 +2667,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.");
 }
 
 
@@ -2818,10 +2711,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);
 	}
@@ -3089,18 +2979,14 @@ main(int argc, char *argv[])
 				else if (strcmp(optarg, "libc") == 0)
 					locale_provider = COLLPROVIDER_LIBC;
 				else
-				{
-					pg_log_error("unrecognized locale provider: %s", optarg);
-					exit(1);
-				}
+					pg_fatal("unrecognized locale provider: %s", optarg);
 				break;
 			case 16:
 				icu_locale = pg_strdup(optarg);
 				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);
 		}
 	}
@@ -3120,17 +3006,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(1);
 	}
 
 	if (icu_locale && locale_provider != COLLPROVIDER_ICU)
-	{
-		pg_log_error("%s cannot be specified unless locale provider \"%s\" is chosen",
-					 "--icu-locale", "icu");
-		exit(1);
-	}
+		pg_fatal("%s cannot be specified unless locale provider \"%s\" is chosen",
+				 "--icu-locale", "icu");
 
 	atexit(cleanup_directories_atexit);
 
@@ -3141,10 +3023,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);
@@ -3154,10 +3033,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);
@@ -3179,15 +3055,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();
@@ -3201,10 +3071,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"
@@ -3247,8 +3114,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 1979e95639..316c5d849d 100644
--- a/src/bin/pg_basebackup/bbstreamer_gzip.c
+++ b/src/bin/pg_basebackup/bbstreamer_gzip.c
@@ -92,42 +92,29 @@ bbstreamer_gzip_writer_new(char *pathname, FILE *file,
 	{
 		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, compress->level,
 					Z_DEFAULT_STRATEGY) != Z_OK)
-	{
-		pg_log_error("could not set compression level %d: %s",
-					 compress->level, get_gz_error(streamer->gzfile));
-		exit(1);
-	}
+		pg_fatal("could not set compression level %d: %s",
+				 compress->level, 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
 }
 
@@ -153,9 +140,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));
 	}
 }
 
@@ -178,11 +164,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;
 }
@@ -259,15 +242,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 a6ec317e2b..6773b9f06e 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, bc_specification *compress)
 
 	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 ed8d084d62..65dcfff0a0 100644
--- a/src/bin/pg_basebackup/pg_basebackup.c
+++ b/src/bin/pg_basebackup/pg_basebackup.c
@@ -322,20 +322,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;
 		}
@@ -344,10 +338,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
@@ -356,18 +347,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
@@ -483,17 +468,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;
 
@@ -639,11 +618,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);
@@ -651,10 +627,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 */
@@ -709,10 +682,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);
 	}
 
 	/*
@@ -735,10 +705,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.
@@ -747,10 +714,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
 }
 
@@ -770,10 +734,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;
@@ -792,15 +753,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);
 	}
 }
 
@@ -929,23 +888,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");
 	}
 
 	/*
@@ -975,27 +927,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;
 }
@@ -1091,11 +1034,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. */
@@ -1111,17 +1051,11 @@ 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)
-		{
-			pg_log_error("background process terminated unexpectedly");
-			exit(1);
-		}
+			pg_fatal("background process terminated unexpectedly");
 
 		(*callback) (r, copybuf, callback_data);
 
@@ -1209,13 +1143,13 @@ CreateBackupStreamer(char *archive_name, char *spclocation,
 	if (must_parse_archive && !is_tar && !is_compressed_tar)
 	{
 		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);
 	}
 
@@ -1427,10 +1361,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);
@@ -1445,11 +1376,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
@@ -1509,9 +1437,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)
@@ -1521,10 +1448,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;
 			}
 
@@ -1577,11 +1501,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;
@@ -1670,11 +1591,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);
 }
 
 /*
@@ -1720,10 +1640,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",
@@ -1793,10 +1710,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);
 
@@ -1817,8 +1731,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);
 	}
 }
 
@@ -1878,9 +1791,8 @@ BaseBackup(char *compression_algorithm, char *compression_detail,
 	{
 		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;
@@ -1963,16 +1875,10 @@ BaseBackup(char *compression_algorithm, char *compression_detail,
 		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");
 
@@ -1999,10 +1905,7 @@ BaseBackup(char *compression_algorithm, char *compression_detail,
 	if (compressloc == COMPRESS_LOCATION_SERVER)
 	{
 		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");
 		AppendStringCommandOption(&buf, use_new_option_syntax,
 								  "COMPRESSION", compression_algorithm);
 		if (compression_detail != NULL)
@@ -2029,28 +1932,19 @@ BaseBackup(char *compression_algorithm, char *compression_detail,
 		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));
 
@@ -2078,16 +1972,10 @@ BaseBackup(char *compression_algorithm, char *compression_detail,
 	 */
 	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
@@ -2122,11 +2010,8 @@ BaseBackup(char *compression_algorithm, char *compression_detail,
 	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
@@ -2221,16 +2106,10 @@ BaseBackup(char *compression_algorithm, char *compression_detail,
 	 */
 	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);
@@ -2277,28 +2156,16 @@ BaseBackup(char *compression_algorithm, char *compression_detail,
 
 #ifndef WIN32
 		if (write(bgpipe[1], xlogend, strlen(xlogend)) != strlen(xlogend))
-		{
-			pg_log_info("could not send command to background pipe: %m");
-			exit(1);
-		}
+			pg_fatal("could not send command to background pipe: %m");
 
 		/* 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 */
 
@@ -2308,11 +2175,8 @@ BaseBackup(char *compression_algorithm, char *compression_detail,
 		 * 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);
 
@@ -2321,21 +2185,16 @@ BaseBackup(char *compression_algorithm, char *compression_detail,
 			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
 	}
@@ -2402,11 +2261,8 @@ BaseBackup(char *compression_algorithm, char *compression_detail,
 		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);
 		}
 	}
 
@@ -2500,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);
@@ -2547,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);
@@ -2580,11 +2430,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);
@@ -2633,12 +2480,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);
 		}
 	}
@@ -2650,8 +2493,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);
 	}
 
@@ -2673,8 +2515,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')
@@ -2686,15 +2527,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);
 	}
 
@@ -2723,20 +2562,14 @@ main(int argc, char **argv)
 		char	   *error_detail;
 
 		if (!parse_bc_algorithm(compression_algorithm, &alg))
-		{
-			pg_log_error("unrecognized compression algorithm \"%s\"",
-						 compression_algorithm);
-			exit(1);
-		}
+			pg_fatal("unrecognized compression algorithm \"%s\"",
+					 compression_algorithm);
 
 		parse_bc_specification(alg, compression_detail, &client_compress);
 		error_detail = validate_bc_specification(&client_compress);
 		if (error_detail != NULL)
-		{
-			pg_log_error("invalid compression specification: %s",
-						 error_detail);
-			exit(1);
-		}
+			pg_fatal("invalid compression specification: %s",
+					 error_detail);
 	}
 	else
 	{
@@ -2752,8 +2585,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);
 	}
 
@@ -2764,8 +2596,7 @@ main(int argc, char **argv)
 		client_compress.algorithm != BACKUP_COMPRESSION_NONE)
 	{
 		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);
 	}
 
@@ -2775,23 +2606,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);
 	}
 
@@ -2803,8 +2631,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;
@@ -2816,8 +2643,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);
 		}
 
@@ -2825,8 +2651,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);
 		}
 	}
@@ -2839,15 +2664,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);
 		}
 
@@ -2856,8 +2679,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);
 		}
 	}
@@ -2869,8 +2691,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);
 	}
 
@@ -2881,8 +2702,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);
 	}
 
@@ -2890,8 +2710,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);
 	}
 
@@ -2959,13 +2778,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 8d2c1e6ce0..23e04741fd 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", dest_folder);
-		exit(1);
-	}
+		pg_fatal("could not open directory \"%s\": %m", dest_folder);
 
 	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,20 +843,17 @@ 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;
 		case COMPRESSION_ZSTD:
-			pg_log_error("compression with %s is not yet supported", "ZSTD");
-			exit(1);
-
+			pg_fatal("compression with %s is not yet supported", "ZSTD");
+			break;
 	}
 
 
@@ -951,11 +897,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
@@ -1013,10 +956,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 1e0ff760eb..acd242d2c9 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 5f0f5ee62d..21dfe1b6ee 100644
--- a/src/bin/pg_checksums/pg_checksums.c
+++ b/src/bin/pg_checksums/pg_checksums.c
@@ -197,10 +197,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++;
 
@@ -214,12 +211,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++;
 
@@ -264,22 +260,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);
 			}
 		}
 
@@ -323,10 +315,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];
@@ -350,10 +339,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];
@@ -377,11 +363,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, '_');
@@ -429,11 +412,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
@@ -528,7 +508,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);
 		}
 	}
@@ -544,7 +525,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);
 		}
 	}
@@ -554,8 +535,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);
 	}
 
@@ -563,30 +543,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);
 	}
 
@@ -597,31 +570,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 ccfbe346be..d886f7e36f 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,11 +1099,11 @@ _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)
-		fatal("actual file length (%lld) does not match expected (%lld)",
-			  (long long) len, (long long) th->fileLen);
+		pg_fatal("actual file length (%lld) does not match expected (%lld)",
+				 (long long) len, (long long) th->fileLen);
 
 	pad = tarPaddingBytesRequired(len);
 	for (i = 0; i < pad; i++)
@@ -1148,7 +1148,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
 		{
 			/*
@@ -1166,9 +1166,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;
@@ -1179,7 +1179,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
@@ -1213,10 +1213,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);
@@ -1252,8 +1252,8 @@ _tarGetHeader(ArchiveHandle *AH, TAR_MEMBER *th)
 				 tag, (unsigned long long) hPos, (unsigned long long) len, sum);
 
 	if (chk != sum)
-		fatal("corrupt tar header found in %s (expected %d, computed %d) file position %llu",
-			  tag, sum, chk, (unsigned long long) ftello(ctx->tarFH));
+		pg_fatal("corrupt tar header found in %s (expected %d, computed %d) file position %llu",
+				 tag, sum, chk, (unsigned long long) ftello(ctx->tarFH));
 
 	th->targetFile = pg_strdup(tag);
 	th->fileLen = len;
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 535b160165..ecac4808d3 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
@@ -2877,8 +2871,8 @@ dumpDatabase(Archive *fout)
 	else if (datlocprovider[0] == 'i')
 		appendPQExpBufferStr(creaQry, "icu");
 	else
-		fatal("unrecognized locale provider: %s",
-			  datlocprovider);
+		pg_fatal("unrecognized locale provider: %s",
+				 datlocprovider);
 
 	if (strlen(collate) > 0 && strcmp(collate, ctype) == 0)
 	{
@@ -3257,7 +3251,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
@@ -3483,8 +3477,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);
 
@@ -3493,8 +3487,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);
@@ -3740,11 +3734,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();
@@ -4223,7 +4214,7 @@ getPublicationTables(Archive *fout, TableInfo tblinfo[], int numTables)
 
 			if (!parsePGArray(PQgetvalue(res, i, i_prattrs),
 							  &attnames, &nattnames))
-				fatal("could not parse %s array", "prattrs");
+				pg_fatal("could not parse %s array", "prattrs");
 			attribs = createPQExpBuffer();
 			for (int k = 0; k < nattnames; k++)
 			{
@@ -4556,7 +4547,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++)
@@ -4938,8 +4929,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");
@@ -5083,7 +5074,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;
 }
 
@@ -6537,8 +6528,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
@@ -6841,12 +6832,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;
@@ -7108,7 +7099,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;
@@ -7340,8 +7331,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));
@@ -7579,7 +7570,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;
@@ -7631,10 +7622,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
@@ -8246,12 +8237,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;
@@ -8280,8 +8271,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));
@@ -8367,12 +8358,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,
@@ -8521,7 +8512,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)
 			{
@@ -8529,7 +8520,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);
 			}
 
@@ -9219,7 +9210,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 */
 }
 
@@ -11687,7 +11678,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
 	{
@@ -11756,8 +11747,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')
@@ -11806,8 +11797,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++)
@@ -11937,8 +11928,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();
@@ -12043,15 +12034,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();
@@ -13109,8 +13100,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");
@@ -13516,8 +13507,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;
 			}
 		}
@@ -13572,8 +13563,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;
 			}
 		}
@@ -13597,8 +13588,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",
@@ -14290,8 +14281,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 */
 	}
 
@@ -14306,8 +14297,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,
@@ -14388,8 +14379,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");
 	}
 
@@ -14413,8 +14404,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)
 	{
@@ -14951,18 +14942,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] == ';');
@@ -15974,8 +15965,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 */
 }
 
@@ -16052,11 +16043,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++)
 			{
@@ -16274,8 +16265,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,
@@ -16502,8 +16493,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 */
@@ -16602,13 +16593,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);
@@ -16637,7 +16625,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 */
 	}
 
@@ -16760,8 +16748,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)
 		{
@@ -16824,13 +16812,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);
@@ -16919,10 +16904,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))
@@ -16994,11 +16976,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)
@@ -17264,11 +17245,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));
 
@@ -17506,11 +17484,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 1592090839..5de3241eb4 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 b39b5c1aac..6516ecaade 100644
--- a/src/bin/pg_rewind/pg_rewind.c
+++ b/src/bin/pg_rewind/pg_rewind.c
@@ -161,10 +161,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;
@@ -205,34 +201,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);
 	}
 
@@ -240,7 +241,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);
 	}
 
@@ -254,8 +255,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
@@ -264,11 +265,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);
 
@@ -1036,16 +1034,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);
 	}
 
 	/*
@@ -1146,7 +1143,8 @@ ensureCleanShutdown(const char *argv0)
 	if (system(postgres_cmd->data) != 0)
 	{
 		pg_log_error("postgres single-user mode in target cluster failed");
-		pg_fatal("Command was: %s", postgres_cmd->data);
+		pg_log_error_detail("Command was: %s", postgres_cmd->data);
+		exit(1);
 	}
 
 	destroyPQExpBuffer(postgres_cmd);
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 6d7fd88c0c..86d3dc46fa 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 74d0a8d1b2..48fecfa315 100644
--- a/src/bin/pg_verifybackup/t/005_bad_manifest.pl
+++ b/src/bin/pg_verifybackup/t/005_bad_manifest.pl
@@ -190,7 +190,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 9ffe9e55bd..1925fee166 100644
--- a/src/bin/pg_waldump/pg_waldump.c
+++ b/src/bin/pg_waldump/pg_waldump.c
@@ -84,7 +84,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
@@ -170,7 +169,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;
 }
 
@@ -226,17 +225,17 @@ 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 (r < 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;
 	}
@@ -296,9 +295,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 */
 }
@@ -339,7 +338,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);
 }
 
 /*
@@ -388,13 +387,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;
@@ -1129,13 +1128,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 */
@@ -1165,15 +1164,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,
@@ -1213,14 +1212,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
@@ -1310,15 +1309,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 acf3e56413..72e7677ae2 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -1502,8 +1502,7 @@ accumStats(StatsData *stats, bool skipped, double lat, double lag,
 			break;
 		default:
 			/* internal error which should never occur */
-			pg_log_fatal("unexpected error status: %d", estatus);
-			exit(1);
+			pg_fatal("unexpected error status: %d", estatus);
 	}
 }
 
@@ -1516,8 +1515,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);
@@ -1533,7 +1532,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);
 }
@@ -2878,8 +2877,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);
 	}
 }
 
@@ -4447,8 +4445,7 @@ getResultString(bool skipped, EStatus estatus)
 				return "deadlock";
 			default:
 				/* internal error which should never occur */
-				pg_log_fatal("unexpected error status: %d", estatus);
-				exit(1);
+				pg_fatal("unexpected error status: %d", estatus);
 		}
 	}
 	else
@@ -4876,10 +4873,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();
@@ -4893,10 +4887,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;
@@ -4938,15 +4929,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);
 
@@ -5086,17 +5071,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);
 		}
 	}
@@ -5117,10 +5099,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);
@@ -5163,7 +5142,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);
 		}
@@ -5207,21 +5186,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 */
@@ -5268,8 +5244,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 */
@@ -5291,8 +5267,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);
 			}
 		}
 
@@ -5384,7 +5359,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);
 
@@ -5734,9 +5709,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);
 }
 
 /*
@@ -5972,18 +5946,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);
@@ -6036,9 +6004,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);
@@ -6070,16 +6038,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
@@ -6096,16 +6058,10 @@ static void
 addScript(const 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);
 
@@ -6505,7 +6461,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;
@@ -6639,10 +6595,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)
 	{
@@ -6689,15 +6642,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 */
@@ -6711,10 +6661,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':
@@ -6787,10 +6734,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].variables, "option", optarg, p))
@@ -6809,10 +6753,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;
@@ -6828,10 +6769,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;
 				}
@@ -6841,10 +6779,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);
 				}
@@ -6865,10 +6800,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;
@@ -6891,10 +6823,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 */
 				{
@@ -6917,11 +6846,8 @@ 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;
 			case 13:			/* failures-detailed */
 				benchmarking_option_set = true;
@@ -6932,10 +6858,7 @@ main(int argc, char **argv)
 					int32		max_tries_arg = atoi(optarg);
 
 					if (max_tries_arg < 0)
-					{
-						pg_log_fatal("invalid number of maximum tries: \"%s\"", optarg);
-						exit(1);
-					}
+						pg_fatal("invalid number of maximum tries: \"%s\"", optarg);
 
 					benchmarking_option_set = true;
 					max_tries = (uint32) max_tries_arg;
@@ -6946,9 +6869,9 @@ main(int argc, char **argv)
 				verbose_errors = 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);
-				break;
 		}
 	}
 
@@ -6974,10 +6897,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)
@@ -7012,25 +6932,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)
@@ -7066,17 +6980,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)
@@ -7084,55 +6992,31 @@ 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");
 
 	if (!max_tries)
 	{
 		if (!latency_limit && duration <= 0)
-		{
-			pg_log_fatal("an unlimited number of transaction tries can only be used with --latency-limit or a duration (-T)");
-			exit(1);
-		}
+			pg_fatal("an unlimited number of transaction tries can only be used with --latency-limit or a duration (-T)");
 	}
 
 	/*
@@ -7182,10 +7066,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);
@@ -7293,10 +7174,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 */
@@ -7308,10 +7186,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);
@@ -7379,7 +7254,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;
 }
@@ -7413,10 +7288,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 */
@@ -7441,9 +7313,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);
 			}
 		}
 	}
@@ -7713,10 +7584,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 */
@@ -7860,8 +7728,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 079f4a1a76..aacbf8349f 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 6f612abf7c..cc4f92a35b 100644
--- a/src/bin/scripts/createdb.c
+++ b/src/bin/scripts/createdb.c
@@ -130,7 +130,8 @@ main(int argc, char *argv[])
 				icu_locale = 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);
 		}
 	}
@@ -149,22 +150,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;
 	}
@@ -172,10 +167,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)
