Re: Frontend error logging style - Mailing list pgsql-hackers

From Tom Lane
Subject Re: Frontend error logging style
Date
Msg-id 913687.1645572199@sss.pgh.pa.us
Whole thread Raw
In response to Re: Frontend error logging style  (Robert Haas <robertmhaas@gmail.com>)
Responses Re: Frontend error logging style  (Andres Freund <andres@anarazel.de>)
Re: Frontend error logging style  (Peter Eisentraut <peter.eisentraut@enterprisedb.com>)
List pgsql-hackers
Robert Haas <robertmhaas@gmail.com> writes:
> On Fri, Nov 19, 2021 at 5:17 AM Peter Eisentraut
> <peter.eisentraut@enterprisedb.com> wrote:
>> If people want to do that kind of thing (I'm undecided whether the
>> complexity is worth it), then make it a different API.  The pg_log_*
>> calls are for writing formatted output.  They normalized existing
>> hand-coded patterns at the time.  We can wrap another API on top of them
>> that does flow control and output.  The pg_log_* stuff is more on the
>> level of syslog(), which also just outputs stuff.  Nobody is suggesting
>> that syslog(LOG_EMERG) should exit the program automatically.  But you
>> can wrap higher-level APIs such as ereport() on top of that that might
>> do that.

> Yeah, that might be a way forward.

This conversation seems to have tailed off without full resolution,
but I observe that pretty much everyone except Peter is on board
with defining pg_log_fatal as pg_log_error + exit(1).  So I think
we should just do that, unless Peter wants to produce a finished
alternative proposal.

I've now gone through and fleshed out the patch I sketched upthread.
This exercise convinced me that we absolutely should do something
very like this, because

(1) As it stands, this patch removes a net of just about 1000 lines
of code.  So we clearly need *something*.

(2) The savings would be even more, except that people have invented
macros for pg_log_error + exit(1) in at least four places already.
(None of them quite the same of course.)  Here I've just fixed those
macro definitions to use pg_log_fatal.  In the name of consistency, we
should probably get rid of those macros in favor of using pg_log_fatal
directly; but I've not done so here, since this patch is eyewateringly
long and boring already.

(3) The amount of inconsistency in how we add on details/hints right
now is even worse than I thought.  For example, we've got places
burying the lede like this:

-        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);

or misidentifying the primary message altogether, like this:

     pg_log_error("query failed: %s",
                  PQerrorMessage(AH->connection));
-    fatal("query was: %s", query);
+    pg_log_error_detail("Query was: %s", query);
+    exit(1);

Thoughts?

            regards, tom lane

diff --git a/contrib/oid2name/oid2name.c b/contrib/oid2name/oid2name.c
index 65cce49993..66fe1c2de9 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",
+            pg_log_fatal("could not connect to database %s",
                          my_opts->dbname);
-            exit(1);
-        }

         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..28c859c295 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_log_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_log_fatal("invalid port number: %s", optarg);
                 param.pg_port = pg_strdup(optarg);
                 break;
             case 'U':
@@ -532,7 +523,8 @@ main(int argc, char **argv)
                 param.pg_prompt = TRI_YES;
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -541,7 +533,7 @@ main(int argc, char **argv)
     if (optind >= argc)
     {
         pg_log_error("missing required argument: database name");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 97f15971e2..22c3874236 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -329,10 +329,7 @@ escape_quotes(const char *src)
     char       *result = escape_single_quotes_ascii(src);

     if (!result)
-    {
-        pg_log_error("out of memory");
-        exit(1);
-    }
+        pg_log_fatal("out of memory");
     return result;
 }

@@ -462,10 +459,7 @@ readfile(const char *path)
     int            n;

     if ((infile = fopen(path, "r")) == NULL)
-    {
-        pg_log_error("could not open file \"%s\" for reading: %m", path);
-        exit(1);
-    }
+        pg_log_fatal("could not open file \"%s\" for reading: %m", path);

     initStringInfo(&line);

@@ -506,24 +500,15 @@ writefile(char *path, char **lines)
     char      **line;

     if ((out_file = fopen(path, "w")) == NULL)
-    {
-        pg_log_error("could not open file \"%s\" for writing: %m", path);
-        exit(1);
-    }
+        pg_log_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_log_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_log_fatal("could not write file \"%s\": %m", path);
 }

 /*
@@ -539,7 +524,7 @@ popen_check(const char *command, const char *mode)
     errno = 0;
     cmdfd = popen(command, mode);
     if (cmdfd == NULL)
-        pg_log_error("could not execute command \"%s\": %m", command);
+        pg_log_fatal("could not execute command \"%s\": %m", command);
     return cmdfd;
 }

@@ -609,9 +594,7 @@ get_id(void)
     if (geteuid() == 0)            /* 0 is root's uid */
     {
         pg_log_error("cannot be run as root");
-        fprintf(stderr,
-                _("Please log in (using, e.g., \"su\") as the (unprivileged) user that will\n"
-                  "own the server process.\n"));
+        pg_log_error_hint("Please log in (using, e.g., \"su\") as the (unprivileged) user that will own the server
process.");
         exit(1);
     }
 #endif
@@ -643,9 +626,8 @@ get_encoding_id(const char *encoding_name)
         if ((enc = pg_valid_server_encoding(encoding_name)) >= 0)
             return enc;
     }
-    pg_log_error("\"%s\" is not a valid server encoding name",
+    pg_log_fatal("\"%s\" is not a valid server encoding name",
                  encoding_name ? encoding_name : "(null)");
-    exit(1);
 }

 /*
@@ -789,25 +771,19 @@ check_input(char *path)
         if (errno == ENOENT)
         {
             pg_log_error("file \"%s\" does not exist", path);
-            fprintf(stderr,
-                    _("This might mean you have a corrupted installation or identified\n"
-                      "the wrong directory with the invocation option -L.\n"));
+            pg_log_error_hint("This might mean you have a corrupted installation or identified the wrong directory
withthe 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
withthe 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
theinvocation option -L."); 
         exit(1);
     }
 }
@@ -828,16 +804,10 @@ write_version_file(const char *extrapath)
         path = psprintf("%s/%s/PG_VERSION", pg_data, extrapath);

     if ((version_file = fopen(path, PG_BINARY_W)) == NULL)
-    {
-        pg_log_error("could not open file \"%s\" for writing: %m", path);
-        exit(1);
-    }
+        pg_log_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_log_fatal("could not write file \"%s\": %m", path);
     free(path);
 }

@@ -854,15 +824,9 @@ set_null_conf(void)
     path = psprintf("%s/postgresql.conf", pg_data);
     conf_file = fopen(path, PG_BINARY_W);
     if (conf_file == NULL)
-    {
-        pg_log_error("could not open file \"%s\" for writing: %m", path);
-        exit(1);
-    }
+        pg_log_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_log_fatal("could not write file \"%s\": %m", path);
     free(path);
 }

@@ -1216,10 +1180,7 @@ setup_config(void)

     writefile(path, conflines);
     if (chmod(path, pg_file_create_mode) != 0)
-    {
-        pg_log_error("could not change permissions of \"%s\": %m", path);
-        exit(1);
-    }
+        pg_log_fatal("could not change permissions of \"%s\": %m", path);

     /*
      * create the automatic configuration file to store the configuration
@@ -1235,10 +1196,7 @@ setup_config(void)

     writefile(path, autoconflines);
     if (chmod(path, pg_file_create_mode) != 0)
-    {
-        pg_log_error("could not change permissions of \"%s\": %m", path);
-        exit(1);
-    }
+        pg_log_fatal("could not change permissions of \"%s\": %m", path);

     free(conflines);

@@ -1321,10 +1279,7 @@ setup_config(void)

     writefile(path, conflines);
     if (chmod(path, pg_file_create_mode) != 0)
-    {
-        pg_log_error("could not change permissions of \"%s\": %m", path);
-        exit(1);
-    }
+        pg_log_fatal("could not change permissions of \"%s\": %m", path);

     free(conflines);

@@ -1336,10 +1291,7 @@ setup_config(void)

     writefile(path, conflines);
     if (chmod(path, pg_file_create_mode) != 0)
-    {
-        pg_log_error("could not change permissions of \"%s\": %m", path);
-        exit(1);
-    }
+        pg_log_fatal("could not change permissions of \"%s\": %m", path);

     free(conflines);

@@ -1373,9 +1325,8 @@ bootstrap_template1(void)
     {
         pg_log_error("input file \"%s\" does not belong to PostgreSQL %s",
                      bki_file, PG_VERSION);
-        fprintf(stderr,
-                _("Check your installation or specify the correct path "
-                  "using the option -L.\n"));
+        pg_log_error_hint("Check your installation or specify the correct path "
+                          "using the option -L.");
         exit(1);
     }

@@ -1495,21 +1446,17 @@ get_su_pwd(void)
         FILE       *pwf = fopen(pwfilename, "r");

         if (!pwf)
-        {
-            pg_log_error("could not open file \"%s\" for reading: %m",
+            pg_log_fatal("could not open file \"%s\" for reading: %m",
                          pwfilename);
-            exit(1);
-        }
         pwd1 = pg_get_line(pwf, NULL);
         if (!pwd1)
         {
             if (ferror(pwf))
-                pg_log_error("could not read password from file \"%s\": %m",
+                pg_log_fatal("could not read password from file \"%s\": %m",
                              pwfilename);
             else
-                pg_log_error("password file \"%s\" is empty",
+                pg_log_fatal("password file \"%s\" is empty",
                              pwfilename);
-            exit(1);
         }
         fclose(pwf);

@@ -2049,10 +1996,7 @@ check_locale_name(int category, const char *locale, char **canonname)

     save = setlocale(category, NULL);
     if (!save)
-    {
-        pg_log_error("setlocale() failed");
-        exit(1);
-    }
+        pg_log_fatal("setlocale() failed");

     /* save may be pointing at a modifiable scratch variable, so copy it. */
     save = pg_strdup(save);
@@ -2070,17 +2014,14 @@ check_locale_name(int category, const char *locale, char **canonname)

     /* restore old value. */
     if (!setlocale(category, save))
-    {
-        pg_log_error("failed to restore old locale \"%s\"", save);
-        exit(1);
-    }
+        pg_log_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_log_fatal("invalid locale name \"%s\"", locale);
         else
         {
             /*
@@ -2091,9 +2032,8 @@ check_locale_name(int category, const char *locale, char **canonname)
              * setlocale's behavior is implementation-specific, it's hard to
              * be sure what it didn't like.  Print a safe generic message.
              */
-            pg_log_error("invalid locale settings; check LANG and LC_* environment variables");
+            pg_log_fatal("invalid locale settings; check LANG and LC_* environment variables");
         }
-        exit(1);
     }
 }

@@ -2119,15 +2059,14 @@ check_locale_encoding(const char *locale, int user_enc)
           user_enc == PG_SQL_ASCII))
     {
         pg_log_error("encoding mismatch");
-        fprintf(stderr,
-                _("The encoding you selected (%s) and the encoding that the\n"
-                  "selected locale uses (%s) do not match.  This would lead to\n"
-                  "misbehavior in various character string processing functions.\n"
-                  "Rerun %s and either do not specify an encoding explicitly,\n"
-                  "or choose a matching combination.\n"),
-                pg_encoding_to_char(user_enc),
-                pg_encoding_to_char(locale_enc),
-                progname);
+        pg_log_error_detail("The encoding you selected (%s) and the encoding that the\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.",
+                            pg_encoding_to_char(user_enc),
+                            pg_encoding_to_char(locale_enc),
+                            progname);
         return false;
     }
     return true;
@@ -2259,9 +2198,8 @@ check_authmethod_valid(const char *authmethod, const char *const *valid_methods,
                 return;
     }

-    pg_log_error("invalid authentication method \"%s\" for \"%s\" connections",
+    pg_log_fatal("invalid authentication method \"%s\" for \"%s\" connections",
                  authmethod, conntype);
-    exit(1);
 }

 static void
@@ -2274,10 +2212,7 @@ check_need_password(const char *authmethodlocal, const char *authmethodhost)
          strcmp(authmethodhost, "password") == 0 ||
          strcmp(authmethodhost, "scram-sha-256") == 0) &&
         !(pwprompt || pwfilename))
-    {
-        pg_log_error("must specify a password for the superuser to enable password authentication");
-        exit(1);
-    }
+        pg_log_fatal("must specify a password for the superuser to enable password authentication");
 }


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


@@ -2336,15 +2267,13 @@ setup_bin_paths(const char *argv0)
             strlcpy(full_path, progname, sizeof(full_path));

         if (ret == -1)
-            pg_log_error("The program \"%s\" is needed by %s but was not found in the\n"
-                         "same directory as \"%s\".\n"
-                         "Check your installation.",
+            pg_log_error("the program \"%s\" is needed by %s but was not found in the same directory as \"%s\"",
                          "postgres", progname, full_path);
         else
-            pg_log_error("The program \"%s\" was found by \"%s\"\n"
-                         "but was not the same version as %s.\n"
-                         "Check your installation.",
+            pg_log_error("the program \"%s\" was found by \"%s\" "
+                         "but was not the same version as %s",
                          "postgres", full_path, progname);
+        pg_log_error_hint("Check your installation.");
         exit(1);
     }

@@ -2359,10 +2288,7 @@ setup_bin_paths(const char *argv0)
         get_share_path(backend_exec, share_path);
     }
     else if (!is_absolute_path(share_path))
-    {
-        pg_log_error("input file location must be an absolute path");
-        exit(1);
-    }
+        pg_log_fatal("input file location must be an absolute path");

     canonicalize_path(share_path);
 }
@@ -2406,9 +2332,8 @@ setup_locale_encoding(void)
             /* Couldn't recognize the locale's codeset */
             pg_log_error("could not find suitable encoding for locale \"%s\"",
                          lc_ctype);
-            fprintf(stderr, _("Rerun %s with the -E option.\n"), progname);
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                    progname);
+            pg_log_error_hint("Rerun %s with the -E option.", progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }
         else if (!pg_valid_server_encoding_id(ctype_enc))
@@ -2427,10 +2352,10 @@ setup_locale_encoding(void)
 #else
             pg_log_error("locale \"%s\" requires unsupported encoding \"%s\"",
                          lc_ctype, pg_encoding_to_char(ctype_enc));
-            fprintf(stderr,
-                    _("Encoding \"%s\" is not allowed as a server-side encoding.\n"
-                      "Rerun %s with a different locale selection.\n"),
-                    pg_encoding_to_char(ctype_enc), progname);
+            pg_log_error_detail("Encoding \"%s\" is not allowed as a server-side encoding.",
+                                pg_encoding_to_char(ctype_enc));
+            pg_log_error_hint("Rerun %s with a different locale selection.",
+                              progname);
             exit(1);
 #endif
         }
@@ -2573,10 +2498,7 @@ create_data_directory(void)
             fflush(stdout);

             if (pg_mkdir_p(pg_data, pg_dir_create_mode) != 0)
-            {
-                pg_log_error("could not create directory \"%s\": %m", pg_data);
-                exit(1);
-            }
+                pg_log_fatal("could not create directory \"%s\": %m", pg_data);
             else
                 check_ok();

@@ -2590,11 +2512,8 @@ create_data_directory(void)
             fflush(stdout);

             if (chmod(pg_data, pg_dir_create_mode) != 0)
-            {
-                pg_log_error("could not change permissions of directory \"%s\": %m",
+                pg_log_fatal("could not change permissions of directory \"%s\": %m",
                              pg_data);
-                exit(1);
-            }
             else
                 check_ok();

@@ -2609,17 +2528,15 @@ create_data_directory(void)
             if (ret != 4)
                 warn_on_mount_point(ret);
             else
-                fprintf(stderr,
-                        _("If you want to create a new database system, either remove or empty\n"
-                          "the directory \"%s\" or run %s\n"
-                          "with an argument other than \"%s\".\n"),
-                        pg_data, progname, pg_data);
+                pg_log_error_hint("If you want to create a new database system, either remove or empty "
+                                  "the directory \"%s\" or run %s "
+                                  "with an argument other than \"%s\".",
+                                  pg_data, progname, pg_data);
             exit(1);            /* no further message needed */

         default:
             /* Trouble accessing directory */
-            pg_log_error("could not access directory \"%s\": %m", pg_data);
-            exit(1);
+            pg_log_fatal("could not access directory \"%s\": %m", pg_data);
     }
 }

@@ -2640,10 +2557,7 @@ create_xlog_or_symlink(void)
         /* clean up xlog directory name, check it's absolute */
         canonicalize_path(xlog_dir);
         if (!is_absolute_path(xlog_dir))
-        {
-            pg_log_error("WAL directory location must be an absolute path");
-            exit(1);
-        }
+            pg_log_fatal("WAL directory location must be an absolute path");

         /* check if the specified xlog directory exists/is empty */
         switch ((ret = pg_check_dir(xlog_dir)))
@@ -2655,11 +2569,8 @@ create_xlog_or_symlink(void)
                 fflush(stdout);

                 if (pg_mkdir_p(xlog_dir, pg_dir_create_mode) != 0)
-                {
-                    pg_log_error("could not create directory \"%s\": %m",
+                    pg_log_fatal("could not create directory \"%s\": %m",
                                  xlog_dir);
-                    exit(1);
-                }
                 else
                     check_ok();

@@ -2673,11 +2584,8 @@ create_xlog_or_symlink(void)
                 fflush(stdout);

                 if (chmod(xlog_dir, pg_dir_create_mode) != 0)
-                {
-                    pg_log_error("could not change permissions of directory \"%s\": %m",
+                    pg_log_fatal("could not change permissions of directory \"%s\": %m",
                                  xlog_dir);
-                    exit(1);
-                }
                 else
                     check_ok();

@@ -2692,39 +2600,29 @@ create_xlog_or_symlink(void)
                 if (ret != 4)
                     warn_on_mount_point(ret);
                 else
-                    fprintf(stderr,
-                            _("If you want to store the WAL there, either remove or empty the directory\n"
-                              "\"%s\".\n"),
-                            xlog_dir);
+                    pg_log_error_hint("If you want to store the WAL there, either remove or empty the directory
\"%s\".",
+                                      xlog_dir);
                 exit(1);

             default:
                 /* Trouble accessing directory */
-                pg_log_error("could not access directory \"%s\": %m", xlog_dir);
-                exit(1);
+                pg_log_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",
+            pg_log_fatal("could not create symbolic link \"%s\": %m",
                          subdirloc);
-            exit(1);
-        }
 #else
-        pg_log_error("symlinks are not supported on this platform");
-        exit(1);
+        pg_log_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",
+            pg_log_fatal("could not create directory \"%s\": %m",
                          subdirloc);
-            exit(1);
-        }
     }

     free(subdirloc);
@@ -2735,15 +2633,12 @@ void
 warn_on_mount_point(int error)
 {
     if (error == 2)
-        fprintf(stderr,
-                _("It contains a dot-prefixed/invisible file, perhaps due to it being a mount point.\n"));
+        pg_log_error_detail("It contains a dot-prefixed/invisible file, perhaps due to it being a mount point.");
     else if (error == 3)
-        fprintf(stderr,
-                _("It contains a lost+found directory, perhaps due to it being a mount point.\n"));
+        pg_log_error_detail("It contains a lost+found directory, perhaps due to it being a mount point.");

-    fprintf(stderr,
-            _("Using a mount point directly as the data directory is not recommended.\n"
-              "Create a subdirectory under the mount point.\n"));
+    pg_log_error_hint("Using a mount point directly as the data directory is not recommended.\n"
+                      "Create a subdirectory under the mount point.");
 }


@@ -2782,10 +2677,7 @@ initialize_data_directory(void)
          * pg_mkdir_p() here, which avoids some failure modes; cf bug #13853.
          */
         if (mkdir(path, pg_dir_create_mode) < 0)
-        {
-            pg_log_error("could not create directory \"%s\": %m", path);
-            exit(1);
-        }
+            pg_log_fatal("could not create directory \"%s\": %m", path);

         free(path);
     }
@@ -3047,8 +2939,7 @@ main(int argc, char *argv[])
                 break;
             default:
                 /* getopt_long already emitted a complaint */
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -3068,8 +2959,7 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -3082,10 +2972,7 @@ main(int argc, char *argv[])

         /* must check that directory is readable */
         if (pg_check_dir(pg_data) <= 0)
-        {
-            pg_log_error("could not access directory \"%s\": %m", pg_data);
-            exit(1);
-        }
+            pg_log_fatal("could not access directory \"%s\": %m", pg_data);

         fputs(_("syncing data to disk ... "), stdout);
         fflush(stdout);
@@ -3095,10 +2982,7 @@ main(int argc, char *argv[])
     }

     if (pwprompt && pwfilename)
-    {
-        pg_log_error("password prompt and password file cannot be specified together");
-        exit(1);
-    }
+        pg_log_fatal("password prompt and password file cannot be specified together");

     check_authmethod_unspecified(&authmethodlocal);
     check_authmethod_unspecified(&authmethodhost);
@@ -3120,15 +3004,9 @@ main(int argc, char *argv[])

         /* verify that wal segment size is valid */
         if (endptr == str_wal_segment_size_mb || *endptr != '\0')
-        {
-            pg_log_error("argument of --wal-segsize must be a number");
-            exit(1);
-        }
+            pg_log_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_log_fatal("argument of --wal-segsize must be a power of 2 between 1 and 1024");
     }

     get_restricted_token();
@@ -3142,10 +3020,7 @@ main(int argc, char *argv[])
         username = effective_user;

     if (strncmp(username, "pg_", 3) == 0)
-    {
-        pg_log_error("superuser name \"%s\" is disallowed; role names cannot begin with \"pg_\"", username);
-        exit(1);
-    }
+        pg_log_fatal("superuser name \"%s\" is disallowed; role names cannot begin with \"pg_\"", username);

     printf(_("The files belonging to this database system will be owned "
              "by user \"%s\".\n"
@@ -3188,8 +3063,8 @@ main(int argc, char *argv[])
     {
         printf("\n");
         pg_log_warning("enabling \"trust\" authentication for local connections");
-        fprintf(stderr, _("You can change this by editing pg_hba.conf or using the option -A, or\n"
-                          "--auth-local and --auth-host, the next time you run initdb.\n"));
+        pg_log_warning_hint("You can change this by editing pg_hba.conf or using the option -A, or "
+                            "--auth-local and --auth-host, the next time you run initdb.");
     }

     if (!noinstructions)
diff --git a/src/bin/pg_amcheck/pg_amcheck.c b/src/bin/pg_amcheck/pg_amcheck.c
index 6607f72938..6672dd336d 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_log_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_log_fatal("invalid start block");
                 if (optval > MaxBlockNumber)
-                {
-                    pg_log_error("start block out of bounds");
-                    exit(1);
-                }
+                    pg_log_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_log_fatal("invalid end block");
                 if (optval > MaxBlockNumber)
-                {
-                    pg_log_error("end block out of bounds");
-                    exit(1);
-                }
+                    pg_log_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_log_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_log_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_log_fatal("cannot specify both a database name and database patterns");
         cparams.dbname = db;
     }

@@ -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_log_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",
+                pg_log_fatal("internal error: received unexpected database pattern_id %d",
                              pattern_id);
-                exit(1);
-            }
             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",
+                pg_log_fatal("internal error: received unexpected relation pattern_id %d",
                              pattern_id);
-                exit(1);
-            }

             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..b024b4a2ab 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",
+                    pg_log_fatal("could not remove file \"%s\": %m",
                                  WALFilePath);
-                    exit(1);
-                }
             }
         }

         if (errno)
-        {
-            pg_log_error("could not read archive location \"%s\": %m",
+            pg_log_fatal("could not read archive location \"%s\": %m",
                          archiveLocation);
-            exit(1);
-        }
         if (closedir(xldir))
-        {
-            pg_log_error("could not close archive location \"%s\": %m",
+            pg_log_fatal("could not close archive location \"%s\": %m",
                          archiveLocation);
-            exit(1);
-        }
     }
     else
-    {
-        pg_log_error("could not open archive location \"%s\": %m",
+        pg_log_fatal("could not open archive location \"%s\": %m",
                      archiveLocation);
-        exit(1);
-    }
 }

 /*
@@ -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..c0e5778f74 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_log_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",
+        pg_log_fatal("could not write to file \"%s\": %m",
                      mystreamer->pathname);
-        exit(1);
     }
 }

@@ -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",
+        pg_log_fatal("could not close file \"%s\": %m",
                      mystreamer->pathname);
-        exit(1);
-    }

     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",
+                pg_log_fatal("could not write to file \"%s\": %m",
                              mystreamer->filename);
-                exit(1);
             }
             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_log_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",
+            pg_log_fatal("could not create directory \"%s\": %m",
                          filename);
-            exit(1);
-        }
     }

 #ifndef WIN32
     if (chmod(filename, mode))
-    {
-        pg_log_error("could not set permissions on directory \"%s\": %m",
+        pg_log_fatal("could not set permissions on directory \"%s\": %m",
                      filename);
-        exit(1);
-    }
 #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",
+        pg_log_fatal("could not create symbolic link from \"%s\" to \"%s\": %m",
                      filename, linktarget);
-        exit(1);
-    }
 }

 /*
@@ -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_log_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",
+        pg_log_fatal("could not set permissions on file \"%s\": %m",
                      filename);
-        exit(1);
-    }
 #endif

     return file;
diff --git a/src/bin/pg_basebackup/bbstreamer_gzip.c b/src/bin/pg_basebackup/bbstreamer_gzip.c
index 894f857103..c51754a362 100644
--- a/src/bin/pg_basebackup/bbstreamer_gzip.c
+++ b/src/bin/pg_basebackup/bbstreamer_gzip.c
@@ -91,42 +91,29 @@ bbstreamer_gzip_writer_new(char *pathname, FILE *file, int compresslevel)
     {
         streamer->gzfile = gzopen(pathname, "wb");
         if (streamer->gzfile == NULL)
-        {
-            pg_log_error("could not create compressed file \"%s\": %m",
+            pg_log_fatal("could not create compressed file \"%s\": %m",
                          pathname);
-            exit(1);
-        }
     }
     else
     {
         int            fd = dup(fileno(file));

         if (fd < 0)
-        {
-            pg_log_error("could not duplicate stdout: %m");
-            exit(1);
-        }
+            pg_log_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_log_fatal("could not open output file: %m");
     }

     if (gzsetparams(streamer->gzfile, compresslevel,
                     Z_DEFAULT_STRATEGY) != Z_OK)
-    {
-        pg_log_error("could not set compression level %d: %s",
+        pg_log_fatal("could not set compression level %d: %s",
                      compresslevel, get_gz_error(streamer->gzfile));
-        exit(1);
-    }

     return &streamer->base;
 #else
-    pg_log_error("this build does not support compression");
-    exit(1);
+    pg_log_fatal("this build does not support compression");
 #endif
 }

@@ -152,9 +139,8 @@ bbstreamer_gzip_writer_content(bbstreamer *streamer,
         /* if write didn't set errno, assume problem is no disk space */
         if (errno == 0)
             errno = ENOSPC;
-        pg_log_error("could not write to compressed file \"%s\": %s",
+        pg_log_fatal("could not write to compressed file \"%s\": %s",
                      mystreamer->pathname, get_gz_error(mystreamer->gzfile));
-        exit(1);
     }
 }

@@ -177,11 +163,8 @@ bbstreamer_gzip_writer_finalize(bbstreamer *streamer)

     errno = 0;                    /* in case gzclose() doesn't set it */
     if (gzclose(mystreamer->gzfile) != 0)
-    {
-        pg_log_error("could not close compressed file \"%s\": %m",
+        pg_log_fatal("could not close compressed file \"%s\": %m",
                      mystreamer->pathname);
-        exit(1);
-    }

     mystreamer->gzfile = NULL;
 }
@@ -258,15 +241,11 @@ bbstreamer_gzip_decompressor_new(bbstreamer *next)
      * possible value for safety.
      */
     if (inflateInit2(zs, 15 + 16) != Z_OK)
-    {
-        pg_log_error("could not initialize compression library");
-        exit(1);
-    }
+        pg_log_fatal("could not initialize compression library");

     return &streamer->base;
 #else
-    pg_log_error("this build does not support compression");
-    exit(1);
+    pg_log_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..612912f641 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_log_fatal("unexpected state while injecting recovery settings");
     }

     bbstreamer_content(mystreamer->base.bbs_next, &mystreamer->member,
diff --git a/src/bin/pg_basebackup/bbstreamer_lz4.c b/src/bin/pg_basebackup/bbstreamer_lz4.c
index f0bc226bf8..35380dbb02 100644
--- a/src/bin/pg_basebackup/bbstreamer_lz4.c
+++ b/src/bin/pg_basebackup/bbstreamer_lz4.c
@@ -109,8 +109,7 @@ bbstreamer_lz4_compressor_new(bbstreamer *next, int compresslevel)

     return &streamer->base;
 #else
-    pg_log_error("this build does not support compression");
-    exit(1);
+    pg_log_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_log_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_log_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..294ff97bdd 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_log_fatal("tar file trailer exceeds 2 blocks");
                 return;

             default:
                 /* Shouldn't happen. */
-                pg_log_error("unexpected state while parsing tar archive");
-                exit(1);
+                pg_log_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_log_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_log_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 08b07d5a06..eadb20c485 100644
--- a/src/bin/pg_basebackup/pg_basebackup.c
+++ b/src/bin/pg_basebackup/pg_basebackup.c
@@ -306,20 +306,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_log_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_log_fatal("multiple \"=\" signs in tablespace mapping");
             else
                 dst = dst_ptr = cell->new_dir;
         }
@@ -328,10 +322,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_log_fatal("invalid tablespace mapping format \"%s\", must be \"OLDDIR=NEWDIR\"", arg);

     /*
      * This check isn't absolutely necessary.  But all tablespaces are created
@@ -340,18 +331,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",
+        pg_log_fatal("old directory is not an absolute path in tablespace mapping: %s",
                      cell->old_dir);
-        exit(1);
-    }

     if (!is_absolute_path(cell->new_dir))
-    {
-        pg_log_error("new directory is not an absolute path in tablespace mapping: %s",
+        pg_log_fatal("new directory is not an absolute path in tablespace mapping: %s",
                      cell->new_dir);
-        exit(1);
-    }

     /*
      * Comparisons done with these values should involve similarly
@@ -466,17 +451,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_log_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\"",
+                pg_log_fatal("could not parse write-ahead log location \"%s\"",
                              xlogend);
-                exit(1);
-            }
             xlogendptr = ((uint64) hi) << 32 | lo;
             has_xlogendptr = 1;

@@ -616,11 +595,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\"",
+        pg_log_fatal("could not parse write-ahead log location \"%s\"",
                      startpos);
-        exit(1);
-    }
     param->startptr = ((uint64) hi) << 32 | lo;
     /* Round off to even segment position */
     param->startptr -= XLogSegmentOffset(param->startptr, WalSegSz);
@@ -628,10 +604,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_log_fatal("could not create pipe for background process: %m");
 #endif

     /* Get a second connection */
@@ -686,10 +659,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_log_fatal("could not create directory \"%s\": %m", statusdir);
     }

     /*
@@ -704,10 +674,7 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier)
         exit(LogStreamerMain(param));
     }
     else if (bgchild < 0)
-    {
-        pg_log_error("could not create background process: %m");
-        exit(1);
-    }
+        pg_log_fatal("could not create background process: %m");

     /*
      * Else we are in the parent process and all is well.
@@ -716,10 +683,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_log_fatal("could not create background thread: %m");
 #endif
 }

@@ -739,10 +703,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_log_fatal("could not create directory \"%s\": %m", dirname);
             if (created)
                 *created = true;
             return;
@@ -761,15 +722,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_log_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_log_fatal("could not access directory \"%s\": %m", dirname);
     }
 }

@@ -898,23 +857,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_log_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_log_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_log_fatal("transfer rate must be greater than zero");
     }

     /*
@@ -944,27 +896,18 @@ parse_max_rate(char *src)
         after_num++;

     if (*after_num != '\0')
-    {
-        pg_log_error("invalid --max-rate unit: \"%s\"", suffix);
-        exit(1);
-    }
+        pg_log_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_log_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_log_fatal("transfer rate \"%s\" is out of range", src);

     return (int32) result;
 }
@@ -1071,10 +1014,7 @@ parse_compress_options(char *src, WalCompressionMethod *methodres,
     /* Check the contents after the colon separator. */
     sep++;
     if (*sep == '\0')
-    {
-        pg_log_error("no compression level defined for method %s", firstpart);
-        exit(1);
-    }
+        pg_log_fatal("no compression level defined for method %s", firstpart);

     /*
      * For any of the methods currently supported, the data after the
@@ -1100,11 +1040,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",
+        pg_log_fatal("could not get COPY data stream: %s",
                      PQerrorMessage(conn));
-        exit(1);
-    }
     PQclear(res);

     /* Loop over chunks until done. */
@@ -1120,11 +1057,8 @@ ReceiveCopyData(PGconn *conn, WriteDataCallback callback,
             break;
         }
         else if (r == -2)
-        {
-            pg_log_error("could not read COPY data: %s",
+            pg_log_fatal("could not read COPY data: %s",
                          PQerrorMessage(conn));
-            exit(1);
-        }

         (*callback) (r, copybuf, callback_data);

@@ -1187,13 +1121,13 @@ CreateBackupStreamer(char *archive_name, char *spclocation,
     if (must_parse_archive && !is_tar && !is_tar_gz && !is_tar_lz4)
     {
         pg_log_error("unable to parse archive: %s", archive_name);
-        pg_log_info("only tar archives can be parsed");
+        pg_log_error_detail("Only tar archives can be parsed.");
         if (format == 'p')
-            pg_log_info("plain format requires pg_basebackup to parse the archive");
+            pg_log_error_detail("Plain format requires pg_basebackup to parse the archive.");
         if (inject_manifest)
-            pg_log_info("using - as the output directory requires pg_basebackup to parse the archive");
+            pg_log_error_detail("Using - as the output directory requires pg_basebackup to parse the archive.");
         if (writerecoveryconf)
-            pg_log_info("the -R option requires pg_basebackup to parse the archive");
+            pg_log_error_detail("The -R option requires pg_basebackup to parse the archive.");
         exit(1);
     }

@@ -1398,10 +1332,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_log_fatal("archives should precede manifest");

                 /* Parse the rest of the CopyData message. */
                 archive_name = GetCopyDataString(r, copybuf, &cursor);
@@ -1416,11 +1347,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\"",
+                    pg_log_fatal("invalid archive name: \"%s\"",
                                  archive_name);
-                    exit(1);
-                }

                 /*
                  * An empty spclocation is treated as NULL. We expect this
@@ -1479,9 +1407,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",
+                        pg_log_fatal("could not write to file \"%s\": %m",
                                      state->manifest_filename);
-                        exit(1);
                     }
                 }
                 else if (state->streamer != NULL)
@@ -1491,10 +1418,7 @@ ReceiveArchiveStreamChunk(size_t r, char *copybuf, void *callback_data)
                                        r - 1, BBSTREAMER_UNKNOWN);
                 }
                 else
-                {
-                    pg_log_error("unexpected payload data");
-                    exit(1);
-                }
+                    pg_log_fatal("unexpected payload data");
                 break;
             }

@@ -1547,11 +1471,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",
+                            pg_log_fatal("could not create file \"%s\": %m",
                                          state->manifest_filename);
-                            exit(1);
-                        }
                     }
                 }
                 break;
@@ -1640,11 +1561,10 @@ static void
 ReportCopyDataParseError(size_t r, char *copybuf)
 {
     if (r == 0)
-        pg_log_error("empty COPY message");
+        pg_log_fatal("empty COPY message");
     else
-        pg_log_error("malformed COPY message of type %d, length %zu",
+        pg_log_fatal("malformed COPY message of type %d, length %zu",
                      copybuf[0], r);
-    exit(1);
 }

 /*
@@ -1689,10 +1609,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_log_fatal("out of memory");

         /* Inject it into the output tarfile. */
         bbstreamer_inject_file(manifest_inject_streamer, "backup_manifest",
@@ -1762,10 +1679,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_log_fatal("could not create file \"%s\": %m", state.filename);

     ReceiveCopyData(conn, ReceiveBackupManifestChunk, &state);

@@ -1786,8 +1700,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_log_fatal("could not write to file \"%s\": %m", state->filename);
     }
 }

@@ -1846,9 +1759,8 @@ BaseBackup(void)
     {
         const char *serverver = PQparameterStatus(conn, "server_version");

-        pg_log_error("incompatible server version %s",
+        pg_log_fatal("incompatible server version %s",
                      serverver ? serverver : "'unknown'");
-        exit(1);
     }
     if (serverMajor >= 1500)
         use_new_option_syntax = true;
@@ -1931,16 +1843,10 @@ BaseBackup(void)
         char       *colon;

         if (serverMajor < 1500)
-        {
-            pg_log_error("backup targets are not supported by this server version");
-            exit(1);
-        }
+            pg_log_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_log_fatal("recovery configuration cannot be written when a backup target is used");

         AppendPlainCommandOption(&buf, use_new_option_syntax, "TABLESPACE_MAP");

@@ -1969,10 +1875,7 @@ BaseBackup(void)
         char *compressmethodstr = NULL;

         if (!use_new_option_syntax)
-        {
-            pg_log_error("server does not support server-side compression");
-            exit(1);
-        }
+            pg_log_fatal("server does not support server-side compression");
         switch (compressmethod)
         {
             case COMPRESSION_GZIP:
@@ -2010,28 +1913,19 @@ BaseBackup(void)
         basebkp = psprintf("BASE_BACKUP %s", buf.data);

     if (PQsendQuery(conn, basebkp) == 0)
-    {
-        pg_log_error("could not send replication command \"%s\": %s",
+        pg_log_fatal("could not send replication command \"%s\": %s",
                      "BASE_BACKUP", PQerrorMessage(conn));
-        exit(1);
-    }

     /*
      * Get the starting WAL location
      */
     res = PQgetResult(conn);
     if (PQresultStatus(res) != PGRES_TUPLES_OK)
-    {
-        pg_log_error("could not initiate base backup: %s",
+        pg_log_fatal("could not initiate base backup: %s",
                      PQerrorMessage(conn));
-        exit(1);
-    }
     if (PQntuples(res) != 1)
-    {
-        pg_log_error("server returned unexpected response to BASE_BACKUP command; got %d rows and %d fields, expected
%drows and %d fields", 
+        pg_log_fatal("server returned unexpected response to BASE_BACKUP command; got %d rows and %d fields, expected
%drows and %d fields", 
                      PQntuples(res), PQnfields(res), 1, 2);
-        exit(1);
-    }

     strlcpy(xlogstart, PQgetvalue(res, 0, 0), sizeof(xlogstart));

@@ -2059,16 +1953,10 @@ BaseBackup(void)
      */
     res = PQgetResult(conn);
     if (PQresultStatus(res) != PGRES_TUPLES_OK)
-    {
-        pg_log_error("could not get backup header: %s",
+        pg_log_fatal("could not get backup header: %s",
                      PQerrorMessage(conn));
-        exit(1);
-    }
     if (PQntuples(res) < 1)
-    {
-        pg_log_error("no data returned from server");
-        exit(1);
-    }
+        pg_log_fatal("no data returned from server");

     /*
      * Sum up the total size, for progress reporting
@@ -2103,11 +1991,8 @@ BaseBackup(void)
     writing_to_stdout = format == 't' && basedir != NULL &&
         strcmp(basedir, "-") == 0;
     if (writing_to_stdout && PQntuples(res) > 1)
-    {
-        pg_log_error("can only write single tablespace to stdout, database has %d",
+        pg_log_fatal("can only write single tablespace to stdout, database has %d",
                      PQntuples(res));
-        exit(1);
-    }

     /*
      * If we're streaming WAL, start the streaming session before we start
@@ -2183,16 +2068,10 @@ BaseBackup(void)
      */
     res = PQgetResult(conn);
     if (PQresultStatus(res) != PGRES_TUPLES_OK)
-    {
-        pg_log_error("backup failed: %s",
+        pg_log_fatal("backup failed: %s",
                      PQerrorMessage(conn));
-        exit(1);
-    }
     if (PQntuples(res) != 1)
-    {
-        pg_log_error("no write-ahead log end position returned from server");
-        exit(1);
-    }
+        pg_log_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);
@@ -2247,20 +2126,11 @@ BaseBackup(void)
         /* Just wait for the background process to exit */
         r = waitpid(bgchild, &status, 0);
         if (r == (pid_t) -1)
-        {
-            pg_log_error("could not wait for child process: %m");
-            exit(1);
-        }
+            pg_log_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_log_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_log_fatal("%s", wait_result_to_str(status));
         /* Exited normally, we're happy! */
 #else                            /* WIN32 */

@@ -2270,11 +2140,8 @@ BaseBackup(void)
          * it's there.
          */
         if (sscanf(xlogend, "%X/%X", &hi, &lo) != 2)
-        {
-            pg_log_error("could not parse write-ahead log location \"%s\"",
+            pg_log_fatal("could not parse write-ahead log location \"%s\"",
                          xlogend);
-            exit(1);
-        }
         xlogendptr = ((uint64) hi) << 32 | lo;
         InterlockedIncrement(&has_xlogendptr);

@@ -2283,21 +2150,16 @@ BaseBackup(void)
             WAIT_OBJECT_0)
         {
             _dosmaperr(GetLastError());
-            pg_log_error("could not wait for child thread: %m");
-            exit(1);
+            pg_log_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_log_fatal("could not get child thread exit status: %m");
         }
         if (status != 0)
-        {
-            pg_log_error("child thread exited with error %u",
+            pg_log_fatal("child thread exited with error %u",
                          (unsigned int) status);
-            exit(1);
-        }
         /* Exited normally, we're happy */
 #endif
     }
@@ -2364,11 +2226,8 @@ BaseBackup(void)
         else
         {
             if (rename(tmp_filename, filename) != 0)
-            {
-                pg_log_error("could not rename file \"%s\" to \"%s\": %m",
+                pg_log_fatal("could not rename file \"%s\" to \"%s\": %m",
                              tmp_filename, filename);
-                exit(1);
-            }
         }
     }

@@ -2458,11 +2317,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\"",
+                    pg_log_fatal("invalid output format \"%s\", must be \"plain\" or \"tar\"",
                                  optarg);
-                    exit(1);
-                }
                 break;
             case 'r':
                 maxrate = parse_max_rate(optarg);
@@ -2505,11 +2361,8 @@ main(int argc, char **argv)
                     includewal = STREAM_WAL;
                 }
                 else
-                {
-                    pg_log_error("invalid wal-method option \"%s\", must be \"fetch\", \"stream\", or \"none\"",
+                    pg_log_fatal("invalid wal-method option \"%s\", must be \"fetch\", \"stream\", or \"none\"",
                                  optarg);
-                    exit(1);
-                }
                 break;
             case 1:
                 xlog_dir = pg_strdup(optarg);
@@ -2542,11 +2395,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\"",
+                    pg_log_fatal("invalid checkpoint argument \"%s\", must be \"fast\" or \"spread\"",
                                  optarg);
-                    exit(1);
-                }
                 break;
             case 'd':
                 connection_string = pg_strdup(optarg);
@@ -2595,12 +2445,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);
         }
     }
@@ -2612,8 +2458,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);
     }

@@ -2635,8 +2480,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')
@@ -2648,15 +2492,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);
     }

@@ -2681,8 +2523,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);
     }

@@ -2692,8 +2533,7 @@ main(int argc, char **argv)
     if (format == 'p' && compressloc == COMPRESS_LOCATION_CLIENT)
     {
         pg_log_error("only tar mode backups can be compressed");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -2703,23 +2543,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);
     }

@@ -2731,8 +2568,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;
@@ -2744,8 +2580,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);
         }

@@ -2753,8 +2588,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);
         }
     }
@@ -2767,15 +2601,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);
         }

@@ -2784,8 +2616,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);
         }
     }
@@ -2798,37 +2629,29 @@ main(int argc, char **argv)
             {
                 pg_log_error("cannot use compression level with method %s",
                              "none");
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
             }
             break;
         case COMPRESSION_GZIP:
             if (compresslevel > 9)
-            {
-                pg_log_error("compression level %d of method %s higher than maximum of 9",
+                pg_log_fatal("compression level %d of method %s higher than maximum of 9",
                              compresslevel, "gzip");
-                exit(1);
-            }
             if (compressloc == COMPRESS_LOCATION_CLIENT)
             {
 #ifdef HAVE_LIBZ
                 if (compresslevel == 0)
                     compresslevel = Z_DEFAULT_COMPRESSION;
 #else
-                pg_log_error("this build does not support compression with %s",
+                pg_log_fatal("this build does not support compression with %s",
                              "gzip");
-                exit(1);
 #endif
             }
             break;
         case COMPRESSION_LZ4:
             if (compresslevel > 12)
-            {
-                pg_log_error("compression level %d of method %s higher than maximum of 12",
+                pg_log_fatal("compression level %d of method %s higher than maximum of 12",
                              compresslevel, "lz4");
-                exit(1);
-            }
             break;
     }

@@ -2839,8 +2662,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);
     }

@@ -2851,8 +2673,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);
     }

@@ -2860,8 +2681,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);
     }

@@ -2917,13 +2737,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_log_fatal("could not create symbolic link \"%s\": %m", linkloc);
 #else
-        pg_log_error("symlinks are not supported on this platform");
-        exit(1);
+        pg_log_fatal("symlinks are not supported on this platform");
 #endif
         free(linkloc);
     }
diff --git a/src/bin/pg_basebackup/pg_receivewal.c b/src/bin/pg_basebackup/pg_receivewal.c
index ccb215c398..7e2a9b7b19 100644
--- a/src/bin/pg_basebackup/pg_receivewal.c
+++ b/src/bin/pg_basebackup/pg_receivewal.c
@@ -239,10 +239,7 @@ get_destination_dir(char *dest_folder)
     Assert(dest_folder != NULL);
     dir = opendir(dest_folder);
     if (dir == NULL)
-    {
-        pg_log_error("could not open directory \"%s\": %m", basedir);
-        exit(1);
-    }
+        pg_log_fatal("could not open directory \"%s\": %m", basedir);

     return dir;
 }
@@ -256,10 +253,7 @@ close_destination_dir(DIR *dest_dir, char *dest_folder)
 {
     Assert(dest_dir != NULL && dest_folder != NULL);
     if (closedir(dest_dir))
-    {
-        pg_log_error("could not close directory \"%s\": %m", dest_folder);
-        exit(1);
-    }
+        pg_log_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_log_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",
+                pg_log_fatal("could not open compressed file \"%s\": %m",
                              fullpath);
-                exit(1);
-            }
             if (lseek(fd, (off_t) (-4), SEEK_END) < 0)
-            {
-                pg_log_error("could not seek in compressed file \"%s\": %m",
+                pg_log_fatal("could not seek in compressed file \"%s\": %m",
                              fullpath);
-                exit(1);
-            }
             r = read(fd, (char *) buf, sizeof(buf));
             if (r != sizeof(buf))
             {
                 if (r < 0)
-                    pg_log_error("could not read compressed file \"%s\": %m",
+                    pg_log_fatal("could not read compressed file \"%s\": %m",
                                  fullpath);
                 else
-                    pg_log_error("could not read compressed file \"%s\": read %d of %zu",
+                    pg_log_fatal("could not read compressed file \"%s\": read %d of %zu",
                                  fullpath, r, sizeof(buf));
-                exit(1);
             }

             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_log_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",
+                pg_log_fatal("could not create LZ4 decompression context: %s",
                              LZ4F_getErrorName(status));
-                exit(1);
-            }

             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_log_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",
+                        pg_log_fatal("could not decompress file \"%s\": %s",
                                      fullpath,
                                      LZ4F_getErrorName(status));
-                        exit(1);
-                    }

                     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",
+                pg_log_fatal("could not free LZ4 decompression context: %s",
                              LZ4F_getErrorName(status));
-                exit(1);
-            }

             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_log_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_log_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",
+                    pg_log_fatal("invalid value \"%s\" for option %s",
                                  optarg, "--compression-method");
-                    exit(1);
-                }
                 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",
+            pg_log_fatal("this build does not support compression with %s",
                          "gzip");
-            exit(1);
 #endif
             break;
         case COMPRESSION_LZ4:
@@ -894,14 +843,12 @@ main(int argc, char **argv)
             {
                 pg_log_error("cannot use --compress with --compression-method=%s",
                              "lz4");
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                        progname);
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
             }
 #else
-            pg_log_error("this build does not support compression with %s",
+            pg_log_fatal("this build does not support compression with %s",
                          "LZ4");
-            exit(1);
 #endif
             break;
     }
@@ -947,11 +894,8 @@ main(int argc, char **argv)
      * be defined in this context.
      */
     if (db_name)
-    {
-        pg_log_error("replication connection using slot \"%s\" is unexpectedly database specific",
+        pg_log_fatal("replication connection using slot \"%s\" is unexpectedly database specific",
                      replication_slot);
-        exit(1);
-    }

     /*
      * Set umask so that directories/files are created with the same
@@ -1009,10 +953,7 @@ main(int argc, char **argv)
             exit(0);
         }
         else if (noloop)
-        {
-            pg_log_error("disconnected");
-            exit(1);
-        }
+            pg_log_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..3ce950263b 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);
-    }

     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_log_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_log_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_log_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_log_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..84f1a5a37a 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);
-            }
             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);
-            }
             lastFlushPosition = blockpos;
         }

diff --git a/src/bin/pg_basebackup/streamutil.c b/src/bin/pg_basebackup/streamutil.c
index 4a6afd1a06..a3b067e4fe 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_log_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_log_fatal("could not connect to server");

         /* If we need a password and -w wasn't given, loop back and get one */
         if (PQstatus(tmpconn) == CONNECTION_BAD &&
diff --git a/src/bin/pg_basebackup/walmethods.c b/src/bin/pg_basebackup/walmethods.c
index a6d08c1270..9b2733a8d4 100644
--- a/src/bin/pg_basebackup/walmethods.c
+++ b/src/bin/pg_basebackup/walmethods.c
@@ -1197,7 +1197,6 @@ tar_close(Walfile f, WalCloseMethod method)
         /* 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);
     }

     /* Clean up and done */
diff --git a/src/bin/pg_checksums/pg_checksums.c b/src/bin/pg_checksums/pg_checksums.c
index 7e69475947..b25b984391 100644
--- a/src/bin/pg_checksums/pg_checksums.c
+++ b/src/bin/pg_checksums/pg_checksums.c
@@ -208,10 +208,7 @@ scan_file(const char *fn, int segmentno)
     f = open(fn, PG_BINARY | flags, 0);

     if (f < 0)
-    {
-        pg_log_error("could not open file \"%s\": %m", fn);
-        exit(1);
-    }
+        pg_log_fatal("could not open file \"%s\": %m", fn);

     files_scanned++;

@@ -225,12 +222,11 @@ scan_file(const char *fn, int segmentno)
         if (r != BLCKSZ)
         {
             if (r < 0)
-                pg_log_error("could not read block %u in file \"%s\": %m",
+                pg_log_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",
+                pg_log_fatal("could not read block %u in file \"%s\": read %d of %d",
                              blockno, fn, r, BLCKSZ);
-            exit(1);
         }
         blocks_scanned++;

@@ -275,22 +271,18 @@ scan_file(const char *fn, int segmentno)

             /* Seek back to beginning of block */
             if (lseek(f, -BLCKSZ, SEEK_CUR) < 0)
-            {
-                pg_log_error("seek failed for block %u in file \"%s\": %m", blockno, fn);
-                exit(1);
-            }
+                pg_log_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",
+                    pg_log_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",
+                    pg_log_fatal("could not write block %u in file \"%s\": wrote %d of %d",
                                  blockno, fn, w, BLCKSZ);
-                exit(1);
             }
         }

@@ -334,10 +326,7 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
     snprintf(path, sizeof(path), "%s/%s", basedir, subdir);
     dir = opendir(path);
     if (!dir)
-    {
-        pg_log_error("could not open directory \"%s\": %m", path);
-        exit(1);
-    }
+        pg_log_fatal("could not open directory \"%s\": %m", path);
     while ((de = readdir(dir)) != NULL)
     {
         char        fn[MAXPGPATH];
@@ -361,10 +350,7 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)

         snprintf(fn, sizeof(fn), "%s/%s", path, de->d_name);
         if (lstat(fn, &st) < 0)
-        {
-            pg_log_error("could not stat file \"%s\": %m", fn);
-            exit(1);
-        }
+            pg_log_fatal("could not stat file \"%s\": %m", fn);
         if (S_ISREG(st.st_mode))
         {
             char        fnonly[MAXPGPATH];
@@ -388,11 +374,8 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
                 *segmentpath++ = '\0';
                 segmentno = atoi(segmentpath);
                 if (segmentno == 0)
-                {
-                    pg_log_error("invalid segment number %d in file name \"%s\"",
+                    pg_log_fatal("invalid segment number %d in file name \"%s\"",
                                  segmentno, fn);
-                    exit(1);
-                }
             }

             forkpath = strchr(fnonly, '_');
@@ -440,11 +423,8 @@ scan_directory(const char *basedir, const char *subdir, bool sizeonly)
                          path, de->d_name, TABLESPACE_VERSION_DIRECTORY);

                 if (lstat(tblspc_path, &tblspc_st) < 0)
-                {
-                    pg_log_error("could not stat file \"%s\": %m",
+                    pg_log_fatal("could not stat file \"%s\": %m",
                                  tblspc_path);
-                    exit(1);
-                }

                 /*
                  * Move backwards once as the scan needs to happen for the
@@ -539,7 +519,8 @@ main(int argc, char *argv[])
                 showprogress = true;
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -555,7 +536,7 @@ main(int argc, char *argv[])
         if (DataDir == NULL)
         {
             pg_log_error("no data directory specified");
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
         }
     }
@@ -565,8 +546,7 @@ main(int argc, char *argv[])
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -574,30 +554,23 @@ main(int argc, char *argv[])
     if (mode != PG_MODE_CHECK && only_filenode)
     {
         pg_log_error("option -f/--filenode can only be used with --check");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
-                progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     /* Read the control file and check compatibility */
     ControlFile = get_controlfile(DataDir, &crc_ok);
     if (!crc_ok)
-    {
-        pg_log_error("pg_control CRC value is incorrect");
-        exit(1);
-    }
+        pg_log_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_log_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
blocksize %u.\n"), 
-                ControlFile->blcksz, BLCKSZ);
+        pg_log_error_detail("The database cluster was initialized with block size %u, but pg_checksums was compiled
withblock size %u.", 
+                            ControlFile->blcksz, BLCKSZ);
         exit(1);
     }

@@ -608,31 +581,19 @@ main(int argc, char *argv[])
      */
     if (ControlFile->state != DB_SHUTDOWNED &&
         ControlFile->state != DB_SHUTDOWNED_IN_RECOVERY)
-    {
-        pg_log_error("cluster must be shut down");
-        exit(1);
-    }
+        pg_log_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_log_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_log_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_log_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..f3f13394cf 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -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",
+                    pg_log_fatal("failed sanity check, parent OID %u of table \"%s\" (OID %u) not found",
                                  inhinfo[i].inhparent,
                                  self->dobj.name,
                                  oid);
-                    exit_nicely(1);
-                }
                 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_log_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_log_fatal("could not parse numeric array \"%s\": invalid character in number", str);
             temp[j++] = s;
         }
     }
diff --git a/src/bin/pg_dump/parallel.c b/src/bin/pg_dump/parallel.c
index bc5251be82..7cc9217143 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_log_fatal("%s() failed: error code %d", "WSAStartup", err);

         parallel_init_done = true;
     }
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index d41a99d6ea..6f93fa5e36 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -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)
diff --git a/src/bin/pg_dump/pg_backup_db.c b/src/bin/pg_dump/pg_backup_db.c
index 3184eda3e7..0289b0843c 100644
--- a/src/bin/pg_dump/pg_backup_db.c
+++ b/src/bin/pg_dump/pg_backup_db.c
@@ -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);
     }

     /*
@@ -261,7 +262,7 @@ 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. */
@@ -270,7 +271,8 @@ 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
diff --git a/src/bin/pg_dump/pg_backup_utils.c b/src/bin/pg_dump/pg_backup_utils.c
index 57140a5504..55ecd6f128 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);
-    }
     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..19e2132f42 100644
--- a/src/bin/pg_dump/pg_backup_utils.h
+++ b/src/bin/pg_dump/pg_backup_utils.h
@@ -31,6 +31,13 @@ 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_log_fatal to call exit_nicely instead of exit */
+#undef pg_log_fatal
+#define pg_log_fatal(...) do { \
+        pg_log_generic(PG_LOG_ERROR, PG_LOG_PRIMARY, __VA_ARGS__); \
+        exit_nicely(1); \
+    } while(0)
+
+#define fatal(...) pg_log_fatal(__VA_ARGS__)

 #endif                            /* PG_BACKUP_UTILS_H */
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index e69dcf8a48..ca68bf8dbe 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,10 +655,7 @@ 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_log_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");
@@ -667,10 +664,7 @@ main(int argc, char **argv)
         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_log_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");
@@ -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("The 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("The command was: %s", q->data);
         exit_nicely(1);
     }
     PQclear(res);
@@ -3714,11 +3708,8 @@ dumpPolicy(Archive *fout, const PolicyInfo *polinfo)
     else if (polinfo->polcmd == 'd')
         cmd = " FOR DELETE";
     else
-    {
-        pg_log_error("unexpected policy command type: %c",
+        pg_log_fatal("unexpected policy command type: %c",
                      polinfo->polcmd);
-        exit_nicely(1);
-    }

     query = createPQExpBuffer();
     delqry = createPQExpBuffer();
@@ -8407,7 +8398,7 @@ getTableAttrs(Archive *fout, TableInfo *tblinfo, int numTables)
                                       "expected %d check constraints on table \"%s\" but found %d",
                                       tbinfo->ncheck),
                              tbinfo->ncheck, tbinfo->dobj.name, numcons);
-                pg_log_error("(The system catalogs might be corrupted.)");
+                pg_log_error_hint("The system catalogs might be corrupted.");
                 exit_nicely(1);
             }

@@ -16480,13 +16471,10 @@ dumpSequence(Archive *fout, const TableInfo *tbinfo)
     res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);

     if (PQntuples(res) != 1)
-    {
-        pg_log_error(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
+        pg_log_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));
-        exit_nicely(1);
-    }

     seqtype = PQgetvalue(res, 0, 0);
     startv = PQgetvalue(res, 0, 1);
@@ -16702,13 +16690,10 @@ dumpSequenceData(Archive *fout, const TableDataInfo *tdinfo)
     res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);

     if (PQntuples(res) != 1)
-    {
-        pg_log_error(ngettext("query to get data of sequence \"%s\" returned %d row (expected 1)",
+        pg_log_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));
-        exit_nicely(1);
-    }

     last = PQgetvalue(res, 0, 0);
     called = (strcmp(PQgetvalue(res, 0, 1), "t") == 0);
@@ -16797,10 +16782,7 @@ dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
         else if (TRIGGER_FOR_INSTEAD(tginfo->tgtype))
             appendPQExpBufferStr(query, "INSTEAD OF");
         else
-        {
-            pg_log_error("unexpected tgtype value: %d", tginfo->tgtype);
-            exit_nicely(1);
-        }
+            pg_log_fatal("unexpected tgtype value: %d", tginfo->tgtype);

         findx = 0;
         if (TRIGGER_FOR_INSERT(tginfo->tgtype))
@@ -16872,11 +16854,10 @@ dumpTrigger(Archive *fout, const TriggerInfo *tginfo)
             if (p + tlen >= tgargs + lentgargs)
             {
                 /* hm, not found before end of bytea value... */
-                pg_log_error("invalid argument string (%s) for trigger \"%s\" on table \"%s\"",
+                pg_log_fatal("invalid argument string (%s) for trigger \"%s\" on table \"%s\"",
                              tginfo->tgargs,
                              tginfo->dobj.name,
                              tbinfo->dobj.name);
-                exit_nicely(1);
             }

             if (findx > 0)
@@ -17142,11 +17123,8 @@ dumpRule(Archive *fout, const RuleInfo *rinfo)
         res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);

         if (PQntuples(res) != 1)
-        {
-            pg_log_error("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
+            pg_log_fatal("query to get rule \"%s\" for table \"%s\" failed: wrong number of rows returned",
                          rinfo->dobj.name, tbinfo->dobj.name);
-            exit_nicely(1);
-        }

         printfPQExpBuffer(cmd, "%s\n", PQgetvalue(res, 0, 0));

diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index d979f93b3d..9b28e7edff 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -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
temporarilydropping 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
droppingthe 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..5854ebbc6f 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"
+            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_dump", progname, full_path);
         else
-            pg_log_error("The program \"%s\" was found by \"%s\"\n"
+            pg_log_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);
-        exit_nicely(1);
     }

     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_log_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_log_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",
+            pg_log_fatal("could not open output file \"%s\": %m",
                          filename);
-            exit_nicely(1);
-        }
     }
     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",
+            pg_log_fatal("invalid client encoding \"%s\" specified",
                          dumpencoding);
-            exit_nicely(1);
-        }
     }

     /*
@@ -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_log_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",
+                pg_log_fatal("could not re-open the output file \"%s\": %m",
                              filename);
-                exit_nicely(1);
-            }
         }

     }
@@ -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_log_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_log_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_log_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_log_fatal("could not get server version");
     server_version = PQserverVersion(conn);
     if (server_version == 0)
-    {
-        pg_log_error("could not parse server version \"%s\"",
+        pg_log_fatal("could not parse server version \"%s\"",
                      remoteversion_str);
-        exit_nicely(1);
-    }

     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..ae35578f14 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_log_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_log_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_log_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_log_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_log_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_log_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\"",
+                pg_log_fatal("unrecognized archive format \"%s\"; please specify \"c\", \"d\", or \"t\"",
                              opts->formatName);
-                exit_nicely(1);
         }
     }

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..2cd6815bf1 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_log_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_log_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_log_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_log_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_log_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_log_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_log_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_log_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_log_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_log_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_log_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",
+        pg_log_fatal("could not read permissions of directory \"%s\": %m",
                      DataDir);
-        exit(1);
-    }

     umask(pg_mode_mask);

     if (chdir(DataDir) < 0)
-    {
-        pg_log_error("could not change directory to \"%s\": %m",
+        pg_log_fatal("could not change directory to \"%s\": %m",
                      DataDir);
-        exit(1);
-    }

     /* 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",
+            pg_log_fatal("could not open file \"%s\" for reading: %m",
                          "postmaster.pid");
-            exit(1);
-        }
     }
     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",
+        pg_log_fatal("could not open file \"%s\" for reading: %m",
                      ver_file);
-        exit(1);
-    }

     /* 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_log_fatal("unexpected empty file \"%s\"", ver_file);
         else
-            pg_log_error("could not read file \"%s\": %m", ver_file);
-        exit(1);
+            pg_log_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_log_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_log_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_log_fatal("could not read directory \"%s\": %m", XLOGDIR);

     if (closedir(xldir))
-    {
-        pg_log_error("could not close directory \"%s\": %m", XLOGDIR);
-        exit(1);
-    }
+        pg_log_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_log_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_log_fatal("could not delete file \"%s\": %m", path);
         }
     }

     if (errno)
-    {
-        pg_log_error("could not read directory \"%s\": %m", XLOGDIR);
-        exit(1);
-    }
+        pg_log_fatal("could not read directory \"%s\": %m", XLOGDIR);

     if (closedir(xldir))
-    {
-        pg_log_error("could not close directory \"%s\": %m", XLOGDIR);
-        exit(1);
-    }
+        pg_log_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_log_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_log_fatal("could not delete file \"%s\": %m", path);
         }
     }

     if (errno)
-    {
-        pg_log_error("could not read directory \"%s\": %m", ARCHSTATDIR);
-        exit(1);
-    }
+        pg_log_fatal("could not read directory \"%s\": %m", ARCHSTATDIR);

     if (closedir(xldir))
-    {
-        pg_log_error("could not close directory \"%s\": %m", ARCHSTATDIR);
-        exit(1);
-    }
+        pg_log_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_log_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_log_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_log_fatal("could not write file \"%s\": %m", path);
         }
     }

     if (fsync(fd) != 0)
-    {
-        pg_log_error("fsync error: %m");
-        exit(1);
-    }
+        pg_log_fatal("fsync error: %m");

     close(fd);
 }
diff --git a/src/bin/pg_rewind/pg_rewind.c b/src/bin/pg_rewind/pg_rewind.c
index efb82a4034..90d005e5f8 100644
--- a/src/bin/pg_rewind/pg_rewind.c
+++ b/src/bin/pg_rewind/pg_rewind.c
@@ -160,10 +160,6 @@ main(int argc, char **argv)
     {
         switch (c)
         {
-            case '?':
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
-                exit(1);
-
             case 'c':
                 restore_wal = true;
                 break;
@@ -204,34 +200,39 @@ main(int argc, char **argv)
             case 4:
                 no_ensure_shutdown = true;
                 break;
+
+            default:
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
+                exit(1);
         }
     }

     if (datadir_source == NULL && connstr_source == NULL)
     {
         pg_log_error("no source specified (--source-pgdata or --source-server)");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (datadir_source != NULL && connstr_source != NULL)
     {
         pg_log_error("only one of --source-pgdata or --source-server can be specified");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (datadir_target == NULL)
     {
         pg_log_error("no target data directory specified (--target-pgdata)");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (writerecoveryconf && connstr_source == NULL)
     {
         pg_log_error("no source server information (--source-server) specified for --write-recovery-conf");
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -239,7 +240,7 @@ main(int argc, char **argv)
     {
         pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

@@ -253,8 +254,8 @@ main(int argc, char **argv)
     if (geteuid() == 0)
     {
         pg_log_error("cannot be executed by \"root\"");
-        fprintf(stderr, _("You must run %s as the PostgreSQL superuser.\n"),
-                progname);
+        pg_log_error_hint("You must run %s as the PostgreSQL superuser.\n",
+                          progname);
         exit(1);
     }
 #endif
@@ -263,11 +264,8 @@ main(int argc, char **argv)

     /* Set mask based on PGDATA permissions */
     if (!GetDataDirectoryCreatePerm(datadir_target))
-    {
-        pg_log_error("could not read permissions of directory \"%s\": %m",
+        pg_log_fatal("could not read permissions of directory \"%s\": %m",
                      datadir_target);
-        exit(1);
-    }

     umask(pg_mode_mask);

@@ -1035,16 +1033,15 @@ getRestoreCommand(const char *argv0)
             strlcpy(full_path, progname, sizeof(full_path));

         if (rc == -1)
-            pg_log_error("The program \"%s\" is needed by %s but was not found in the\n"
+            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.",
                          "postgres", progname, full_path);
         else
-            pg_log_error("The program \"%s\" was found by \"%s\"\n"
+            pg_log_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);
-        exit(1);
     }

     /*
@@ -1125,7 +1122,8 @@ ensureCleanShutdown(const char *argv0)
     if (system(cmd) != 0)
     {
         pg_log_error("postgres single-user mode in target cluster failed");
-        pg_fatal("Command was: %s", cmd);
+        pg_log_error_detail("Command was: %s", cmd);
+        exit(1);
     }
 }

diff --git a/src/bin/pg_rewind/pg_rewind.h b/src/bin/pg_rewind/pg_rewind.h
index 388870ce95..815ee36801 100644
--- a/src/bin/pg_rewind/pg_rewind.h
+++ b/src/bin/pg_rewind/pg_rewind.h
@@ -34,7 +34,7 @@ extern uint64 fetch_size;
 extern uint64 fetch_done;

 /* logging support */
-#define pg_fatal(...) do { pg_log_fatal(__VA_ARGS__); exit(1); } while(0)
+#define pg_fatal(...) pg_log_fatal(__VA_ARGS__)

 /* in parsexlog.c */
 extern void extractPageMap(const char *datadir, XLogRecPtr startpoint,
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..2e98fd3d1a 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_log_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_log_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",
+                    pg_log_fatal("%s must be in range %u..%u",
                                  "--secs-per-test", 1, UINT_MAX);
-                    exit(1);
-                }
                 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_verifybackup/pg_verifybackup.c b/src/bin/pg_verifybackup/pg_verifybackup.c
index 05cb520c11..a41a7ae044 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);
     }

@@ -304,7 +302,6 @@ main(int argc, char **argv)
                              "but was not the same version as %s.\n"
                              "Check your installation.",
                              "pg_waldump", full_path, "pg_verifybackup");
-            exit(1);
         }
     }

@@ -449,7 +446,7 @@ report_manifest_error(JsonManifestParseContext *context, const char *fmt,...)
     va_list        ap;

     va_start(ap, fmt);
-    pg_log_generic_v(PG_LOG_FATAL, gettext(fmt), ap);
+    pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, gettext(fmt), ap);
     va_end(ap);

     exit(1);
@@ -840,7 +837,7 @@ report_backup_error(verifier_context *context, const char *pg_restrict fmt,...)
     va_list        ap;

     va_start(ap, fmt);
-    pg_log_generic_v(PG_LOG_ERROR, gettext(fmt), ap);
+    pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, gettext(fmt), ap);
     va_end(ap);

     context->saw_any_error = true;
@@ -857,7 +854,7 @@ report_fatal_error(const char *pg_restrict fmt,...)
     va_list        ap;

     va_start(ap, fmt);
-    pg_log_generic_v(PG_LOG_FATAL, gettext(fmt), ap);
+    pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, gettext(fmt), ap);
     va_end(ap);

     exit(1);
diff --git a/src/bin/pg_verifybackup/t/005_bad_manifest.pl b/src/bin/pg_verifybackup/t/005_bad_manifest.pl
index 118beb53d7..7890645161 100644
--- a/src/bin/pg_verifybackup/t/005_bad_manifest.pl
+++ b/src/bin/pg_verifybackup/t/005_bad_manifest.pl
@@ -192,7 +192,7 @@ sub test_fatal_error

     my ($test_name, $manifest_contents) = @_;

-    test_bad_manifest($test_name, qr/fatal: $test_name/, $manifest_contents);
+    test_bad_manifest($test_name, qr/error: $test_name/, $manifest_contents);
     return;
 }

diff --git a/src/bin/pg_waldump/pg_waldump.c b/src/bin/pg_waldump/pg_waldump.c
index a6251e1a96..ae99778e3c 100644
--- a/src/bin/pg_waldump/pg_waldump.c
+++ b/src/bin/pg_waldump/pg_waldump.c
@@ -75,7 +75,7 @@ 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)
+#define fatal_error(...) pg_log_fatal(__VA_ARGS__)

 /*
  * When sigint is called, just tell the system to exit at the next possible
@@ -1185,6 +1185,6 @@ main(int argc, char **argv)
     return EXIT_SUCCESS;

 bad_argument:
-    fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+    pg_log_error_hint("Try \"%s --help\" for more information.", progname);
     return EXIT_FAILURE;
 }
diff --git a/src/bin/pgbench/pgbench.c b/src/bin/pgbench/pgbench.c
index f166a77e3a..7680d7ff76 100644
--- a/src/bin/pgbench/pgbench.c
+++ b/src/bin/pgbench/pgbench.c
@@ -1301,8 +1301,8 @@ executeStatement(PGconn *con, const char *sql)
     res = PQexec(con, sql);
     if (PQresultStatus(res) != PGRES_COMMAND_OK)
     {
-        pg_log_fatal("query failed: %s", PQerrorMessage(con));
-        pg_log_info("query was: %s", sql);
+        pg_log_error("query failed: %s", PQerrorMessage(con));
+        pg_log_error_detail("Query was: %s", sql);
         exit(1);
     }
     PQclear(res);
@@ -1318,7 +1318,7 @@ tryExecuteStatement(PGconn *con, const char *sql)
     if (PQresultStatus(res) != PGRES_COMMAND_OK)
     {
         pg_log_error("%s", PQerrorMessage(con));
-        pg_log_info("(ignoring this error and continuing anyway)");
+        pg_log_error_detail("(ignoring this error and continuing anyway)");
     }
     PQclear(res);
 }
@@ -2651,7 +2651,6 @@ 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);
     }
 }

@@ -4192,10 +4191,7 @@ initGenerateDataClientSide(PGconn *con)
     res = PQexec(con, copy_statement);

     if (PQresultStatus(res) != PGRES_COPY_IN)
-    {
         pg_log_fatal("unexpected copy in result: %s", PQerrorMessage(con));
-        exit(1);
-    }
     PQclear(res);

     start = pg_time_now();
@@ -4209,10 +4205,7 @@ initGenerateDataClientSide(PGconn *con)
                           INT64_FORMAT "\t" INT64_FORMAT "\t%d\t\n",
                           j, k / naccounts + 1, 0);
         if (PQputline(con, sql.data))
-        {
             pg_log_fatal("PQputline failed");
-            exit(1);
-        }

         if (CancelRequested)
             break;
@@ -4254,15 +4247,9 @@ initGenerateDataClientSide(PGconn *con)
         fputc('\n', stderr);    /* Need to move to next line */

     if (PQputline(con, "\\.\n"))
-    {
         pg_log_fatal("very last PQputline failed");
-        exit(1);
-    }
     if (PQendcopy(con))
-    {
         pg_log_fatal("PQendcopy failed");
-        exit(1);
-    }

     termPQExpBuffer(&sql);

@@ -4402,17 +4389,14 @@ static void
 checkInitSteps(const char *initialize_steps)
 {
     if (initialize_steps[0] == '\0')
-    {
         pg_log_fatal("no initialization steps specified");
-        exit(1);
-    }

     for (const char *step = initialize_steps; *step != '\0'; step++)
     {
         if (strchr(ALL_INIT_STEPS " ", *step) == NULL)
         {
-            pg_log_fatal("unrecognized initialization step \"%c\"", *step);
-            pg_log_info("Allowed step characters are: \"" ALL_INIT_STEPS "\".");
+            pg_log_error("unrecognized initialization step \"%c\"", *step);
+            pg_log_error_detail("Allowed step characters are: \"" ALL_INIT_STEPS "\".");
             exit(1);
         }
     }
@@ -4433,10 +4417,7 @@ runInitSteps(const char *initialize_steps)
     initPQExpBuffer(&stats);

     if ((con = doConnect()) == NULL)
-    {
         pg_log_fatal("could not create connection for initialization");
-        exit(1);
-    }

     setup_cancel_handler(NULL);
     SetCancelConn(con);
@@ -4479,7 +4460,7 @@ runInitSteps(const char *initialize_steps)
             case ' ':
                 break;            /* ignore */
             default:
-                pg_log_fatal("unrecognized initialization step \"%c\"", *step);
+                pg_log_error("unrecognized initialization step \"%c\"", *step);
                 PQfinish(con);
                 exit(1);
         }
@@ -4523,21 +4504,18 @@ GetTableInfo(PGconn *con, bool scale_given)
     {
         char       *sqlState = PQresultErrorField(res, PG_DIAG_SQLSTATE);

-        pg_log_fatal("could not count number of branches: %s", PQerrorMessage(con));
+        pg_log_error("could not count number of branches: %s", PQerrorMessage(con));

         if (sqlState && strcmp(sqlState, ERRCODE_UNDEFINED_TABLE) == 0)
-            pg_log_info("Perhaps you need to do initialization (\"pgbench -i\") in database \"%s\"",
-                        PQdb(con));
+            pg_log_error_hint("Perhaps you need to do initialization (\"pgbench -i\") in database \"%s\".",
+                              PQdb(con));

         exit(1);
     }
     scale = atoi(PQgetvalue(res, 0, 0));
     if (scale < 0)
-    {
         pg_log_fatal("invalid count(*) from pgbench_branches: \"%s\"",
                      PQgetvalue(res, 0, 0));
-        exit(1);
-    }
     PQclear(res);

     /* warn if we override user-given -s switch */
@@ -4584,8 +4562,8 @@ GetTableInfo(PGconn *con, bool scale_given)
          * This case is unlikely as pgbench already found "pgbench_branches"
          * above to compute the scale.
          */
-        pg_log_fatal("no pgbench_accounts table found in search_path");
-        pg_log_info("Perhaps you need to do initialization (\"pgbench -i\") in database \"%s\".", PQdb(con));
+        pg_log_error("no pgbench_accounts table found in search_path");
+        pg_log_error_hint("Perhaps you need to do initialization (\"pgbench -i\") in database \"%s\".", PQdb(con));
         exit(1);
     }
     else                        /* PQntupes(res) == 1 */
@@ -4608,7 +4586,6 @@ GetTableInfo(PGconn *con, bool scale_given)
             {
                 /* possibly a newer version with new partition method */
                 pg_log_fatal("unexpected partition method: \"%s\"", ps);
-                exit(1);
             }
         }

@@ -4700,7 +4677,7 @@ syntax_error(const char *source, int lineno,
     if (command != NULL)
         appendPQExpBuffer(&buf, " in command \"%s\"", command);

-    pg_log_fatal("%s", buf.data);
+    pg_log_error("%s", buf.data);

     termPQExpBuffer(&buf);

@@ -5050,7 +5027,6 @@ 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);
 }

 /*
@@ -5286,18 +5262,12 @@ process_file(const char *filename, int weight)
     if (strcmp(filename, "-") == 0)
         fd = stdin;
     else if ((fd = fopen(filename, "r")) == NULL)
-    {
         pg_log_fatal("could not open file \"%s\": %m", filename);
-        exit(1);
-    }

     buf = read_file_contents(fd);

     if (ferror(fd))
-    {
         pg_log_fatal("could not read file \"%s\": %m", filename);
-        exit(1);
-    }

     if (fd != stdin)
         fclose(fd);
@@ -5350,9 +5320,9 @@ findBuiltin(const char *name)

     /* error cases */
     if (found == 0)
-        pg_log_fatal("no builtin script found for name \"%s\"", name);
+        pg_log_error("no builtin script found for name \"%s\"", name);
     else                        /* found > 1 */
-        pg_log_fatal("ambiguous builtin name: %d builtin scripts found for prefix \"%s\"", found, name);
+        pg_log_error("ambiguous builtin name: %d builtin scripts found for prefix \"%s\"", found, name);

     listAvailableScripts();
     exit(1);
@@ -5384,16 +5354,10 @@ parseScriptWeight(const char *option, char **script)
         errno = 0;
         wtmp = strtol(sep + 1, &badp, 10);
         if (errno != 0 || badp == sep + 1 || *badp != '\0')
-        {
             pg_log_fatal("invalid weight specification: %s", sep);
-            exit(1);
-        }
         if (wtmp > INT_MAX || wtmp < 0)
-        {
             pg_log_fatal("weight specification out of range (0 .. %d): %lld",
                          INT_MAX, (long long) wtmp);
-            exit(1);
-        }
         weight = wtmp;
     }
     else
@@ -5410,16 +5374,10 @@ static void
 addScript(ParsedScript script)
 {
     if (script.commands == NULL || script.commands[0] == NULL)
-    {
         pg_log_fatal("empty command list for script \"%s\"", script.desc);
-        exit(1);
-    }

     if (num_scripts >= MAX_SCRIPTS)
-    {
         pg_log_fatal("at most %d SQL scripts are allowed", MAX_SCRIPTS);
-        exit(1);
-    }

     CheckConditional(script);

@@ -5735,7 +5693,7 @@ set_random_seed(const char *seed)
         if (sscanf(seed, "%lu%c", &ulseed, &garbage) != 1)
         {
             pg_log_error("unrecognized random seed option \"%s\"", seed);
-            pg_log_info("Expecting an unsigned integer, \"time\" or \"rand\"");
+            pg_log_error_detail("Expecting an unsigned integer, \"time\" or \"rand\".");
             return false;
         }
         iseed = (uint64) ulseed;
@@ -5866,10 +5824,7 @@ main(int argc, char **argv)

     /* set random seed early, because it may be used while parsing scripts. */
     if (!set_random_seed(getenv("PGBENCH_RANDOM_SEED")))
-    {
         pg_log_fatal("error while setting random seed from PGBENCH_RANDOM_SEED environment variable");
-        exit(1);
-    }

     while ((c = getopt_long(argc, argv, "iI:h:nvp:dqb:SNc:j:Crs:t:T:U:lf:D:F:M:P:R:L:", long_options, &optindex)) !=
-1)
     {
@@ -5916,15 +5871,12 @@ main(int argc, char **argv)
 #else                            /* but BSD doesn't ... */
                 if (getrlimit(RLIMIT_OFILE, &rlim) == -1)
 #endif                            /* RLIMIT_NOFILE */
-                {
                     pg_log_fatal("getrlimit failed: %m");
-                    exit(1);
-                }
                 if (rlim.rlim_cur < nclients + 3)
                 {
-                    pg_log_fatal("need at least %d open files, but system limit is %ld",
+                    pg_log_error("need at least %d open files, but system limit is %ld",
                                  nclients + 3, (long) rlim.rlim_cur);
-                    pg_log_info("Reduce number of clients, or use limit/ulimit to increase the system limit.");
+                    pg_log_error_hint("Reduce number of clients, or use limit/ulimit to increase the system limit.");
                     exit(1);
                 }
 #endif                            /* HAVE_GETRLIMIT */
@@ -5938,10 +5890,7 @@ main(int argc, char **argv)
                 }
 #ifndef ENABLE_THREAD_SAFETY
                 if (nthreads != 1)
-                {
                     pg_log_fatal("threads are not supported on this platform; use -j1");
-                    exit(1);
-                }
 #endif                            /* !ENABLE_THREAD_SAFETY */
                 break;
             case 'C':
@@ -6014,10 +5963,7 @@ main(int argc, char **argv)
                     benchmarking_option_set = true;

                     if ((p = strchr(optarg, '=')) == NULL || p == optarg || *(p + 1) == '\0')
-                    {
                         pg_log_fatal("invalid variable definition: \"%s\"", optarg);
-                        exit(1);
-                    }

                     *p++ = '\0';
                     if (!putVariable(&state[0], "option", optarg, p))
@@ -6036,10 +5982,7 @@ main(int argc, char **argv)
                     if (strcmp(optarg, QUERYMODE[querymode]) == 0)
                         break;
                 if (querymode >= NUM_QUERYMODE)
-                {
                     pg_log_fatal("invalid query mode (-M): \"%s\"", optarg);
-                    exit(1);
-                }
                 break;
             case 'P':
                 benchmarking_option_set = true;
@@ -6055,10 +5998,7 @@ main(int argc, char **argv)
                     benchmarking_option_set = true;

                     if (throttle_value <= 0.0)
-                    {
                         pg_log_fatal("invalid rate limit: \"%s\"", optarg);
-                        exit(1);
-                    }
                     /* Invert rate limit into per-transaction delay in usec */
                     throttle_delay = 1000000.0 / throttle_value;
                 }
@@ -6068,10 +6008,7 @@ main(int argc, char **argv)
                     double        limit_ms = atof(optarg);

                     if (limit_ms <= 0.0)
-                    {
                         pg_log_fatal("invalid latency limit: \"%s\"", optarg);
-                        exit(1);
-                    }
                     benchmarking_option_set = true;
                     latency_limit = (int64) (limit_ms * 1000);
                 }
@@ -6092,10 +6029,7 @@ main(int argc, char **argv)
                 benchmarking_option_set = true;
                 sample_rate = atof(optarg);
                 if (sample_rate <= 0.0 || sample_rate > 1.0)
-                {
                     pg_log_fatal("invalid sampling rate: \"%s\"", optarg);
-                    exit(1);
-                }
                 break;
             case 5:                /* aggregate-interval */
                 benchmarking_option_set = true;
@@ -6118,10 +6052,7 @@ main(int argc, char **argv)
             case 9:                /* random-seed */
                 benchmarking_option_set = true;
                 if (!set_random_seed(optarg))
-                {
                     pg_log_fatal("error while setting random seed from --random-seed option");
-                    exit(1);
-                }
                 break;
             case 10:            /* list */
                 {
@@ -6144,16 +6075,13 @@ main(int argc, char **argv)
                 else if (pg_strcasecmp(optarg, "hash") == 0)
                     partition_method = PART_HASH;
                 else
-                {
                     pg_log_fatal("invalid partition method, expecting \"range\" or \"hash\", got: \"%s\"",
                                  optarg);
-                    exit(1);
-                }
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
-                break;
         }
     }

@@ -6179,10 +6107,7 @@ main(int argc, char **argv)
     }

     if (total_weight == 0 && !is_init_mode)
-    {
         pg_log_fatal("total script weight must not be zero");
-        exit(1);
-    }

     /* show per script stats if several scripts are used */
     if (num_scripts > 1)
@@ -6217,25 +6142,19 @@ main(int argc, char **argv)

     if (optind < argc)
     {
-        pg_log_fatal("too many command-line arguments (first is \"%s\")",
+        pg_log_error("too many command-line arguments (first is \"%s\")",
                      argv[optind]);
-        fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+        pg_log_error_hint("Try \"%s --help\" for more information.", progname);
         exit(1);
     }

     if (is_init_mode)
     {
         if (benchmarking_option_set)
-        {
             pg_log_fatal("some of the specified options cannot be used in initialization (-i) mode");
-            exit(1);
-        }

         if (partitions == 0 && partition_method != PART_NONE)
-        {
             pg_log_fatal("--partition-method requires greater than zero --partitions");
-            exit(1);
-        }

         /* set default method */
         if (partitions > 0 && partition_method == PART_NONE)
@@ -6271,17 +6190,11 @@ main(int argc, char **argv)
     else
     {
         if (initialization_option_set)
-        {
             pg_log_fatal("some of the specified options cannot be used in benchmarking mode");
-            exit(1);
-        }
     }

     if (nxacts > 0 && duration > 0)
-    {
         pg_log_fatal("specify either a number of transactions (-t) or a duration (-T), not both");
-        exit(1);
-    }

     /* Use DEFAULT_NXACTS if neither nxacts nor duration is specified. */
     if (nxacts <= 0 && duration <= 0)
@@ -6289,47 +6202,26 @@ main(int argc, char **argv)

     /* --sampling-rate may be used only with -l */
     if (sample_rate > 0.0 && !use_log)
-    {
         pg_log_fatal("log sampling (--sampling-rate) is allowed only when logging transactions (-l)");
-        exit(1);
-    }

     /* --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);
-    }

     if (agg_interval > 0 && !use_log)
-    {
         pg_log_fatal("log aggregation is allowed only when actually logging transactions");
-        exit(1);
-    }

     if (!use_log && logfile_prefix)
-    {
         pg_log_fatal("log file prefix (--log-prefix) is allowed only when logging transactions (-l)");
-        exit(1);
-    }

     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);
-    }

     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);
-    }

     if (progress_timestamp && progress == 0)
-    {
         pg_log_fatal("--progress-timestamp is allowed only under --progress");
-        exit(1);
-    }

     /*
      * save main process id in the global variable because process id will be
@@ -6378,10 +6270,7 @@ main(int argc, char **argv)
     /* opening connection... */
     con = doConnect();
     if (con == NULL)
-    {
         pg_log_fatal("could not create connection for setup");
-        exit(1);
-    }

     /* report pgbench and server versions */
     printVersion(con);
@@ -6487,10 +6376,7 @@ main(int argc, char **argv)

     errno = THREAD_BARRIER_INIT(&barrier, nthreads);
     if (errno != 0)
-    {
         pg_log_fatal("could not initialize barrier: %m");
-        exit(1);
-    }

 #ifdef ENABLE_THREAD_SAFETY
     /* start all threads but thread 0 which is executed directly later */
@@ -6502,10 +6388,7 @@ main(int argc, char **argv)
         errno = THREAD_CREATE(&thread->thread, threadRun, thread);

         if (errno != 0)
-        {
             pg_log_fatal("could not create thread: %m");
-            exit(1);
-        }
     }
 #else
     Assert(nthreads == 1);
@@ -6569,7 +6452,7 @@ main(int argc, char **argv)
     THREAD_BARRIER_DESTROY(&barrier);

     if (exit_code != 0)
-        pg_log_fatal("Run was aborted; the above results are incomplete.");
+        pg_log_error("Run was aborted; the above results are incomplete.");

     return exit_code;
 }
@@ -6603,10 +6486,7 @@ threadRun(void *arg)
         thread->logfile = fopen(logpath, "w");

         if (thread->logfile == NULL)
-        {
             pg_log_fatal("could not open logfile \"%s\": %m", logpath);
-            exit(1);
-        }
     }

     /* explicitly initialize the state machines */
@@ -6633,7 +6513,6 @@ threadRun(void *arg)
                 /* coldly abort on initial connection failure */
                 pg_log_fatal("could not create connection for client %d",
                              state[i].id);
-                exit(1);
             }
         }
     }
@@ -6901,10 +6780,7 @@ setalarm(int seconds)
         !CreateTimerQueueTimer(&timer, queue,
                                win32_timer_callback, NULL, seconds * 1000, 0,
                                WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE))
-    {
         pg_log_fatal("failed to set timer");
-        exit(1);
-    }
 }

 #endif                            /* WIN32 */
@@ -7049,7 +6925,6 @@ add_socket_to_set(socket_set *sa, int fd, int idx)
          * complicating the API to make it less grotty.
          */
         pg_log_fatal("too many client connections for select()");
-        exit(1);
     }
     FD_SET(fd, &sa->fds);
     if (fd > sa->maxfd)
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 292cff5df9..1380a95b3e 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -232,7 +232,7 @@ HandleSlashCmds(PsqlScanState scan_state,
     {
         pg_log_error("invalid command \\%s", cmd);
         if (pset.cur_cmd_interactive)
-            pg_log_info("Try \\? for help.");
+            pg_log_error_hint("Try \\? for help.");
         status = PSQL_CMD_ERROR;
     }

diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
index d65b9a124f..f9c0a2a4d2 100644
--- a/src/bin/psql/common.c
+++ b/src/bin/psql/common.c
@@ -302,7 +302,7 @@ CheckConnection(void)
     {
         if (!pset.cur_cmd_interactive)
         {
-            pg_log_fatal("connection to server was lost");
+            pg_log_error("connection to server was lost");
             exit(EXIT_BADCONN);
         }

diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 56afa6817e..a5b0fa7f49 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);
-        }
     }

     /*
diff --git a/src/bin/psql/mainloop.c b/src/bin/psql/mainloop.c
index e5c976fc4f..168fbb35c4 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_log_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_log_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..596635eac1 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);
-    }

     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);
-        }
     }

     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);
-                    }

                     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);
-    }

     get_etc_path(my_exec_path, etc_path);

diff --git a/src/bin/scripts/clusterdb.c b/src/bin/scripts/clusterdb.c
index 4c97bd41d7..b33e5a4db9 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_log_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_log_fatal("cannot cluster specific table(s) in all databases");

         cparams.dbname = maintenance_db;

diff --git a/src/bin/scripts/createdb.c b/src/bin/scripts/createdb.c
index b0c6805bc9..d1cfe1787a 100644
--- a/src/bin/scripts/createdb.c
+++ b/src/bin/scripts/createdb.c
@@ -120,7 +120,8 @@ main(int argc, char *argv[])
                 maintenance_db = pg_strdup(optarg);
                 break;
             default:
-                fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+                /* getopt_long already emitted a complaint */
+                pg_log_error_hint("Try \"%s --help\" for more information.", progname);
                 exit(1);
         }
     }
@@ -139,22 +140,16 @@ main(int argc, char *argv[])
         default:
             pg_log_error("too many command-line arguments (first is \"%s\")",
                          argv[optind + 2]);
-            fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
+            pg_log_error_hint("Try \"%s --help\" for more information.", progname);
             exit(1);
     }

     if (locale)
     {
         if (lc_ctype)
-        {
-            pg_log_error("only one of --locale and --lc-ctype can be specified");
-            exit(1);
-        }
+            pg_log_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_log_fatal("only one of --locale and --lc-collate can be specified");
         lc_ctype = locale;
         lc_collate = locale;
     }
@@ -162,10 +157,7 @@ main(int argc, char *argv[])
     if (encoding)
     {
         if (pg_char_to_encoding(encoding) < 0)
-        {
-            pg_log_error("\"%s\" is not a valid encoding name", encoding);
-            exit(1);
-        }
+            pg_log_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..b5cabc33f0 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",
+            pg_log_fatal("password encryption failed: %s",
                          PQerrorMessage(conn));
-            exit(1);
-        }
         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..f8bfda1404 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_log_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_log_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_log_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_log_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_log_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_log_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_log_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_log_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_log_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_log_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",
+        pg_log_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
                      "concurrently", "12");
-        exit(1);
     }

     if (tablespace && PQserverVersion(conn) < 140000)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+        pg_log_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
                      "tablespace", "14");
-        exit(1);
     }

     if (!parallel)
diff --git a/src/bin/scripts/vacuumdb.c b/src/bin/scripts/vacuumdb.c
index 4f6917fd39..f19969d6a3 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",
+            pg_log_fatal("cannot use the \"%s\" option when performing only analyze",
                          "full");
-            exit(1);
-        }
         if (vacopts.freeze)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
+            pg_log_fatal("cannot use the \"%s\" option when performing only analyze",
                          "freeze");
-            exit(1);
-        }
         if (vacopts.disable_page_skipping)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
+            pg_log_fatal("cannot use the \"%s\" option when performing only analyze",
                          "disable-page-skipping");
-            exit(1);
-        }
         if (vacopts.no_index_cleanup)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
+            pg_log_fatal("cannot use the \"%s\" option when performing only analyze",
                          "no-index-cleanup");
-            exit(1);
-        }
         if (vacopts.force_index_cleanup)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
+            pg_log_fatal("cannot use the \"%s\" option when performing only analyze",
                          "force-index-cleanup");
-            exit(1);
-        }
         if (!vacopts.do_truncate)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
+            pg_log_fatal("cannot use the \"%s\" option when performing only analyze",
                          "no-truncate");
-            exit(1);
-        }
         if (!vacopts.process_toast)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing only analyze",
+            pg_log_fatal("cannot use the \"%s\" option when performing only analyze",
                          "no-process-toast");
-            exit(1);
-        }
         /* 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",
+            pg_log_fatal("cannot use the \"%s\" option when performing only analyze",
                          "parallel");
-            exit(1);
-        }
         if (vacopts.full)
-        {
-            pg_log_error("cannot use the \"%s\" option when performing full vacuum",
+            pg_log_fatal("cannot use the \"%s\" option when performing full vacuum",
                          "parallel");
-            exit(1);
-        }
     }

     /* 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",
+        pg_log_fatal("cannot use the \"%s\" option with the \"%s\" option",
                      "no-index-cleanup", "force-index-cleanup");
-        exit(1);
-    }

     /* 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_log_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_log_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",
+        pg_log_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
                      "disable-page-skipping", "9.6");
-        exit(1);
     }

     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",
+        pg_log_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
                      "no-index-cleanup", "12");
-        exit(1);
     }

     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",
+        pg_log_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
                      "force-index-cleanup", "12");
-        exit(1);
     }

     if (!vacopts->do_truncate && PQserverVersion(conn) < 120000)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+        pg_log_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
                      "no-truncate", "12");
-        exit(1);
     }

     if (!vacopts->process_toast && PQserverVersion(conn) < 140000)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+        pg_log_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
                      "no-process-toast", "14");
-        exit(1);
     }

     if (vacopts->skip_locked && PQserverVersion(conn) < 120000)
     {
         PQfinish(conn);
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+        pg_log_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
                      "skip-locked", "12");
-        exit(1);
     }

     if (vacopts->min_xid_age != 0 && PQserverVersion(conn) < 90600)
-    {
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+        pg_log_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
                      "--min-xid-age", "9.6");
-        exit(1);
-    }

     if (vacopts->min_mxid_age != 0 && PQserverVersion(conn) < 90600)
-    {
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+        pg_log_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
                      "--min-mxid-age", "9.6");
-        exit(1);
-    }

     if (vacopts->parallel_workers >= 0 && PQserverVersion(conn) < 130000)
-    {
-        pg_log_error("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
+        pg_log_fatal("cannot use the \"%s\" option on server versions older than PostgreSQL %s",
                      "--parallel", "13");
-        exit(1);
-    }

     if (!quiet)
     {
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..8f3bfdf2db 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_log_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..90f2e5bb6a 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);
-    }

     /*
      * 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);
-            }
             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);
-                }
                 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);
-            }
         }
     }

@@ -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);
-    }

     /*
      * 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..f95d2d5987 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",
+            pg_log_fatal("could not connect to database %s: out of memory",
                          cparams->dbname);
-            exit(1);
-        }

         /*
          * 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_log_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..987a4350fe 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);
-    }

     /* 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..265fe166da 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_log_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_log_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_log_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_log_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_log_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_log_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_log_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_log_fatal("out of memory");
     return result;
 }
diff --git a/src/include/common/logging.h b/src/include/common/logging.h
index 43cc79afa8..74a315837a 100644
--- a/src/include/common/logging.h
+++ b/src/include/common/logging.h
@@ -16,7 +16,7 @@
 enum pg_log_level
 {
     /*
-     * Not initialized yet
+     * Not initialized yet (not to be used as an actual message log level).
      */
     PG_LOG_NOTSET = 0,

@@ -43,20 +43,42 @@ enum pg_log_level
     PG_LOG_ERROR,

     /*
-     * Severe errors that cause program termination.  (One-shot programs may
-     * chose to label even fatal errors as merely "errors".  The distinction
-     * is up to the program.)
-     */
-    PG_LOG_FATAL,
-
-    /*
-     * Turn all logging off.
+     * Turn all logging off (not to be used as an actual message log level).
      */
     PG_LOG_OFF,
 };

+/*
+ * __pg_log_level is the minimum log level that will actually be shown.
+ */
 extern enum pg_log_level __pg_log_level;

+/*
+ * A log message can have several parts.  The primary message is required,
+ * others are optional.  When emitting multiple parts, do so in the order of
+ * this enum, for consistency.
+ */
+enum pg_log_part
+{
+    /*
+     * The primary message.  Try to keep it to one line; follow the backend's
+     * style guideline for primary messages.
+     */
+    PG_LOG_PRIMARY,
+
+    /*
+     * Additional detail.  Follow the backend's style guideline for detail
+     * messages.
+     */
+    PG_LOG_DETAIL,
+
+    /*
+     * Hint (not guaranteed correct) about how to fix the problem.  Follow the
+     * backend's style guideline for hint messages.
+     */
+    PG_LOG_HINT,
+};
+
 /*
  * Kind of a hack to be able to produce the psql output exactly as required by
  * the regression tests.
@@ -70,27 +92,85 @@ void        pg_logging_increase_verbosity(void);
 void        pg_logging_set_pre_callback(void (*cb) (void));
 void        pg_logging_set_locus_callback(void (*cb) (const char **filename, uint64 *lineno));

-void        pg_log_generic(enum pg_log_level level, const char *pg_restrict fmt,...) pg_attribute_printf(2, 3);
-void        pg_log_generic_v(enum pg_log_level level, const char *pg_restrict fmt, va_list ap) pg_attribute_printf(2,
0);
+void        pg_log_generic(enum pg_log_level level, enum pg_log_part part,
+                           const char *pg_restrict fmt,...)
+            pg_attribute_printf(3, 4);
+void        pg_log_generic_v(enum pg_log_level level, enum pg_log_part part,
+                             const char *pg_restrict fmt, va_list ap)
+            pg_attribute_printf(3, 0);

-#define pg_log_fatal(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_FATAL)) pg_log_generic(PG_LOG_FATAL, __VA_ARGS__); \
+/*
+ * Preferred style is to use these macros to perform logging; don't call
+ * pg_log_generic[_v] directly, except perhaps in error interface code.
+ */
+#define pg_log_error(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_ERROR)) \
+            pg_log_generic(PG_LOG_ERROR, PG_LOG_PRIMARY, __VA_ARGS__); \
     } while(0)

-#define pg_log_error(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_ERROR)) pg_log_generic(PG_LOG_ERROR, __VA_ARGS__); \
+#define pg_log_error_detail(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_ERROR)) \
+            pg_log_generic(PG_LOG_ERROR, PG_LOG_DETAIL, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_error_hint(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_ERROR)) \
+            pg_log_generic(PG_LOG_ERROR, PG_LOG_HINT, __VA_ARGS__); \
     } while(0)

 #define pg_log_warning(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_WARNING)) pg_log_generic(PG_LOG_WARNING, __VA_ARGS__); \
+        if (likely(__pg_log_level <= PG_LOG_WARNING)) \
+            pg_log_generic(PG_LOG_WARNING, PG_LOG_PRIMARY, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_warning_detail(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_WARNING)) \
+            pg_log_generic(PG_LOG_WARNING, PG_LOG_DETAIL, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_warning_hint(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_WARNING)) \
+            pg_log_generic(PG_LOG_WARNING, PG_LOG_HINT, __VA_ARGS__); \
     } while(0)

 #define pg_log_info(...) do { \
-        if (likely(__pg_log_level <= PG_LOG_INFO)) pg_log_generic(PG_LOG_INFO, __VA_ARGS__); \
+        if (likely(__pg_log_level <= PG_LOG_INFO)) \
+            pg_log_generic(PG_LOG_INFO, PG_LOG_PRIMARY, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_info_detail(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_INFO)) \
+            pg_log_generic(PG_LOG_INFO, PG_LOG_DETAIL, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_info_hint(...) do { \
+        if (likely(__pg_log_level <= PG_LOG_INFO)) \
+            pg_log_generic(PG_LOG_INFO, PG_LOG_HINT, __VA_ARGS__); \
     } while(0)

 #define pg_log_debug(...) do { \
-        if (unlikely(__pg_log_level <= PG_LOG_DEBUG)) pg_log_generic(PG_LOG_DEBUG, __VA_ARGS__); \
+        if (unlikely(__pg_log_level <= PG_LOG_DEBUG)) \
+            pg_log_generic(PG_LOG_DEBUG, PG_LOG_PRIMARY, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_debug_detail(...) do { \
+        if (unlikely(__pg_log_level <= PG_LOG_DEBUG)) \
+            pg_log_generic(PG_LOG_DEBUG, PG_LOG_DETAIL, __VA_ARGS__); \
+    } while(0)
+
+#define pg_log_debug_hint(...) do { \
+        if (unlikely(__pg_log_level <= PG_LOG_DEBUG)) \
+            pg_log_generic(PG_LOG_DEBUG, PG_LOG_HINT, __VA_ARGS__); \
+    } while(0)
+
+/*
+ * A common special case: pg_log_error() and immediately exit(1).  There is
+ * no situation where it makes sense to suppress the message, so we ignore
+ * __pg_log_level.
+ */
+#define pg_log_fatal(...) do { \
+        pg_log_generic(PG_LOG_ERROR, PG_LOG_PRIMARY, __VA_ARGS__); \
+        exit(1); \
     } while(0)

 #endif                            /* COMMON_LOGGING_H */

pgsql-hackers by date:

Previous
From: Justin Pryzby
Date:
Subject: wal_compression=zstd
Next
From: Tomas Vondra
Date:
Subject: Re: logical decoding and replication of sequences